1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Qualcomm Peripheral Image Loader 4 * 5 * Copyright (C) 2016 Linaro Ltd 6 * Copyright (C) 2015 Sony Mobile Communications Inc 7 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 8 */ 9 10 #include <linux/cleanup.h> 11 #include <linux/device.h> 12 #include <linux/elf.h> 13 #include <linux/firmware.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/firmware/qcom/qcom_scm.h> 17 #include <linux/sizes.h> 18 #include <linux/slab.h> 19 #include <linux/soc/qcom/mdt_loader.h> 20 21 static bool mdt_header_valid(const struct firmware *fw) 22 { 23 const struct elf32_hdr *ehdr; 24 size_t phend; 25 size_t shend; 26 27 if (fw->size < sizeof(*ehdr)) 28 return false; 29 30 ehdr = (struct elf32_hdr *)fw->data; 31 32 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) 33 return false; 34 35 if (ehdr->e_phentsize != sizeof(struct elf32_phdr)) 36 return false; 37 38 phend = size_add(size_mul(sizeof(struct elf32_phdr), ehdr->e_phnum), ehdr->e_phoff); 39 if (phend > fw->size) 40 return false; 41 42 if (ehdr->e_shentsize || ehdr->e_shnum) { 43 if (ehdr->e_shentsize != sizeof(struct elf32_shdr)) 44 return false; 45 46 shend = size_add(size_mul(sizeof(struct elf32_shdr), ehdr->e_shnum), ehdr->e_shoff); 47 if (shend > fw->size) 48 return false; 49 } 50 51 return true; 52 } 53 54 static bool mdt_phdr_loadable(const struct elf32_phdr *phdr) 55 { 56 if (phdr->p_type != PT_LOAD) 57 return false; 58 59 if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) 60 return false; 61 62 if (!phdr->p_memsz) 63 return false; 64 65 return true; 66 } 67 68 static ssize_t mdt_load_split_segment(void *ptr, const struct elf32_phdr *phdrs, 69 unsigned int segment, const char *fw_name, 70 struct device *dev) 71 { 72 const struct elf32_phdr *phdr = &phdrs[segment]; 73 const struct firmware *seg_fw; 74 ssize_t ret; 75 76 if (strlen(fw_name) < 4) 77 return -EINVAL; 78 79 char *seg_name __free(kfree) = kstrdup(fw_name, GFP_KERNEL); 80 if (!seg_name) 81 return -ENOMEM; 82 83 sprintf(seg_name + strlen(fw_name) - 3, "b%02d", segment); 84 ret = request_firmware_into_buf(&seg_fw, seg_name, dev, 85 ptr, phdr->p_filesz); 86 if (ret) { 87 dev_err(dev, "error %zd loading %s\n", ret, seg_name); 88 return ret; 89 } 90 91 if (seg_fw->size != phdr->p_filesz) { 92 dev_err(dev, 93 "failed to load segment %d from truncated file %s\n", 94 segment, seg_name); 95 ret = -EINVAL; 96 } 97 98 release_firmware(seg_fw); 99 100 return ret; 101 } 102 103 /** 104 * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt 105 * @fw: firmware object for the mdt file 106 * 107 * Returns size of the loaded firmware blob, or -EINVAL on failure. 108 */ 109 ssize_t qcom_mdt_get_size(const struct firmware *fw) 110 { 111 const struct elf32_phdr *phdrs; 112 const struct elf32_phdr *phdr; 113 const struct elf32_hdr *ehdr; 114 phys_addr_t min_addr = PHYS_ADDR_MAX; 115 phys_addr_t max_addr = 0; 116 int i; 117 118 if (!mdt_header_valid(fw)) 119 return -EINVAL; 120 121 ehdr = (struct elf32_hdr *)fw->data; 122 phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); 123 124 for (i = 0; i < ehdr->e_phnum; i++) { 125 phdr = &phdrs[i]; 126 127 if (!mdt_phdr_loadable(phdr)) 128 continue; 129 130 if (phdr->p_paddr < min_addr) 131 min_addr = phdr->p_paddr; 132 133 if (phdr->p_paddr + phdr->p_memsz > max_addr) 134 max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); 135 } 136 137 return min_addr < max_addr ? max_addr - min_addr : -EINVAL; 138 } 139 EXPORT_SYMBOL_GPL(qcom_mdt_get_size); 140 141 /** 142 * qcom_mdt_read_metadata() - read header and metadata from mdt or mbn 143 * @fw: firmware of mdt header or mbn 144 * @data_len: length of the read metadata blob 145 * @fw_name: name of the firmware, for construction of segment file names 146 * @dev: device handle to associate resources with 147 * 148 * The mechanism that performs the authentication of the loading firmware 149 * expects an ELF header directly followed by the segment of hashes, with no 150 * padding inbetween. This function allocates a chunk of memory for this pair 151 * and copy the two pieces into the buffer. 152 * 153 * In the case of split firmware the hash is found directly following the ELF 154 * header, rather than at p_offset described by the second program header. 155 * 156 * The caller is responsible to free (kfree()) the returned pointer. 157 * 158 * Return: pointer to data, or ERR_PTR() 159 */ 160 void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, 161 const char *fw_name, struct device *dev) 162 { 163 const struct elf32_phdr *phdrs; 164 const struct elf32_hdr *ehdr; 165 unsigned int hash_segment = 0; 166 size_t hash_offset; 167 size_t hash_size; 168 size_t ehdr_size; 169 unsigned int i; 170 ssize_t ret; 171 void *data; 172 173 if (!mdt_header_valid(fw)) 174 return ERR_PTR(-EINVAL); 175 176 ehdr = (struct elf32_hdr *)fw->data; 177 phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); 178 179 if (ehdr->e_phnum < 2) 180 return ERR_PTR(-EINVAL); 181 182 if (phdrs[0].p_type == PT_LOAD) 183 return ERR_PTR(-EINVAL); 184 185 for (i = 1; i < ehdr->e_phnum; i++) { 186 if ((phdrs[i].p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) { 187 hash_segment = i; 188 break; 189 } 190 } 191 192 if (!hash_segment) { 193 dev_err(dev, "no hash segment found in %s\n", fw_name); 194 return ERR_PTR(-EINVAL); 195 } 196 197 ehdr_size = phdrs[0].p_filesz; 198 hash_size = phdrs[hash_segment].p_filesz; 199 200 data = kmalloc(ehdr_size + hash_size, GFP_KERNEL); 201 if (!data) 202 return ERR_PTR(-ENOMEM); 203 204 /* Copy ELF header */ 205 memcpy(data, fw->data, ehdr_size); 206 207 if (ehdr_size + hash_size == fw->size) { 208 /* Firmware is split and hash is packed following the ELF header */ 209 hash_offset = phdrs[0].p_filesz; 210 memcpy(data + ehdr_size, fw->data + hash_offset, hash_size); 211 } else if (phdrs[hash_segment].p_offset + hash_size <= fw->size) { 212 /* Hash is in its own segment, but within the loaded file */ 213 hash_offset = phdrs[hash_segment].p_offset; 214 memcpy(data + ehdr_size, fw->data + hash_offset, hash_size); 215 } else { 216 /* Hash is in its own segment, beyond the loaded file */ 217 ret = mdt_load_split_segment(data + ehdr_size, phdrs, hash_segment, fw_name, dev); 218 if (ret) { 219 kfree(data); 220 return ERR_PTR(ret); 221 } 222 } 223 224 *data_len = ehdr_size + hash_size; 225 226 return data; 227 } 228 EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); 229 230 /** 231 * qcom_mdt_pas_init() - initialize PAS region for firmware loading 232 * @dev: device handle to associate resources with 233 * @fw: firmware object for the mdt file 234 * @fw_name: name of the firmware, for construction of segment file names 235 * @pas_id: PAS identifier 236 * @mem_phys: physical address of allocated memory region 237 * @ctx: PAS metadata context, to be released by caller 238 * 239 * Returns 0 on success, negative errno otherwise. 240 */ 241 int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, 242 const char *fw_name, int pas_id, phys_addr_t mem_phys, 243 struct qcom_scm_pas_metadata *ctx) 244 { 245 const struct elf32_phdr *phdrs; 246 const struct elf32_phdr *phdr; 247 const struct elf32_hdr *ehdr; 248 phys_addr_t min_addr = PHYS_ADDR_MAX; 249 phys_addr_t max_addr = 0; 250 bool relocate = false; 251 size_t metadata_len; 252 void *metadata; 253 int ret; 254 int i; 255 256 if (!mdt_header_valid(fw)) 257 return -EINVAL; 258 259 ehdr = (struct elf32_hdr *)fw->data; 260 phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); 261 262 for (i = 0; i < ehdr->e_phnum; i++) { 263 phdr = &phdrs[i]; 264 265 if (!mdt_phdr_loadable(phdr)) 266 continue; 267 268 if (phdr->p_flags & QCOM_MDT_RELOCATABLE) 269 relocate = true; 270 271 if (phdr->p_paddr < min_addr) 272 min_addr = phdr->p_paddr; 273 274 if (phdr->p_paddr + phdr->p_memsz > max_addr) 275 max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); 276 } 277 278 metadata = qcom_mdt_read_metadata(fw, &metadata_len, fw_name, dev); 279 if (IS_ERR(metadata)) { 280 ret = PTR_ERR(metadata); 281 dev_err(dev, "error %d reading firmware %s metadata\n", ret, fw_name); 282 goto out; 283 } 284 285 ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len, ctx); 286 kfree(metadata); 287 if (ret) { 288 /* Invalid firmware metadata */ 289 dev_err(dev, "error %d initializing firmware %s\n", ret, fw_name); 290 goto out; 291 } 292 293 if (relocate) { 294 ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); 295 if (ret) { 296 /* Unable to set up relocation */ 297 dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); 298 goto out; 299 } 300 } 301 302 out: 303 return ret; 304 } 305 EXPORT_SYMBOL_GPL(qcom_mdt_pas_init); 306 307 static bool qcom_mdt_bins_are_split(const struct firmware *fw, const char *fw_name) 308 { 309 const struct elf32_phdr *phdrs; 310 const struct elf32_hdr *ehdr; 311 uint64_t seg_start, seg_end; 312 int i; 313 314 ehdr = (struct elf32_hdr *)fw->data; 315 phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); 316 317 for (i = 0; i < ehdr->e_phnum; i++) { 318 /* 319 * The size of the MDT file is not padded to include any 320 * zero-sized segments at the end. Ignore these, as they should 321 * not affect the decision about image being split or not. 322 */ 323 if (!phdrs[i].p_filesz) 324 continue; 325 326 seg_start = phdrs[i].p_offset; 327 seg_end = phdrs[i].p_offset + phdrs[i].p_filesz; 328 if (seg_start > fw->size || seg_end > fw->size) 329 return true; 330 } 331 332 return false; 333 } 334 335 static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, 336 const char *fw_name, int pas_id, void *mem_region, 337 phys_addr_t mem_phys, size_t mem_size, 338 phys_addr_t *reloc_base, bool pas_init) 339 { 340 const struct elf32_phdr *phdrs; 341 const struct elf32_phdr *phdr; 342 const struct elf32_hdr *ehdr; 343 phys_addr_t mem_reloc; 344 phys_addr_t min_addr = PHYS_ADDR_MAX; 345 ssize_t offset; 346 bool relocate = false; 347 bool is_split; 348 void *ptr; 349 int ret = 0; 350 int i; 351 352 if (!fw || !mem_region || !mem_phys || !mem_size) 353 return -EINVAL; 354 355 if (!mdt_header_valid(fw)) 356 return -EINVAL; 357 358 is_split = qcom_mdt_bins_are_split(fw, fw_name); 359 ehdr = (struct elf32_hdr *)fw->data; 360 phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); 361 362 for (i = 0; i < ehdr->e_phnum; i++) { 363 phdr = &phdrs[i]; 364 365 if (!mdt_phdr_loadable(phdr)) 366 continue; 367 368 if (phdr->p_flags & QCOM_MDT_RELOCATABLE) 369 relocate = true; 370 371 if (phdr->p_paddr < min_addr) 372 min_addr = phdr->p_paddr; 373 } 374 375 if (relocate) { 376 /* 377 * The image is relocatable, so offset each segment based on 378 * the lowest segment address. 379 */ 380 mem_reloc = min_addr; 381 } else { 382 /* 383 * Image is not relocatable, so offset each segment based on 384 * the allocated physical chunk of memory. 385 */ 386 mem_reloc = mem_phys; 387 } 388 389 for (i = 0; i < ehdr->e_phnum; i++) { 390 phdr = &phdrs[i]; 391 392 if (!mdt_phdr_loadable(phdr)) 393 continue; 394 395 offset = phdr->p_paddr - mem_reloc; 396 if (offset < 0 || offset + phdr->p_memsz > mem_size) { 397 dev_err(dev, "segment outside memory range\n"); 398 ret = -EINVAL; 399 break; 400 } 401 402 if (phdr->p_filesz > phdr->p_memsz) { 403 dev_err(dev, 404 "refusing to load segment %d with p_filesz > p_memsz\n", 405 i); 406 ret = -EINVAL; 407 break; 408 } 409 410 ptr = mem_region + offset; 411 412 if (phdr->p_filesz && !is_split) { 413 /* Firmware is large enough to be non-split */ 414 if (phdr->p_offset + phdr->p_filesz > fw->size) { 415 dev_err(dev, "file %s segment %d would be truncated\n", 416 fw_name, i); 417 ret = -EINVAL; 418 break; 419 } 420 421 memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz); 422 } else if (phdr->p_filesz) { 423 /* Firmware not large enough, load split-out segments */ 424 ret = mdt_load_split_segment(ptr, phdrs, i, fw_name, dev); 425 if (ret) 426 break; 427 } 428 429 if (phdr->p_memsz > phdr->p_filesz) 430 memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); 431 } 432 433 if (reloc_base) 434 *reloc_base = mem_reloc; 435 436 return ret; 437 } 438 439 /** 440 * qcom_mdt_load() - load the firmware which header is loaded as fw 441 * @dev: device handle to associate resources with 442 * @fw: firmware object for the mdt file 443 * @firmware: name of the firmware, for construction of segment file names 444 * @pas_id: PAS identifier 445 * @mem_region: allocated memory region to load firmware into 446 * @mem_phys: physical address of allocated memory region 447 * @mem_size: size of the allocated memory region 448 * @reloc_base: adjusted physical address after relocation 449 * 450 * Returns 0 on success, negative errno otherwise. 451 */ 452 int qcom_mdt_load(struct device *dev, const struct firmware *fw, 453 const char *firmware, int pas_id, void *mem_region, 454 phys_addr_t mem_phys, size_t mem_size, 455 phys_addr_t *reloc_base) 456 { 457 int ret; 458 459 ret = qcom_mdt_pas_init(dev, fw, firmware, pas_id, mem_phys, NULL); 460 if (ret) 461 return ret; 462 463 return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, 464 mem_size, reloc_base, true); 465 } 466 EXPORT_SYMBOL_GPL(qcom_mdt_load); 467 468 /** 469 * qcom_mdt_load_no_init() - load the firmware which header is loaded as fw 470 * @dev: device handle to associate resources with 471 * @fw: firmware object for the mdt file 472 * @firmware: name of the firmware, for construction of segment file names 473 * @pas_id: PAS identifier 474 * @mem_region: allocated memory region to load firmware into 475 * @mem_phys: physical address of allocated memory region 476 * @mem_size: size of the allocated memory region 477 * @reloc_base: adjusted physical address after relocation 478 * 479 * Returns 0 on success, negative errno otherwise. 480 */ 481 int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, 482 const char *firmware, int pas_id, 483 void *mem_region, phys_addr_t mem_phys, 484 size_t mem_size, phys_addr_t *reloc_base) 485 { 486 return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, 487 mem_size, reloc_base, false); 488 } 489 EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); 490 491 MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); 492 MODULE_LICENSE("GPL v2"); 493