aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/soc/qcom/mdt_loader.c
diff options
context:
space:
mode:
authorBjorn Andersson <bjorn.andersson@linaro.org>2019-06-21 21:21:45 -0400
committerBjorn Andersson <bjorn.andersson@linaro.org>2019-07-01 22:02:28 -0400
commit498b98e939007f8bb65094dfa229e84b6bf30e62 (patch)
tree2c27da1adf91c0fd774b6ac42d47da6f0602985a /drivers/soc/qcom/mdt_loader.c
parent13140de09cc2dd5e5166ad42292bb82af4e23cef (diff)
soc: qcom: mdt_loader: Support loading non-split images
In some software releases the firmware images are not split up with each loadable segment in it's own file. Check the size of the loaded firmware to see if it still contains each segment to be loaded, before falling back to the split-out segments. Acked-by: Andy Gross <agross@kernel.org> Reviewed-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Diffstat (limited to 'drivers/soc/qcom/mdt_loader.c')
-rw-r--r--drivers/soc/qcom/mdt_loader.c88
1 files changed, 85 insertions, 3 deletions
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c
index 1c488024c698..1c970d6dd532 100644
--- a/drivers/soc/qcom/mdt_loader.c
+++ b/drivers/soc/qcom/mdt_loader.c
@@ -74,6 +74,66 @@ ssize_t qcom_mdt_get_size(const struct firmware *fw)
74} 74}
75EXPORT_SYMBOL_GPL(qcom_mdt_get_size); 75EXPORT_SYMBOL_GPL(qcom_mdt_get_size);
76 76
77/**
78 * qcom_mdt_read_metadata() - read header and metadata from mdt or mbn
79 * @fw: firmware of mdt header or mbn
80 * @data_len: length of the read metadata blob
81 *
82 * The mechanism that performs the authentication of the loading firmware
83 * expects an ELF header directly followed by the segment of hashes, with no
84 * padding inbetween. This function allocates a chunk of memory for this pair
85 * and copy the two pieces into the buffer.
86 *
87 * In the case of split firmware the hash is found directly following the ELF
88 * header, rather than at p_offset described by the second program header.
89 *
90 * The caller is responsible to free (kfree()) the returned pointer.
91 *
92 * Return: pointer to data, or ERR_PTR()
93 */
94void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len)
95{
96 const struct elf32_phdr *phdrs;
97 const struct elf32_hdr *ehdr;
98 size_t hash_offset;
99 size_t hash_size;
100 size_t ehdr_size;
101 void *data;
102
103 ehdr = (struct elf32_hdr *)fw->data;
104 phdrs = (struct elf32_phdr *)(ehdr + 1);
105
106 if (ehdr->e_phnum < 2)
107 return ERR_PTR(-EINVAL);
108
109 if (phdrs[0].p_type == PT_LOAD || phdrs[1].p_type == PT_LOAD)
110 return ERR_PTR(-EINVAL);
111
112 if ((phdrs[1].p_flags & QCOM_MDT_TYPE_MASK) != QCOM_MDT_TYPE_HASH)
113 return ERR_PTR(-EINVAL);
114
115 ehdr_size = phdrs[0].p_filesz;
116 hash_size = phdrs[1].p_filesz;
117
118 data = kmalloc(ehdr_size + hash_size, GFP_KERNEL);
119 if (!data)
120 return ERR_PTR(-ENOMEM);
121
122 /* Is the header and hash already packed */
123 if (ehdr_size + hash_size == fw->size)
124 hash_offset = phdrs[0].p_filesz;
125 else
126 hash_offset = phdrs[1].p_offset;
127
128 memcpy(data, fw->data, ehdr_size);
129 memcpy(data + ehdr_size, fw->data + hash_offset, hash_size);
130
131 *data_len = ehdr_size + hash_size;
132
133 return data;
134}
135EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata);
136
77static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, 137static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
78 const char *firmware, int pas_id, void *mem_region, 138 const char *firmware, int pas_id, void *mem_region,
79 phys_addr_t mem_phys, size_t mem_size, 139 phys_addr_t mem_phys, size_t mem_size,
@@ -86,12 +146,14 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
86 phys_addr_t mem_reloc; 146 phys_addr_t mem_reloc;
87 phys_addr_t min_addr = PHYS_ADDR_MAX; 147 phys_addr_t min_addr = PHYS_ADDR_MAX;
88 phys_addr_t max_addr = 0; 148 phys_addr_t max_addr = 0;
149 size_t metadata_len;
89 size_t fw_name_len; 150 size_t fw_name_len;
90 ssize_t offset; 151 ssize_t offset;
152 void *metadata;
91 char *fw_name; 153 char *fw_name;
92 bool relocate = false; 154 bool relocate = false;
93 void *ptr; 155 void *ptr;
94 int ret; 156 int ret = 0;
95 int i; 157 int i;
96 158
97 if (!fw || !mem_region || !mem_phys || !mem_size) 159 if (!fw || !mem_region || !mem_phys || !mem_size)
@@ -109,7 +171,15 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
109 return -ENOMEM; 171 return -ENOMEM;
110 172
111 if (pas_init) { 173 if (pas_init) {
112 ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); 174 metadata = qcom_mdt_read_metadata(fw, &metadata_len);
175 if (IS_ERR(metadata)) {
176 ret = PTR_ERR(metadata);
177 goto out;
178 }
179
180 ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len);
181
182 kfree(metadata);
113 if (ret) { 183 if (ret) {
114 dev_err(dev, "invalid firmware metadata\n"); 184 dev_err(dev, "invalid firmware metadata\n");
115 goto out; 185 goto out;
@@ -170,7 +240,19 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
170 240
171 ptr = mem_region + offset; 241 ptr = mem_region + offset;
172 242
173 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 */
174 sprintf(fw_name + fw_name_len - 3, "b%02d", i); 256 sprintf(fw_name + fw_name_len - 3, "b%02d", i);
175 ret = request_firmware_into_buf(&seg_fw, fw_name, dev, 257 ret = request_firmware_into_buf(&seg_fw, fw_name, dev,
176 ptr, phdr->p_filesz); 258 ptr, phdr->p_filesz);