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) 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 dev_err(dev, "error %d reading firmware %s metadata\n", 170 ret, fw_name); 171 goto out; 172 } 173 174 ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len); 175 176 kfree(metadata); 177 if (ret) { 178 /* Invalid firmware metadata */ 179 dev_err(dev, "error %d initializing firmware %s\n", 180 ret, fw_name); 181 goto out; 182 } 183 } 184 185 for (i = 0; i < ehdr->e_phnum; i++) { 186 phdr = &phdrs[i]; 187 188 if (!mdt_phdr_valid(phdr)) 189 continue; 190 191 if (phdr->p_flags & QCOM_MDT_RELOCATABLE) 192 relocate = true; 193 194 if (phdr->p_paddr < min_addr) 195 min_addr = phdr->p_paddr; 196 197 if (phdr->p_paddr + phdr->p_memsz > max_addr) 198 max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); 199 } 200 201 if (relocate) { 202 if (pas_init) { 203 ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, 204 max_addr - min_addr); 205 if (ret) { 206 /* Unable to set up relocation */ 207 dev_err(dev, "error %d setting up firmware %s\n", 208 ret, fw_name); 209 goto out; 210 } 211 } 212 213 /* 214 * The image is relocatable, so offset each segment based on 215 * the lowest segment address. 216 */ 217 mem_reloc = min_addr; 218 } else { 219 /* 220 * Image is not relocatable, so offset each segment based on 221 * the allocated physical chunk of memory. 222 */ 223 mem_reloc = mem_phys; 224 } 225 226 for (i = 0; i < ehdr->e_phnum; i++) { 227 phdr = &phdrs[i]; 228 229 if (!mdt_phdr_valid(phdr)) 230 continue; 231 232 offset = phdr->p_paddr - mem_reloc; 233 if (offset < 0 || offset + phdr->p_memsz > mem_size) { 234 dev_err(dev, "segment outside memory range\n"); 235 ret = -EINVAL; 236 break; 237 } 238 239 if (phdr->p_filesz > phdr->p_memsz) { 240 dev_err(dev, 241 "refusing to load segment %d with p_filesz > p_memsz\n", 242 i); 243 ret = -EINVAL; 244 break; 245 } 246 247 ptr = mem_region + offset; 248 249 if (phdr->p_filesz && phdr->p_offset < fw->size) { 250 /* Firmware is large enough to be non-split */ 251 if (phdr->p_offset + phdr->p_filesz > fw->size) { 252 dev_err(dev, "file %s segment %d would be truncated\n", 253 fw_name, i); 254 ret = -EINVAL; 255 break; 256 } 257 258 memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz); 259 } else if (phdr->p_filesz) { 260 /* Firmware not large enough, load split-out segments */ 261 sprintf(fw_name + fw_name_len - 3, "b%02d", i); 262 ret = request_firmware_into_buf(&seg_fw, fw_name, dev, 263 ptr, phdr->p_filesz); 264 if (ret) { 265 dev_err(dev, "error %d loading %s\n", 266 ret, fw_name); 267 break; 268 } 269 270 if (seg_fw->size != phdr->p_filesz) { 271 dev_err(dev, 272 "failed to load segment %d from truncated file %s\n", 273 i, fw_name); 274 release_firmware(seg_fw); 275 ret = -EINVAL; 276 break; 277 } 278 279 release_firmware(seg_fw); 280 } 281 282 if (phdr->p_memsz > phdr->p_filesz) 283 memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); 284 } 285 286 if (reloc_base) 287 *reloc_base = mem_reloc; 288 289 out: 290 kfree(fw_name); 291 292 return ret; 293 } 294 295 /** 296 * qcom_mdt_load() - load the firmware which header is loaded as fw 297 * @dev: device handle to associate resources with 298 * @fw: firmware object for the mdt file 299 * @firmware: name of the firmware, for construction of segment file names 300 * @pas_id: PAS identifier 301 * @mem_region: allocated memory region to load firmware into 302 * @mem_phys: physical address of allocated memory region 303 * @mem_size: size of the allocated memory region 304 * @reloc_base: adjusted physical address after relocation 305 * 306 * Returns 0 on success, negative errno otherwise. 307 */ 308 int qcom_mdt_load(struct device *dev, const struct firmware *fw, 309 const char *firmware, int pas_id, void *mem_region, 310 phys_addr_t mem_phys, size_t mem_size, 311 phys_addr_t *reloc_base) 312 { 313 return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, 314 mem_size, reloc_base, true); 315 } 316 EXPORT_SYMBOL_GPL(qcom_mdt_load); 317 318 /** 319 * qcom_mdt_load_no_init() - load the firmware which header is loaded as fw 320 * @dev: device handle to associate resources with 321 * @fw: firmware object for the mdt file 322 * @firmware: name of the firmware, for construction of segment file names 323 * @pas_id: PAS identifier 324 * @mem_region: allocated memory region to load firmware into 325 * @mem_phys: physical address of allocated memory region 326 * @mem_size: size of the allocated memory region 327 * @reloc_base: adjusted physical address after relocation 328 * 329 * Returns 0 on success, negative errno otherwise. 330 */ 331 int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, 332 const char *firmware, int pas_id, 333 void *mem_region, phys_addr_t mem_phys, 334 size_t mem_size, phys_addr_t *reloc_base) 335 { 336 return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, 337 mem_size, reloc_base, false); 338 } 339 EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); 340 341 MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); 342 MODULE_LICENSE("GPL v2"); 343