1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#define pr_fmt(fmt) "%s: " fmt, __func__
19
20#include <linux/module.h>
21#include <linux/firmware.h>
22#include <linux/remoteproc.h>
23#include <linux/elf.h>
24
25#include "remoteproc_internal.h"
26
27
28
29
30
31
32
33
34int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw)
35{
36 const char *name = rproc->firmware;
37 struct device *dev = &rproc->dev;
38 struct elf32_hdr *ehdr;
39 char class;
40
41 if (!fw) {
42 dev_err(dev, "failed to load %s\n", name);
43 return -EINVAL;
44 }
45
46 if (fw->size < sizeof(struct elf32_hdr)) {
47 dev_err(dev, "Image is too small\n");
48 return -EINVAL;
49 }
50
51 ehdr = (struct elf32_hdr *)fw->data;
52
53
54 class = ehdr->e_ident[EI_CLASS];
55 if (class != ELFCLASS32) {
56 dev_err(dev, "Unsupported class: %d\n", class);
57 return -EINVAL;
58 }
59
60
61# ifdef __LITTLE_ENDIAN
62 if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
63# else
64 if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
65# endif
66 dev_err(dev, "Unsupported firmware endianness\n");
67 return -EINVAL;
68 }
69
70 if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
71 dev_err(dev, "Image is too small\n");
72 return -EINVAL;
73 }
74
75 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
76 dev_err(dev, "Image is corrupted (bad magic)\n");
77 return -EINVAL;
78 }
79
80 if (ehdr->e_phnum == 0) {
81 dev_err(dev, "No loadable segments\n");
82 return -EINVAL;
83 }
84
85 if (ehdr->e_phoff > fw->size) {
86 dev_err(dev, "Firmware size is too small\n");
87 return -EINVAL;
88 }
89
90 return 0;
91}
92EXPORT_SYMBOL(rproc_elf_sanity_check);
93
94
95
96
97
98
99
100
101
102
103
104
105u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
106{
107 struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
108
109 return ehdr->e_entry;
110}
111EXPORT_SYMBOL(rproc_elf_get_boot_addr);
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
138{
139 struct device *dev = &rproc->dev;
140 struct elf32_hdr *ehdr;
141 struct elf32_phdr *phdr;
142 int i, ret = 0;
143 const u8 *elf_data = fw->data;
144
145 ehdr = (struct elf32_hdr *)elf_data;
146 phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
147
148
149 for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
150 u32 da = phdr->p_paddr;
151 u32 memsz = phdr->p_memsz;
152 u32 filesz = phdr->p_filesz;
153 u32 offset = phdr->p_offset;
154 void *ptr;
155
156 if (phdr->p_type != PT_LOAD)
157 continue;
158
159 dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
160 phdr->p_type, da, memsz, filesz);
161
162 if (filesz > memsz) {
163 dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
164 filesz, memsz);
165 ret = -EINVAL;
166 break;
167 }
168
169 if (offset + filesz > fw->size) {
170 dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
171 offset + filesz, fw->size);
172 ret = -EINVAL;
173 break;
174 }
175
176
177 ptr = rproc_da_to_va(rproc, da, memsz);
178 if (!ptr) {
179 dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
180 ret = -EINVAL;
181 break;
182 }
183
184
185 if (phdr->p_filesz)
186 memcpy(ptr, elf_data + phdr->p_offset, filesz);
187
188
189
190
191
192
193
194
195 if (memsz > filesz)
196 memset(ptr + filesz, 0, memsz - filesz);
197 }
198
199 return ret;
200}
201EXPORT_SYMBOL(rproc_elf_load_segments);
202
203static struct elf32_shdr *
204find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
205{
206 struct elf32_shdr *shdr;
207 int i;
208 const char *name_table;
209 struct resource_table *table = NULL;
210 const u8 *elf_data = (void *)ehdr;
211
212
213 shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
214 name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
215
216 for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
217 u32 size = shdr->sh_size;
218 u32 offset = shdr->sh_offset;
219
220 if (strcmp(name_table + shdr->sh_name, ".resource_table"))
221 continue;
222
223 table = (struct resource_table *)(elf_data + offset);
224
225
226 if (offset + size > fw_size || offset + size < size) {
227 dev_err(dev, "resource table truncated\n");
228 return NULL;
229 }
230
231
232 if (sizeof(struct resource_table) > size) {
233 dev_err(dev, "header-less resource table\n");
234 return NULL;
235 }
236
237
238 if (table->ver != 1) {
239 dev_err(dev, "unsupported fw ver: %d\n", table->ver);
240 return NULL;
241 }
242
243
244 if (table->reserved[0] || table->reserved[1]) {
245 dev_err(dev, "non zero reserved bytes\n");
246 return NULL;
247 }
248
249
250 if (struct_size(table, offset, table->num) > size) {
251 dev_err(dev, "resource table incomplete\n");
252 return NULL;
253 }
254
255 return shdr;
256 }
257
258 return NULL;
259}
260
261
262
263
264
265
266
267
268
269
270
271int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw)
272{
273 struct elf32_hdr *ehdr;
274 struct elf32_shdr *shdr;
275 struct device *dev = &rproc->dev;
276 struct resource_table *table = NULL;
277 const u8 *elf_data = fw->data;
278 size_t tablesz;
279
280 ehdr = (struct elf32_hdr *)elf_data;
281
282 shdr = find_table(dev, ehdr, fw->size);
283 if (!shdr)
284 return -EINVAL;
285
286 table = (struct resource_table *)(elf_data + shdr->sh_offset);
287 tablesz = shdr->sh_size;
288
289
290
291
292
293
294
295 rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL);
296 if (!rproc->cached_table)
297 return -ENOMEM;
298
299 rproc->table_ptr = rproc->cached_table;
300 rproc->table_sz = tablesz;
301
302 return 0;
303}
304EXPORT_SYMBOL(rproc_elf_load_rsc_table);
305
306
307
308
309
310
311
312
313
314
315
316
317struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
318 const struct firmware *fw)
319{
320 struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
321 struct elf32_shdr *shdr;
322
323 shdr = find_table(&rproc->dev, ehdr, fw->size);
324 if (!shdr)
325 return NULL;
326
327 return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size);
328}
329EXPORT_SYMBOL(rproc_elf_find_loaded_rsc_table);
330