1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2015 - 2025 Intel Corporation 4 */ 5 6 #include <linux/device.h> 7 #include <linux/export.h> 8 #include <linux/gfp_types.h> 9 #include <linux/pci.h> 10 #include <linux/sizes.h> 11 #include <linux/slab.h> 12 #include <linux/types.h> 13 14 #include "ipu7.h" 15 #include "ipu7-cpd.h" 16 17 /* $CPD */ 18 #define CPD_HDR_MARK 0x44504324 19 20 /* Maximum size is 4K DWORDs or 16KB */ 21 #define MAX_MANIFEST_SIZE (SZ_4K * sizeof(u32)) 22 23 #define CPD_MANIFEST_IDX 0 24 #define CPD_BINARY_START_IDX 1U 25 #define CPD_METADATA_START_IDX 2U 26 #define CPD_BINARY_NUM 2U /* ISYS + PSYS */ 27 /* 28 * Entries include: 29 * 1 manifest entry. 30 * 1 metadata entry for each sub system(ISYS and PSYS). 31 * 1 binary entry for each sub system(ISYS and PSYS). 32 */ 33 #define CPD_ENTRY_NUM (CPD_BINARY_NUM * 2U + 1U) 34 35 #define CPD_METADATA_ATTR 0xa 36 #define CPD_METADATA_IPL 0x1c 37 #define ONLINE_METADATA_SIZE 128U 38 #define ONLINE_METADATA_LINES 6U 39 40 struct ipu7_cpd_hdr { 41 u32 hdr_mark; 42 u32 ent_cnt; 43 u8 hdr_ver; 44 u8 ent_ver; 45 u8 hdr_len; 46 u8 rsvd; 47 u8 partition_name[4]; 48 u32 crc32; 49 } __packed; 50 51 struct ipu7_cpd_ent { 52 u8 name[12]; 53 u32 offset; 54 u32 len; 55 u8 rsvd[4]; 56 } __packed; 57 58 struct ipu7_cpd_metadata_hdr { 59 u32 type; 60 u32 len; 61 } __packed; 62 63 struct ipu7_cpd_metadata_attr { 64 struct ipu7_cpd_metadata_hdr hdr; 65 u8 compression_type; 66 u8 encryption_type; 67 u8 rsvd[2]; 68 u32 uncompressed_size; 69 u32 compressed_size; 70 u32 module_id; 71 u8 hash[48]; 72 } __packed; 73 74 struct ipu7_cpd_metadata_ipl { 75 struct ipu7_cpd_metadata_hdr hdr; 76 u32 param[4]; 77 u8 rsvd[8]; 78 } __packed; 79 80 struct ipu7_cpd_metadata { 81 struct ipu7_cpd_metadata_attr attr; 82 struct ipu7_cpd_metadata_ipl ipl; 83 } __packed; 84 85 static inline struct ipu7_cpd_ent *ipu7_cpd_get_entry(const void *cpd, int idx) 86 { 87 const struct ipu7_cpd_hdr *cpd_hdr = cpd; 88 89 return ((struct ipu7_cpd_ent *)((u8 *)cpd + cpd_hdr->hdr_len)) + idx; 90 } 91 92 #define ipu7_cpd_get_manifest(cpd) ipu7_cpd_get_entry(cpd, 0) 93 94 static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx) 95 { 96 struct ipu7_cpd_ent *cpd_ent = 97 ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); 98 99 return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset); 100 } 101 102 static int ipu7_cpd_validate_cpd(struct ipu7_device *isp, 103 const void *cpd, unsigned long data_size) 104 { 105 const struct ipu7_cpd_hdr *cpd_hdr = cpd; 106 struct device *dev = &isp->pdev->dev; 107 struct ipu7_cpd_ent *ent; 108 unsigned int i; 109 u8 len; 110 111 len = cpd_hdr->hdr_len; 112 113 /* Ensure cpd hdr is within moduledata */ 114 if (data_size < len) { 115 dev_err(dev, "Invalid CPD moduledata size\n"); 116 return -EINVAL; 117 } 118 119 /* Check for CPD file marker */ 120 if (cpd_hdr->hdr_mark != CPD_HDR_MARK) { 121 dev_err(dev, "Invalid CPD header marker\n"); 122 return -EINVAL; 123 } 124 125 /* Sanity check for CPD entry header */ 126 if (cpd_hdr->ent_cnt != CPD_ENTRY_NUM) { 127 dev_err(dev, "Invalid CPD entry number %d\n", 128 cpd_hdr->ent_cnt); 129 return -EINVAL; 130 } 131 if ((data_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) { 132 dev_err(dev, "Invalid CPD entry headers\n"); 133 return -EINVAL; 134 } 135 136 /* Ensure that all entries are within moduledata */ 137 ent = (struct ipu7_cpd_ent *)(((u8 *)cpd_hdr) + len); 138 for (i = 0; i < cpd_hdr->ent_cnt; i++) { 139 if (data_size < ent->offset || 140 data_size - ent->offset < ent->len) { 141 dev_err(dev, "Invalid CPD entry %d\n", i); 142 return -EINVAL; 143 } 144 ent++; 145 } 146 147 return 0; 148 } 149 150 static int ipu7_cpd_validate_metadata(struct ipu7_device *isp, 151 const void *cpd, int idx) 152 { 153 const struct ipu7_cpd_ent *cpd_ent = 154 ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); 155 const struct ipu7_cpd_metadata *metadata = 156 ipu7_cpd_get_metadata(cpd, idx); 157 struct device *dev = &isp->pdev->dev; 158 159 /* Sanity check for metadata size */ 160 if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) { 161 dev_err(dev, "Invalid metadata size\n"); 162 return -EINVAL; 163 } 164 165 /* Validate type and length of metadata sections */ 166 if (metadata->attr.hdr.type != CPD_METADATA_ATTR) { 167 dev_err(dev, "Invalid metadata attr type (%d)\n", 168 metadata->attr.hdr.type); 169 return -EINVAL; 170 } 171 if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) { 172 dev_err(dev, "Invalid metadata attr size (%d)\n", 173 metadata->attr.hdr.len); 174 return -EINVAL; 175 } 176 if (metadata->ipl.hdr.type != CPD_METADATA_IPL) { 177 dev_err(dev, "Invalid metadata ipl type (%d)\n", 178 metadata->ipl.hdr.type); 179 return -EINVAL; 180 } 181 if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) { 182 dev_err(dev, "Invalid metadata ipl size (%d)\n", 183 metadata->ipl.hdr.len); 184 return -EINVAL; 185 } 186 187 return 0; 188 } 189 190 int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, const void *cpd_file, 191 unsigned long cpd_file_size) 192 { 193 struct device *dev = &isp->pdev->dev; 194 struct ipu7_cpd_ent *ent; 195 unsigned int i; 196 int ret; 197 char *buf; 198 199 ret = ipu7_cpd_validate_cpd(isp, cpd_file, cpd_file_size); 200 if (ret) { 201 dev_err(dev, "Invalid CPD in file\n"); 202 return -EINVAL; 203 } 204 205 /* Sanity check for manifest size */ 206 ent = ipu7_cpd_get_manifest(cpd_file); 207 if (ent->len > MAX_MANIFEST_SIZE) { 208 dev_err(dev, "Invalid manifest size\n"); 209 return -EINVAL; 210 } 211 212 /* Validate metadata */ 213 for (i = 0; i < CPD_BINARY_NUM; i++) { 214 ret = ipu7_cpd_validate_metadata(isp, cpd_file, i); 215 if (ret) { 216 dev_err(dev, "Invalid metadata%d\n", i); 217 return ret; 218 } 219 } 220 221 /* Get fw binary version. */ 222 buf = kmalloc(ONLINE_METADATA_SIZE, GFP_KERNEL); 223 if (!buf) 224 return -ENOMEM; 225 for (i = 0; i < CPD_BINARY_NUM; i++) { 226 char *lines[ONLINE_METADATA_LINES]; 227 char *info = buf; 228 unsigned int l; 229 230 ent = ipu7_cpd_get_entry(cpd_file, 231 CPD_BINARY_START_IDX + i * 2U); 232 memcpy(info, (u8 *)cpd_file + ent->offset + ent->len - 233 ONLINE_METADATA_SIZE, ONLINE_METADATA_SIZE); 234 for (l = 0; l < ONLINE_METADATA_LINES; l++) { 235 lines[l] = strsep((char **)&info, "\n"); 236 if (!lines[l]) 237 break; 238 } 239 if (l < ONLINE_METADATA_LINES) { 240 dev_err(dev, "Failed to parse fw binary%d info.\n", i); 241 continue; 242 } 243 dev_info(dev, "FW binary%d info:\n", i); 244 dev_info(dev, "Name: %s\n", lines[1]); 245 dev_info(dev, "Version: %s\n", lines[2]); 246 dev_info(dev, "Timestamp: %s\n", lines[3]); 247 dev_info(dev, "Commit: %s\n", lines[4]); 248 } 249 kfree(buf); 250 251 return 0; 252 } 253 EXPORT_SYMBOL_NS_GPL(ipu7_cpd_validate_cpd_file, "INTEL_IPU7"); 254 255 int ipu7_cpd_copy_binary(const void *cpd, const char *name, 256 void *code_region, u32 *entry) 257 { 258 unsigned int i; 259 260 for (i = 0; i < CPD_BINARY_NUM; i++) { 261 const struct ipu7_cpd_ent *binary = 262 ipu7_cpd_get_entry(cpd, CPD_BINARY_START_IDX + i * 2U); 263 const struct ipu7_cpd_metadata *metadata = 264 ipu7_cpd_get_metadata(cpd, i); 265 266 if (!strncmp(binary->name, name, sizeof(binary->name))) { 267 memcpy(code_region + metadata->ipl.param[0], 268 cpd + binary->offset, binary->len); 269 *entry = metadata->ipl.param[2]; 270 return 0; 271 } 272 } 273 274 return -ENOENT; 275 } 276 EXPORT_SYMBOL_NS_GPL(ipu7_cpd_copy_binary, "INTEL_IPU7"); 277