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) 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 /** 336 * qcom_mdt_load_no_init() - load the firmware which header is loaded as fw 337 * @dev: device handle to associate resources with 338 * @fw: firmware object for the mdt file 339 * @fw_name: name of the firmware, for construction of segment file names 340 * @mem_region: allocated memory region to load firmware into 341 * @mem_phys: physical address of allocated memory region 342 * @mem_size: size of the allocated memory region 343 * @reloc_base: adjusted physical address after relocation 344 * 345 * Returns 0 on success, negative errno otherwise. 346 */ 347 int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, 348 const char *fw_name, void *mem_region, 349 phys_addr_t mem_phys, size_t mem_size, 350 phys_addr_t *reloc_base) 351 { 352 const struct elf32_phdr *phdrs; 353 const struct elf32_phdr *phdr; 354 const struct elf32_hdr *ehdr; 355 phys_addr_t mem_reloc; 356 phys_addr_t min_addr = PHYS_ADDR_MAX; 357 ssize_t offset; 358 bool relocate = false; 359 bool is_split; 360 void *ptr; 361 int ret = 0; 362 int i; 363 364 if (!fw || !mem_region || !mem_phys || !mem_size) 365 return -EINVAL; 366 367 if (!mdt_header_valid(fw)) 368 return -EINVAL; 369 370 is_split = qcom_mdt_bins_are_split(fw); 371 ehdr = (struct elf32_hdr *)fw->data; 372 phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); 373 374 for (i = 0; i < ehdr->e_phnum; i++) { 375 phdr = &phdrs[i]; 376 377 if (!mdt_phdr_loadable(phdr)) 378 continue; 379 380 if (phdr->p_flags & QCOM_MDT_RELOCATABLE) 381 relocate = true; 382 383 if (phdr->p_paddr < min_addr) 384 min_addr = phdr->p_paddr; 385 } 386 387 if (relocate) { 388 /* 389 * The image is relocatable, so offset each segment based on 390 * the lowest segment address. 391 */ 392 mem_reloc = min_addr; 393 } else { 394 /* 395 * Image is not relocatable, so offset each segment based on 396 * the allocated physical chunk of memory. 397 */ 398 mem_reloc = mem_phys; 399 } 400 401 for (i = 0; i < ehdr->e_phnum; i++) { 402 phdr = &phdrs[i]; 403 404 if (!mdt_phdr_loadable(phdr)) 405 continue; 406 407 offset = phdr->p_paddr - mem_reloc; 408 if (offset < 0 || offset + phdr->p_memsz > mem_size) { 409 dev_err(dev, "segment outside memory range\n"); 410 ret = -EINVAL; 411 break; 412 } 413 414 if (phdr->p_filesz > phdr->p_memsz) { 415 dev_err(dev, 416 "refusing to load segment %d with p_filesz > p_memsz\n", 417 i); 418 ret = -EINVAL; 419 break; 420 } 421 422 ptr = mem_region + offset; 423 424 if (phdr->p_filesz && !is_split) { 425 /* Firmware is large enough to be non-split */ 426 if (phdr->p_offset + phdr->p_filesz > fw->size) { 427 dev_err(dev, "file %s segment %d would be truncated\n", 428 fw_name, i); 429 ret = -EINVAL; 430 break; 431 } 432 433 memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz); 434 } else if (phdr->p_filesz) { 435 /* Firmware not large enough, load split-out segments */ 436 ret = mdt_load_split_segment(ptr, phdrs, i, fw_name, dev); 437 if (ret) 438 break; 439 } 440 441 if (phdr->p_memsz > phdr->p_filesz) 442 memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); 443 } 444 445 if (reloc_base) 446 *reloc_base = mem_reloc; 447 448 return ret; 449 } 450 EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); 451 452 /** 453 * qcom_mdt_load() - load the firmware which header is loaded as fw 454 * @dev: device handle to associate resources with 455 * @fw: firmware object for the mdt file 456 * @fw_name: name of the firmware, for construction of segment file names 457 * @pas_id: PAS identifier 458 * @mem_region: allocated memory region to load firmware into 459 * @mem_phys: physical address of allocated memory region 460 * @mem_size: size of the allocated memory region 461 * @reloc_base: adjusted physical address after relocation 462 * 463 * Returns 0 on success, negative errno otherwise. 464 */ 465 int qcom_mdt_load(struct device *dev, const struct firmware *fw, 466 const char *fw_name, int pas_id, void *mem_region, 467 phys_addr_t mem_phys, size_t mem_size, 468 phys_addr_t *reloc_base) 469 { 470 int ret; 471 472 ret = qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL); 473 if (ret) 474 return ret; 475 476 return qcom_mdt_load_no_init(dev, fw, fw_name, mem_region, mem_phys, 477 mem_size, reloc_base); 478 } 479 EXPORT_SYMBOL_GPL(qcom_mdt_load); 480 481 MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); 482 MODULE_LICENSE("GPL v2"); 483