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 ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len); 58 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 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 size_t cmpnt_count = metadata_size - extn_size; 71 72 cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size); 73 74 if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) { 75 dev_err(&isp->pdev->dev, "Component index out of range (%d)\n", 76 idx); 77 return ERR_PTR(-EINVAL); 78 } 79 80 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 cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx); 90 if (IS_ERR(cmpnt)) 91 return PTR_ERR(cmpnt); 92 93 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 cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx); 103 if (IS_ERR(cmpnt)) 104 return PTR_ERR(cmpnt); 105 106 return cmpnt->id; 107 } 108 109 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 if (!module_data) 123 return -EINVAL; 124 125 module_data_hdr = module_data; 126 dir_hdr = module_data + module_data_hdr->hdr_len; 127 len = dir_hdr->hdr_len; 128 dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len); 129 130 pkg_dir[0] = PKG_DIR_HDR_MARK; 131 /* pkg_dir entry count = component count + pkg_dir header */ 132 pkg_dir[1] = dir_hdr->ent_cnt + 1; 133 134 for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) { 135 u64 *p = &pkg_dir[PKG_DIR_ENT_LEN * (1 + i)]; 136 int ver, id; 137 138 *p++ = dma_addr_module_data + dir_ent->offset; 139 id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata, 140 metadata_size, i); 141 if (id < 0 || id > MAX_COMPONENT_ID) { 142 dev_err(&isp->pdev->dev, "Invalid CPD component id\n"); 143 return -EINVAL; 144 } 145 146 ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata, 147 metadata_size, i); 148 if (ver < 0 || ver > MAX_COMPONENT_VERSION) { 149 dev_err(&isp->pdev->dev, 150 "Invalid CPD component version\n"); 151 return -EINVAL; 152 } 153 154 *p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) | 155 FIELD_PREP(PKG_DIR_TYPE_MASK, id) | 156 FIELD_PREP(PKG_DIR_VERSION_MASK, ver); 157 } 158 159 return 0; 160 } 161 162 int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src) 163 { 164 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 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 man_sz = man_ent->len; 173 174 met_ent = ipu6_cpd_get_metadata(src); 175 met_sz = met_ent->len; 176 177 adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz; 178 adev->pkg_dir = ipu6_dma_alloc(adev, adev->pkg_dir_size, 179 &adev->pkg_dir_dma_addr, GFP_KERNEL, 0); 180 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 ret = ipu6_cpd_parse_module_data(isp, src + ent->offset, 196 ent->len, dma_addr_src + ent->offset, 197 adev->pkg_dir, src + met_ent->offset, 198 met_ent->len); 199 if (ret) { 200 dev_err(&isp->pdev->dev, "Failed to parse module data\n"); 201 ipu6_dma_free(adev, adev->pkg_dir_size, 202 adev->pkg_dir, adev->pkg_dir_dma_addr, 0); 203 return ret; 204 } 205 206 /* Copy manifest after pkg_dir */ 207 pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT; 208 memcpy(pkg_dir_pos, src + man_ent->offset, man_sz); 209 210 /* Copy metadata after manifest */ 211 pkg_dir_pos += man_sz; 212 memcpy(pkg_dir_pos, src + met_ent->offset, met_sz); 213 214 ipu6_dma_sync_single(adev, adev->pkg_dir_dma_addr, 215 adev->pkg_dir_size); 216 217 return 0; 218 } 219 EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6); 220 221 void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev) 222 { 223 ipu6_dma_free(adev, adev->pkg_dir_size, adev->pkg_dir, 224 adev->pkg_dir_dma_addr, 0); 225 } 226 EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6); 227 228 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 len = cpd_hdr->hdr_len; 238 239 /* Ensure cpd hdr is within moduledata */ 240 if (cpd_size < len) { 241 dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n"); 242 return -EINVAL; 243 } 244 245 /* Sanity check for CPD header */ 246 if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) { 247 dev_err(&isp->pdev->dev, "Invalid CPD header\n"); 248 return -EINVAL; 249 } 250 251 /* Ensure that all entries are within moduledata */ 252 ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len); 253 for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) { 254 if (data_size < ent->offset || 255 data_size - ent->offset < ent->len) { 256 dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i); 257 return -EINVAL; 258 } 259 } 260 261 return 0; 262 } 263 264 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 if (moduledata_size < sizeof(*mod_hdr) || 273 moduledata_size < mod_hdr->hdr_len) { 274 dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n"); 275 return -EINVAL; 276 } 277 278 dev_info(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date); 279 ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len, 280 moduledata_size - mod_hdr->hdr_len, 281 moduledata_size); 282 if (ret) { 283 dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n"); 284 return ret; 285 } 286 287 return 0; 288 } 289 290 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 if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) { 297 dev_err(&isp->pdev->dev, "Invalid CPD metadata\n"); 298 return -EINVAL; 299 } 300 301 /* Validate extension and image types */ 302 if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT || 303 extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) { 304 dev_err(&isp->pdev->dev, 305 "Invalid CPD metadata descriptor img_type (%d)\n", 306 extn->img_type); 307 return -EINVAL; 308 } 309 310 /* Validate metadata size multiple of metadata components */ 311 if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) { 312 dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n"); 313 return -EINVAL; 314 } 315 316 return 0; 317 } 318 319 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 ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size, 327 cpd_file_size); 328 if (ret) { 329 dev_err(&isp->pdev->dev, "Invalid CPD in file\n"); 330 return ret; 331 } 332 333 /* Check for CPD file marker */ 334 if (hdr->hdr_mark != CPD_HDR_MARK) { 335 dev_err(&isp->pdev->dev, "Invalid CPD header\n"); 336 return -EINVAL; 337 } 338 339 /* Sanity check for manifest size */ 340 ent = ipu6_cpd_get_manifest(cpd_file); 341 if (ent->len > MAX_MANIFEST_SIZE) { 342 dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n"); 343 return -EINVAL; 344 } 345 346 /* Validate metadata */ 347 ent = ipu6_cpd_get_metadata(cpd_file); 348 ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len); 349 if (ret) { 350 dev_err(&isp->pdev->dev, "Invalid CPD metadata\n"); 351 return ret; 352 } 353 354 /* Validate moduledata */ 355 ent = ipu6_cpd_get_moduledata(cpd_file); 356 ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset, 357 ent->len); 358 if (ret) 359 dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n"); 360 361 return ret; 362 } 363