.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Qualcomm Peripheral Image Loader |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2016 Linaro Ltd |
---|
5 | 6 | * Copyright (C) 2015 Sony Mobile Communications Inc |
---|
6 | 7 | * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or |
---|
9 | | - * modify it under the terms of the GNU General Public License |
---|
10 | | - * version 2 as published by the Free Software Foundation. |
---|
11 | | - * |
---|
12 | | - * This program is distributed in the hope that it will be useful, |
---|
13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
15 | | - * GNU General Public License for more details. |
---|
16 | 8 | */ |
---|
17 | 9 | |
---|
18 | 10 | #include <linux/device.h> |
---|
.. | .. |
---|
74 | 66 | } |
---|
75 | 67 | EXPORT_SYMBOL_GPL(qcom_mdt_get_size); |
---|
76 | 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 | + |
---|
77 | 129 | static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, |
---|
78 | 130 | const char *firmware, int pas_id, void *mem_region, |
---|
79 | 131 | phys_addr_t mem_phys, size_t mem_size, |
---|
.. | .. |
---|
86 | 138 | phys_addr_t mem_reloc; |
---|
87 | 139 | phys_addr_t min_addr = PHYS_ADDR_MAX; |
---|
88 | 140 | phys_addr_t max_addr = 0; |
---|
| 141 | + size_t metadata_len; |
---|
89 | 142 | size_t fw_name_len; |
---|
90 | 143 | ssize_t offset; |
---|
| 144 | + void *metadata; |
---|
91 | 145 | char *fw_name; |
---|
92 | 146 | bool relocate = false; |
---|
93 | 147 | void *ptr; |
---|
94 | | - int ret; |
---|
| 148 | + int ret = 0; |
---|
95 | 149 | int i; |
---|
96 | 150 | |
---|
97 | 151 | if (!fw || !mem_region || !mem_phys || !mem_size) |
---|
.. | .. |
---|
109 | 163 | return -ENOMEM; |
---|
110 | 164 | |
---|
111 | 165 | if (pas_init) { |
---|
112 | | - ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); |
---|
| 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); |
---|
113 | 175 | if (ret) { |
---|
114 | 176 | dev_err(dev, "invalid firmware metadata\n"); |
---|
115 | 177 | goto out; |
---|
.. | .. |
---|
178 | 240 | |
---|
179 | 241 | ptr = mem_region + offset; |
---|
180 | 242 | |
---|
181 | | - if (phdr->p_filesz) { |
---|
| 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 */ |
---|
182 | 256 | sprintf(fw_name + fw_name_len - 3, "b%02d", i); |
---|
183 | 257 | ret = request_firmware_into_buf(&seg_fw, fw_name, dev, |
---|
184 | 258 | ptr, phdr->p_filesz); |
---|