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