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/device.h> 11 #include <linux/elf.h> 12 #include <linux/firmware.h> 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/qcom_scm.h> 16 #include <linux/sizes.h> 17 #include <linux/slab.h> 18 #include <linux/soc/qcom/mdt_loader.h> 19 20 static bool mdt_phdr_valid(const struct elf32_phdr *phdr) 21 { 22 if (phdr->p_type != PT_LOAD) 23 return false; 24 25 if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) 26 return false; 27 28 if (!phdr->p_memsz) 29 return false; 30 31 return true; 32 } 33 34 /** 35 * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt 36 * @fw: firmware object for the mdt file 37 * 38 * Returns size of the loaded firmware blob, or -EINVAL on failure. 39 */ 40 ssize_t qcom_mdt_get_size(const struct firmware *fw) 41 { 42 const struct elf32_phdr *phdrs; 43 const struct elf32_phdr *phdr; 44 const struct elf32_hdr *ehdr; 45 phys_addr_t min_addr = PHYS_ADDR_MAX; 46 phys_addr_t max_addr = 0; 47 int i; 48 49 ehdr = (struct elf32_hdr *)fw->data; 50 phdrs = (struct elf32_phdr *)(ehdr + 1); 51 52 for (i = 0; i < ehdr->e_phnum; i++) { 53 phdr = &phdrs[i]; 54 55 if (!mdt_phdr_valid(phdr)) 56 continue; 57 58 if (phdr->p_paddr < min_addr) 59 min_addr = phdr->p_paddr; 60 61 if (phdr->p_paddr + phdr->p_memsz > max_addr) 62 max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); 63 } 64 65 return min_addr < max_addr ? max_addr - min_addr : -EINVAL; 66 } 67 EXPORT_SYMBOL_GPL(qcom_mdt_get_size); 68 69 /** 70 * qcom_mdt_read_metadata() - read header and metadata from mdt or mbn 71 * @fw: firmware of mdt header or mbn 72 * @data_len: length of the read metadata blob 73 * 74 * The mechanism that performs the authentication of the loading firmware 75 * expects an ELF header directly followed by the segment of hashes, with no 76 * padding inbetween. This function allocates a chunk of memory for this pair 77 * and copy the two pieces into the buffer. 78 * 79 * In the case of split firmware the hash is found directly following the ELF 80 * header, rather than at p_offset described by the second program header. 81 * 82 * The caller is responsible to free (kfree()) the returned pointer. 83 * 84 * Return: pointer to data, or ERR_PTR() 85 */ 86 void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len) 87 { 88 const struct elf32_phdr *phdrs; 89 const struct elf32_hdr *ehdr; 90 size_t hash_offset; 91 size_t hash_size; 92 size_t ehdr_size; 93 void *data; 94 95 ehdr = (struct elf32_hdr *)fw->data; 96 phdrs = (struct elf32_phdr *)(ehdr + 1); 97 98 if (ehdr->e_phnum < 2) 99 return ERR_PTR(-EINVAL); 100 101 if (phdrs[0].p_type == PT_LOAD || phdrs[1].p_type == PT_LOAD) 102 return ERR_PTR(-EINVAL); 103 104 if ((phdrs[1].p_flags & QCOM_MDT_TYPE_MASK) != QCOM_MDT_TYPE_HASH) 105 return ERR_PTR(-EINVAL); 106 107 ehdr_size = phdrs[0].p_filesz; 108 hash_size = phdrs[1].p_filesz; 109 110 data = kmalloc(ehdr_size + hash_size, GFP_KERNEL); 111 if (!data) 112 return ERR_PTR(-ENOMEM); 113 114 /* Is the header and hash already packed */ 115 if (ehdr_size + hash_size == fw->size) 116 hash_offset = phdrs[0].p_filesz; 117 else 118 hash_offset = phdrs[1].p_offset; 119 120 memcpy(data, fw->data, ehdr_size); 121 memcpy(data + ehdr_size, fw->data + hash_offset, hash_size); 122 123 *data_len = ehdr_size + hash_size; 124 125 return data; 126 } 127 EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); 128 129 static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, 130 const char *firmware, int pas_id, void *mem_region, 131 phys_addr_t mem_phys, size_t mem_size, 132 phys_addr_t *reloc_base, bool pas_init) 133 { 134 const struct elf32_phdr *phdrs; 135 const struct elf32_phdr *phdr; 136 const struct elf32_hdr *ehdr; 137 const struct firmware *seg_fw; 138 phys_addr_t mem_reloc; 139 phys_addr_t min_addr = PHYS_ADDR_MAX; 140 phys_addr_t max_addr = 0; 141 size_t metadata_len; 142 size_t fw_name_len; 143 ssize_t offset; 144 void *metadata; 145 char *fw_name; 146 bool relocate = false; 147 void *ptr; 148 int ret = 0; 149 int i; 150 151 if (!fw || !mem_region || !mem_phys || !mem_size) 152 return -EINVAL; 153 154 ehdr = (struct elf32_hdr *)fw->data; 155 phdrs = (struct elf32_phdr *)(ehdr + 1); 156 157 fw_name_len = strlen(firmware); 158 if (fw_name_len <= 4) 159 return -EINVAL; 160 161 fw_name = kstrdup(firmware, GFP_KERNEL); 162 if (!fw_name) 163 return -ENOMEM; 164 165 if (pas_init) { 166 metadata = qcom_mdt_read_metadata(fw, &metadata_len); 167 if (IS_ERR(metadata)) { 168 ret = PTR_ERR(metadata); 169 goto out; 170 } 171 172 ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len); 173 174 kfree(metadata); 175 if (ret) { 176 dev_err(dev, "invalid firmware metadata\n"); 177 goto out; 178 } 179 } 180 181 for (i = 0; i < ehdr->e_phnum; i++) { 182 phdr = &phdrs[i]; 183 184 if (!mdt_phdr_valid(phdr)) 185 continue; 186 187 if (phdr->p_flags & QCOM_MDT_RELOCATABLE) 188 relocate = true; 189 190 if (phdr->p_paddr < min_addr) 191 min_addr = phdr->p_paddr; 192 193 if (phdr->p_paddr + phdr->p_memsz > max_addr) 194 max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); 195 } 196 197 if (relocate) { 198 if (pas_init) { 199 ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, 200 max_addr - min_addr); 201 if (ret) { 202 dev_err(dev, "unable to setup relocation\n"); 203 goto out; 204 } 205 } 206 207 /* 208 * The image is relocatable, so offset each segment based on 209 * the lowest segment address. 210 */ 211 mem_reloc = min_addr; 212 } else { 213 /* 214 * Image is not relocatable, so offset each segment based on 215 * the allocated physical chunk of memory. 216 */ 217 mem_reloc = mem_phys; 218 } 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 offset = phdr->p_paddr - mem_reloc; 227 if (offset < 0 || offset + phdr->p_memsz > mem_size) { 228 dev_err(dev, "segment outside memory range\n"); 229 ret = -EINVAL; 230 break; 231 } 232 233 if (phdr->p_filesz > phdr->p_memsz) { 234 dev_err(dev, 235 "refusing to load segment %d with p_filesz > p_memsz\n", 236 i); 237 ret = -EINVAL; 238 break; 239 } 240 241 ptr = mem_region + offset; 242 243 if (phdr->p_filesz && phdr->p_offset < fw->size) { 244 /* Firmware is large enough to be non-split */ 245 if (phdr->p_offset + phdr->p_filesz > fw->size) { 246 dev_err(dev, 247 "failed to load segment %d from truncated file %s\n", 248 i, firmware); 249 ret = -EINVAL; 250 break; 251 } 252 253 memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz); 254 } else if (phdr->p_filesz) { 255 /* Firmware not large enough, load split-out segments */ 256 sprintf(fw_name + fw_name_len - 3, "b%02d", i); 257 ret = request_firmware_into_buf(&seg_fw, fw_name, dev, 258 ptr, phdr->p_filesz); 259 if (ret) { 260 dev_err(dev, "failed to load %s\n", fw_name); 261 break; 262 } 263 264 if (seg_fw->size != phdr->p_filesz) { 265 dev_err(dev, 266 "failed to load segment %d from truncated file %s\n", 267 i, fw_name); 268 release_firmware(seg_fw); 269 ret = -EINVAL; 270 break; 271 } 272 273 release_firmware(seg_fw); 274 } 275 276 if (phdr->p_memsz > phdr->p_filesz) 277 memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); 278 } 279 280 if (reloc_base) 281 *reloc_base = mem_reloc; 282 283 out: 284 kfree(fw_name); 285 286 return ret; 287 } 288 289 /** 290 * qcom_mdt_load() - load the firmware which header is loaded as fw 291 * @dev: device handle to associate resources with 292 * @fw: firmware object for the mdt file 293 * @firmware: name of the firmware, for construction of segment file names 294 * @pas_id: PAS identifier 295 * @mem_region: allocated memory region to load firmware into 296 * @mem_phys: physical address of allocated memory region 297 * @mem_size: size of the allocated memory region 298 * @reloc_base: adjusted physical address after relocation 299 * 300 * Returns 0 on success, negative errno otherwise. 301 */ 302 int qcom_mdt_load(struct device *dev, const struct firmware *fw, 303 const char *firmware, int pas_id, void *mem_region, 304 phys_addr_t mem_phys, size_t mem_size, 305 phys_addr_t *reloc_base) 306 { 307 return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, 308 mem_size, reloc_base, true); 309 } 310 EXPORT_SYMBOL_GPL(qcom_mdt_load); 311 312 /** 313 * qcom_mdt_load_no_init() - load the firmware which header is loaded as fw 314 * @dev: device handle to associate resources with 315 * @fw: firmware object for the mdt file 316 * @firmware: name of the firmware, for construction of segment file names 317 * @pas_id: PAS identifier 318 * @mem_region: allocated memory region to load firmware into 319 * @mem_phys: physical address of allocated memory region 320 * @mem_size: size of the allocated memory region 321 * @reloc_base: adjusted physical address after relocation 322 * 323 * Returns 0 on success, negative errno otherwise. 324 */ 325 int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, 326 const char *firmware, int pas_id, 327 void *mem_region, phys_addr_t mem_phys, 328 size_t mem_size, phys_addr_t *reloc_base) 329 { 330 return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, 331 mem_size, reloc_base, false); 332 } 333 EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); 334 335 MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); 336 MODULE_LICENSE("GPL v2"); 337