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 19 /* 15 entries + header*/ 20 #define MAX_PKG_DIR_ENT_CNT 16 21 /* 2 qword per entry/header */ 22 #define PKG_DIR_ENT_LEN 2 23 /* PKG_DIR size in bytes */ 24 #define PKG_DIR_SIZE ((MAX_PKG_DIR_ENT_CNT) * \ 25 (PKG_DIR_ENT_LEN) * sizeof(u64)) 26 /* _IUPKDR_ */ 27 #define PKG_DIR_HDR_MARK 0x5f4955504b44525fULL 28 29 /* $CPD */ 30 #define CPD_HDR_MARK 0x44504324 31 32 #define MAX_MANIFEST_SIZE (SZ_2K * sizeof(u32)) 33 #define MAX_METADATA_SIZE SZ_64K 34 35 #define MAX_COMPONENT_ID 127 36 #define MAX_COMPONENT_VERSION 0xffff 37 38 #define MANIFEST_IDX 0 39 #define METADATA_IDX 1 40 #define MODULEDATA_IDX 2 41 /* 42 * PKG_DIR Entry (type == id) 43 * 63:56 55 54:48 47:32 31:24 23:0 44 * Rsvd Rsvd Type Version Rsvd Size 45 */ 46 #define PKG_DIR_SIZE_MASK GENMASK(23, 0) 47 #define PKG_DIR_VERSION_MASK GENMASK(47, 32) 48 #define PKG_DIR_TYPE_MASK GENMASK(54, 48) 49 50 static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd, 51 u8 idx) 52 { 53 const struct ipu6_cpd_hdr *cpd_hdr = cpd; 54 const struct ipu6_cpd_ent *ent; 55 56 ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len); 57 return ent + idx; 58 } 59 60 #define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX) 61 #define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX) 62 #define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX) 63 64 static const struct ipu6_cpd_metadata_cmpnt_hdr * 65 ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata, 66 unsigned int metadata_size, u8 idx) 67 { 68 size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn); 69 size_t cmpnt_count = metadata_size - extn_size; 70 71 cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size); 72 73 if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) { 74 dev_err(&isp->pdev->dev, "Component index out of range (%d)\n", 75 idx); 76 return ERR_PTR(-EINVAL); 77 } 78 79 return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size; 80 } 81 82 static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp, 83 const void *metadata, 84 unsigned int metadata_size, u8 idx) 85 { 86 const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt; 87 88 cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx); 89 if (IS_ERR(cmpnt)) 90 return PTR_ERR(cmpnt); 91 92 return cmpnt->ver; 93 } 94 95 static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp, 96 const void *metadata, 97 unsigned int metadata_size, u8 idx) 98 { 99 const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt; 100 101 cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx); 102 if (IS_ERR(cmpnt)) 103 return PTR_ERR(cmpnt); 104 105 return cmpnt->id; 106 } 107 108 static int ipu6_cpd_parse_module_data(struct ipu6_device *isp, 109 const void *module_data, 110 unsigned int module_data_size, 111 dma_addr_t dma_addr_module_data, 112 u64 *pkg_dir, const void *metadata, 113 unsigned int metadata_size) 114 { 115 const struct ipu6_cpd_module_data_hdr *module_data_hdr; 116 const struct ipu6_cpd_hdr *dir_hdr; 117 const struct ipu6_cpd_ent *dir_ent; 118 unsigned int i; 119 u8 len; 120 121 if (!module_data) 122 return -EINVAL; 123 124 module_data_hdr = module_data; 125 dir_hdr = module_data + module_data_hdr->hdr_len; 126 len = dir_hdr->hdr_len; 127 dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len); 128 129 pkg_dir[0] = PKG_DIR_HDR_MARK; 130 /* pkg_dir entry count = component count + pkg_dir header */ 131 pkg_dir[1] = dir_hdr->ent_cnt + 1; 132 133 for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) { 134 u64 *p = &pkg_dir[PKG_DIR_ENT_LEN * (1 + i)]; 135 int ver, id; 136 137 *p++ = dma_addr_module_data + dir_ent->offset; 138 id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata, 139 metadata_size, i); 140 if (id < 0 || id > MAX_COMPONENT_ID) { 141 dev_err(&isp->pdev->dev, "Invalid CPD component id\n"); 142 return -EINVAL; 143 } 144 145 ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata, 146 metadata_size, i); 147 if (ver < 0 || ver > MAX_COMPONENT_VERSION) { 148 dev_err(&isp->pdev->dev, 149 "Invalid CPD component version\n"); 150 return -EINVAL; 151 } 152 153 *p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) | 154 FIELD_PREP(PKG_DIR_TYPE_MASK, id) | 155 FIELD_PREP(PKG_DIR_VERSION_MASK, ver); 156 } 157 158 return 0; 159 } 160 161 int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src) 162 { 163 dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl); 164 const struct ipu6_cpd_ent *ent, *man_ent, *met_ent; 165 struct device *dev = &adev->auxdev.dev; 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 = dma_alloc_attrs(dev, 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 dma_free_attrs(dev, 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 dma_sync_single_range_for_device(dev, adev->pkg_dir_dma_addr, 215 0, adev->pkg_dir_size, DMA_TO_DEVICE); 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 dma_free_attrs(&adev->auxdev.dev, 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