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