LCOV - code coverage report
Current view: top level - ipu4 - ipu6-cpd.c (source / functions) Coverage Total Hit
Test: ipu4.info Lines: 50.8 % 118 60
Test Date: 2026-05-12 04:57:36 Functions: 75.0 % 8 6

            Line data    Source code
       1              : // SPDX-License-Identifier: GPL-2.0-only
       2              : /*
       3              :  * Copyright (C) 2013--2024 Intel Corporation
       4              :  */
       5              : 
       6              : #include <linux/bitfield.h>
       7              : #include <linux/bits.h>
       8              : #include <linux/err.h>
       9              : #include <linux/dma-mapping.h>
      10              : #include <linux/gfp_types.h>
      11              : #include <linux/math64.h>
      12              : #include <linux/sizes.h>
      13              : #include <linux/types.h>
      14              : 
      15              : #include "ipu6.h"
      16              : #include "ipu6-bus.h"
      17              : #include "ipu6-cpd.h"
      18              : #include "ipu6-dma.h"
      19              : 
      20              : /* 15 entries + header*/
      21              : #define MAX_PKG_DIR_ENT_CNT             16
      22              : /* 2 qword per entry/header */
      23              : #define PKG_DIR_ENT_LEN                 2
      24              : /* PKG_DIR size in bytes */
      25              : #define PKG_DIR_SIZE                    ((MAX_PKG_DIR_ENT_CNT) *        \
      26              :                                          (PKG_DIR_ENT_LEN) * sizeof(u64))
      27              : /* _IUPKDR_ */
      28              : #define PKG_DIR_HDR_MARK                0x5f4955504b44525fULL
      29              : 
      30              : /* $CPD */
      31              : #define CPD_HDR_MARK                    0x44504324
      32              : 
      33              : #define MAX_MANIFEST_SIZE               (SZ_2K * sizeof(u32))
      34              : #define MAX_METADATA_SIZE               SZ_64K
      35              : 
      36              : #define MAX_COMPONENT_ID                127
      37              : #define MAX_COMPONENT_VERSION           0xffff
      38              : 
      39              : #define MANIFEST_IDX    0
      40              : #define METADATA_IDX    1
      41              : #define MODULEDATA_IDX  2
      42              : /*
      43              :  * PKG_DIR Entry (type == id)
      44              :  * 63:56        55      54:48   47:32   31:24   23:0
      45              :  * Rsvd         Rsvd    Type    Version Rsvd    Size
      46              :  */
      47              : #define PKG_DIR_SIZE_MASK       GENMASK_ULL(23, 0)
      48              : #define PKG_DIR_VERSION_MASK    GENMASK_ULL(47, 32)
      49              : #define PKG_DIR_TYPE_MASK       GENMASK_ULL(54, 48)
      50              : 
      51              : static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd,
      52              :                                                             u8 idx)
      53              : {
      54              :         const struct ipu6_cpd_hdr *cpd_hdr = cpd;
      55              :         const struct ipu6_cpd_ent *ent;
      56              : 
      57            4 :         ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len);
      58            5 :         return ent + idx;
      59              : }
      60              : 
      61              : #define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX)
      62              : #define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX)
      63              : #define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX)
      64              : 
      65              : static const struct ipu6_cpd_metadata_cmpnt_hdr *
      66            0 : ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata,
      67              :                             unsigned int metadata_size, u8 idx)
      68              : {
      69              :         size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn);
      70            0 :         size_t cmpnt_count = metadata_size - extn_size;
      71              : 
      72            0 :         cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size);
      73              : 
      74            0 :         if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) {
      75            0 :                 dev_err(&isp->pdev->dev, "Component index out of range (%d)\n",
      76              :                         idx);
      77            0 :                 return ERR_PTR(-EINVAL);
      78              :         }
      79              : 
      80            0 :         return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size;
      81              : }
      82              : 
      83              : static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp,
      84              :                                            const void *metadata,
      85              :                                            unsigned int metadata_size, u8 idx)
      86              : {
      87              :         const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
      88              : 
      89            0 :         cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
      90            0 :         if (IS_ERR(cmpnt))
      91            0 :                 return PTR_ERR(cmpnt);
      92              : 
      93            0 :         return cmpnt->ver;
      94              : }
      95              : 
      96              : static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp,
      97              :                                           const void *metadata,
      98              :                                           unsigned int metadata_size, u8 idx)
      99              : {
     100              :         const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
     101              : 
     102            0 :         cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
     103            0 :         if (IS_ERR(cmpnt))
     104            0 :                 return PTR_ERR(cmpnt);
     105              : 
     106            0 :         return cmpnt->id;
     107              : }
     108              : 
     109            1 : static int ipu6_cpd_parse_module_data(struct ipu6_device *isp,
     110              :                                       const void *module_data,
     111              :                                       unsigned int module_data_size,
     112              :                                       dma_addr_t dma_addr_module_data,
     113              :                                       u64 *pkg_dir, const void *metadata,
     114              :                                       unsigned int metadata_size)
     115              : {
     116              :         const struct ipu6_cpd_module_data_hdr *module_data_hdr;
     117              :         const struct ipu6_cpd_hdr *dir_hdr;
     118              :         const struct ipu6_cpd_ent *dir_ent;
     119              :         unsigned int i;
     120              :         u8 len;
     121              : 
     122            1 :         if (!module_data)
     123              :                 return -EINVAL;
     124              : 
     125              :         module_data_hdr = module_data;
     126            1 :         dir_hdr = module_data + module_data_hdr->hdr_len;
     127            1 :         len = dir_hdr->hdr_len;
     128            1 :         dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len);
     129              : 
     130            1 :         pkg_dir[0] = PKG_DIR_HDR_MARK;
     131              :         /* pkg_dir entry count = component count + pkg_dir header */
     132            1 :         pkg_dir[1] = dir_hdr->ent_cnt + 1;
     133              : 
     134            1 :         for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) {
     135            0 :                 u64 *p = &pkg_dir[PKG_DIR_ENT_LEN *  (1 + i)];
     136              :                 int ver, id;
     137              : 
     138            0 :                 *p++ = dma_addr_module_data + dir_ent->offset;
     139              :                 id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata,
     140              :                                                     metadata_size, i);
     141            0 :                 if (id < 0 || id > MAX_COMPONENT_ID) {
     142            0 :                         dev_err(&isp->pdev->dev, "Invalid CPD component id\n");
     143            0 :                         return -EINVAL;
     144              :                 }
     145              : 
     146              :                 ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata,
     147              :                                                       metadata_size, i);
     148            0 :                 if (ver < 0 || ver > MAX_COMPONENT_VERSION) {
     149            0 :                         dev_err(&isp->pdev->dev,
     150              :                                 "Invalid CPD component version\n");
     151            0 :                         return -EINVAL;
     152              :                 }
     153              : 
     154            0 :                 *p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) |
     155            0 :                         FIELD_PREP(PKG_DIR_TYPE_MASK, id) |
     156            0 :                         FIELD_PREP(PKG_DIR_VERSION_MASK, ver);
     157              :         }
     158              : 
     159              :         return 0;
     160              : }
     161              : 
     162            1 : int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src)
     163              : {
     164            1 :         dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl);
     165              :         const struct ipu6_cpd_ent *ent, *man_ent, *met_ent;
     166            1 :         struct ipu6_device *isp = adev->isp;
     167              :         unsigned int man_sz, met_sz;
     168              :         void *pkg_dir_pos;
     169              :         int ret;
     170              : 
     171              :         man_ent = ipu6_cpd_get_manifest(src);
     172            1 :         man_sz = man_ent->len;
     173              : 
     174              :         met_ent = ipu6_cpd_get_metadata(src);
     175            1 :         met_sz = met_ent->len;
     176              : 
     177            1 :         adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz;
     178            1 :         adev->pkg_dir = ipu6_dma_alloc(adev, adev->pkg_dir_size,
     179              :                                        &adev->pkg_dir_dma_addr, GFP_KERNEL, 0);
     180            1 :         if (!adev->pkg_dir)
     181              :                 return -ENOMEM;
     182              : 
     183              :         /*
     184              :          * pkg_dir entry/header:
     185              :          * qword | 63:56 | 55   | 54:48 | 47:32 | 31:24 | 23:0
     186              :          * N         Address/Offset/"_IUPKDR_"
     187              :          * N + 1 | rsvd  | rsvd | type  | ver   | rsvd  | size
     188              :          *
     189              :          * We can ignore other fields that size in N + 1 qword as they
     190              :          * are 0 anyway. Just setting size for now.
     191              :          */
     192              : 
     193              :         ent = ipu6_cpd_get_moduledata(src);
     194              : 
     195            1 :         ret = ipu6_cpd_parse_module_data(isp, src + ent->offset,
     196            1 :                                          ent->len, dma_addr_src + ent->offset,
     197            1 :                                          adev->pkg_dir, src + met_ent->offset,
     198            1 :                                          met_ent->len);
     199            1 :         if (ret) {
     200            0 :                 dev_err(&isp->pdev->dev, "Failed to parse module data\n");
     201            0 :                 ipu6_dma_free(adev, adev->pkg_dir_size,
     202            0 :                               adev->pkg_dir, adev->pkg_dir_dma_addr, 0);
     203            0 :                 return ret;
     204              :         }
     205              : 
     206              :         /* Copy manifest after pkg_dir */
     207            1 :         pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT;
     208            1 :         memcpy(pkg_dir_pos, src + man_ent->offset, man_sz);
     209              : 
     210              :         /* Copy metadata after manifest */
     211            1 :         pkg_dir_pos += man_sz;
     212            1 :         memcpy(pkg_dir_pos, src + met_ent->offset, met_sz);
     213              : 
     214            1 :         ipu6_dma_sync_single(adev, adev->pkg_dir_dma_addr,
     215            1 :                              adev->pkg_dir_size);
     216              : 
     217            1 :         return 0;
     218              : }
     219              : EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6);
     220              : 
     221            0 : void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev)
     222              : {
     223            0 :         ipu6_dma_free(adev, adev->pkg_dir_size, adev->pkg_dir,
     224              :                       adev->pkg_dir_dma_addr, 0);
     225            0 : }
     226              : EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6);
     227              : 
     228            2 : static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd,
     229              :                                  unsigned long cpd_size,
     230              :                                  unsigned long data_size)
     231              : {
     232              :         const struct ipu6_cpd_hdr *cpd_hdr = cpd;
     233              :         const struct ipu6_cpd_ent *ent;
     234              :         unsigned int i;
     235              :         u8 len;
     236              : 
     237            2 :         len = cpd_hdr->hdr_len;
     238              : 
     239              :         /* Ensure cpd hdr is within moduledata */
     240            2 :         if (cpd_size < len) {
     241            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
     242            0 :                 return -EINVAL;
     243              :         }
     244              : 
     245              :         /* Sanity check for CPD header */
     246            2 :         if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {
     247            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD header\n");
     248            0 :                 return -EINVAL;
     249              :         }
     250              : 
     251              :         /* Ensure that all entries are within moduledata */
     252            2 :         ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len);
     253            5 :         for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) {
     254            3 :                 if (data_size < ent->offset ||
     255            3 :                     data_size - ent->offset < ent->len) {
     256            0 :                         dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i);
     257            0 :                         return -EINVAL;
     258              :                 }
     259              :         }
     260              : 
     261              :         return 0;
     262              : }
     263              : 
     264            1 : static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,
     265              :                                         const void *moduledata,
     266              :                                         u32 moduledata_size)
     267              : {
     268              :         const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata;
     269              :         int ret;
     270              : 
     271              :         /* Ensure moduledata hdr is within moduledata */
     272            1 :         if (moduledata_size < sizeof(*mod_hdr) ||
     273            1 :             moduledata_size < mod_hdr->hdr_len) {
     274            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
     275            0 :                 return -EINVAL;
     276              :         }
     277              : 
     278            1 :         dev_dbg(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date);
     279            1 :         ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len,
     280            1 :                                     moduledata_size - mod_hdr->hdr_len,
     281              :                                     moduledata_size);
     282            1 :         if (ret) {
     283            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n");
     284            0 :                 return ret;
     285              :         }
     286              : 
     287              :         return 0;
     288              : }
     289              : 
     290            1 : static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,
     291              :                                       const void *metadata, u32 meta_size)
     292              : {
     293              :         const struct ipu6_cpd_metadata_extn *extn = metadata;
     294              : 
     295              :         /* Sanity check for metadata size */
     296            1 :         if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) {
     297            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
     298            0 :                 return -EINVAL;
     299              :         }
     300              : 
     301              :         /* Validate extension and image types */
     302            1 :         if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT ||
     303            1 :             extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) {
     304            0 :                 dev_err(&isp->pdev->dev,
     305              :                         "Invalid CPD metadata descriptor img_type (%d)\n",
     306              :                         extn->img_type);
     307            0 :                 return -EINVAL;
     308              :         }
     309              : 
     310              :         /* Validate metadata size multiple of metadata components */
     311            1 :         if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) {
     312            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n");
     313            0 :                 return -EINVAL;
     314              :         }
     315              : 
     316              :         return 0;
     317              : }
     318              : 
     319            1 : int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
     320              :                                unsigned long cpd_file_size)
     321              : {
     322              :         const struct ipu6_cpd_hdr *hdr = cpd_file;
     323              :         const struct ipu6_cpd_ent *ent;
     324              :         int ret;
     325              : 
     326            1 :         ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size,
     327              :                                     cpd_file_size);
     328            1 :         if (ret) {
     329            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD in file\n");
     330            0 :                 return ret;
     331              :         }
     332              : 
     333              :         /* Check for CPD file marker */
     334            1 :         if (hdr->hdr_mark != CPD_HDR_MARK) {
     335            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD header\n");
     336            0 :                 return -EINVAL;
     337              :         }
     338              : 
     339              :         /* Sanity check for manifest size */
     340              :         ent = ipu6_cpd_get_manifest(cpd_file);
     341            1 :         if (ent->len > MAX_MANIFEST_SIZE) {
     342            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n");
     343            0 :                 return -EINVAL;
     344              :         }
     345              : 
     346              :         /* Validate metadata */
     347              :         ent = ipu6_cpd_get_metadata(cpd_file);
     348            1 :         ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len);
     349            1 :         if (ret) {
     350            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
     351            0 :                 return ret;
     352              :         }
     353              : 
     354              :         /* Validate moduledata */
     355              :         ent = ipu6_cpd_get_moduledata(cpd_file);
     356            1 :         ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset,
     357            1 :                                            ent->len);
     358            1 :         if (ret)
     359            0 :                 dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n");
     360              : 
     361              :         return ret;
     362              : }
        

Generated by: LCOV version 2.0-1