1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* Remote Processor Framework Elf loader
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Copyright (C) 2011 Google, Inc.
*
* Ohad Ben-Cohen <ohad@wizery.com>
* Brian Swetland <swetland@google.com>
* Mark Grosen <mgrosen@ti.com>
* Fernando Guzman Lugo <fernando.lugo@ti.com>
* Suman Anna <s-anna@ti.com>
* Robert Tivy <rtivy@ti.com>
* Armando Uribe De Leon <x0095078@ti.com>
* Sjur Brændeland <sjur.brandeland@stericsson.com>
*/
#include <common.h>
#include <elf.h>
#include <linux/remoteproc.h>
#include "remoteproc_internal.h"
int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
{
struct device_d *dev = &rproc->dev;
struct elf32_hdr *ehdr;
struct elf32_phdr *phdr;
int i, ret = 0;
const u8 *elf_data = fw->data;
ehdr = (struct elf32_hdr *)elf_data;
phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
/* go through the available ELF segments */
for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
u32 da = phdr->p_paddr;
u32 memsz = phdr->p_memsz;
u32 filesz = phdr->p_filesz;
u32 offset = phdr->p_offset;
void *ptr;
if (phdr->p_type != PT_LOAD)
continue;
dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
phdr->p_type, da, memsz, filesz);
if (filesz > memsz) {
dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
filesz, memsz);
ret = -EINVAL;
break;
}
if (offset + filesz > fw->size) {
dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
offset + filesz, fw->size);
ret = -EINVAL;
break;
}
/* grab the kernel address for this device address */
ptr = rproc_da_to_va(rproc, da, memsz);
if (!ptr) {
dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
ret = -EINVAL;
break;
}
/* put the segment where the remote processor expects it */
if (phdr->p_filesz)
memcpy(ptr, elf_data + phdr->p_offset, filesz);
/*
* Zero out remaining memory for this segment.
*
* This isn't strictly required since dma_alloc_coherent already
* did this for us. albeit harmless, we may consider removing
* this.
*/
if (memsz > filesz)
memset(ptr + filesz, 0, memsz - filesz);
}
return ret;
}
EXPORT_SYMBOL(rproc_elf_load_segments);
|