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