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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
|
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2012 ChromeOS Authors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <console/console.h>
#include <rmodule.h>
/* Change this define to get more verbose debugging for module loading. */
#define PK_ADJ_LEVEL BIOS_NEVER
#if CONFIG_ARCH_X86
/*
* On X86, the only relocations currently allowed are R_386_RELATIVE which
* have '0' for the symbol info in the relocation metadata (in r_info).
* The reason is that the module is fully linked and just has the relocations'
* locations.
*/
typedef struct {
u32 r_offset;
u32 r_info;
} Elf32_Rel;
#define R_386_RELATIVE 8
#define RELOCTION_ENTRY_SIZE sizeof(Elf32_Rel)
static inline int rmodule_reloc_offset(const void *reloc)
{
const Elf32_Rel *rel = reloc;
return rel->r_offset;
}
static inline int rmodule_reloc_valid(const void *reloc)
{
const Elf32_Rel *rel = reloc;
return (rel->r_info == R_386_RELATIVE);
}
static inline void *remodule_next_reloc(const void *reloc)
{
const Elf32_Rel *rel = reloc;
rel++;
return (void *)rel;
}
#else
#error Arch needs to add relocation information support for RMODULE
#endif
static inline int rmodule_is_loaded(const struct rmodule *module)
{
return module->location != NULL;
}
/* Calculate a loaded program address based on the blob address. */
static inline void *rmodule_load_addr(const struct rmodule *module,
u32 blob_addr)
{
char *loc = module->location;
return &loc[blob_addr - module->header->module_link_start_address];
}
/* Initialize a rmodule structure based on raw data. */
int rmodule_parse(void *ptr, struct rmodule *module)
{
char *base;
struct rmodule_header *rhdr;
base = ptr;
rhdr = ptr;
if (rhdr == NULL)
return -1;
/* Sanity check the raw data. */
if (rhdr->magic != RMODULE_MAGIC)
return -1;
if (rhdr->version != RMODULE_VERSION_1)
return -1;
/* Indicate the module hasn't been loaded yet. */
module->location = NULL;
/* The rmodule only needs a reference to the reloc_header. */
module->header = rhdr;
/* The payload lives after the header. */
module->payload = &base[rhdr->payload_begin_offset];
module->payload_size = rhdr->payload_end_offset -
rhdr->payload_begin_offset;
module->relocations = &base[rhdr->relocations_begin_offset];
return 0;
}
int rmodule_memory_size(const struct rmodule *module)
{
return module->header->module_program_size;
}
void *rmodule_parameters(const struct rmodule *module)
{
if (!rmodule_is_loaded(module))
return NULL;
/* Indicate if there are no parameters. */
if (module->header->parameters_begin == module->header->parameters_end)
return NULL;
return rmodule_load_addr(module, module->header->parameters_begin);
}
int rmodule_entry_offset(const struct rmodule *module)
{
return module->header->module_entry_point -
module->header->module_link_start_address;
}
void *rmodule_entry(const struct rmodule *module)
{
if (!rmodule_is_loaded(module))
return NULL;
return rmodule_load_addr(module, module->header->module_entry_point);
}
static void rmodule_clear_bss(struct rmodule *module)
{
char *begin;
int size;
begin = rmodule_load_addr(module, module->header->bss_begin);
size = module->header->bss_end - module->header->bss_begin;
memset(begin, 0, size);
}
static inline int rmodule_number_relocations(const struct rmodule *module)
{
int r;
r = module->header->relocations_end_offset;
r -= module->header->relocations_begin_offset;
r /= RELOCTION_ENTRY_SIZE;
return r;
}
static void rmodule_copy_payload(const struct rmodule *module)
{
printk(BIOS_DEBUG, "Loading module at %p with entry %p. "
"filesize: 0x%x memsize: 0x%x\n",
module->location, rmodule_entry(module),
module->payload_size, rmodule_memory_size(module));
/* No need to copy the payload if the load location and the
* payload location are the same. */
if (module->location == module->payload)
return;
memcpy(module->location, module->payload, module->payload_size);
}
static inline u32 *rmodule_adjustment_location(const struct rmodule *module,
const void *reloc)
{
int reloc_offset;
/* Don't relocate header field entries -- only program relocations. */
reloc_offset = rmodule_reloc_offset(reloc);
if (reloc_offset < module->header->module_link_start_address)
return NULL;
return rmodule_load_addr(module, reloc_offset);
}
static int rmodule_relocate(const struct rmodule *module)
{
int num_relocations;
const void *reloc;
u32 adjustment;
/* Each relocation needs to be adjusted relative to the beginning of
* the loaded program. */
adjustment = (u32)rmodule_load_addr(module, 0);
reloc = module->relocations;
num_relocations = rmodule_number_relocations(module);
printk(BIOS_DEBUG, "Processing %d relocs with adjust value of 0x%08x\n",
num_relocations, adjustment);
while (num_relocations > 0) {
u32 *adjust_loc;
if (!rmodule_reloc_valid(reloc))
return -1;
/* If the adjustment location is non-NULL adjust it. */
adjust_loc = rmodule_adjustment_location(module, reloc);
if (adjust_loc != NULL) {
printk(PK_ADJ_LEVEL, "Adjusting %p: 0x%08x -> 0x%08x\n",
adjust_loc, *adjust_loc,
*adjust_loc + adjustment);
*adjust_loc += adjustment;
}
reloc = remodule_next_reloc(reloc);
num_relocations--;
}
return 0;
}
int rmodule_load_alignment(const struct rmodule *module)
{
/* The load alignment is the start of the program's linked address.
* The base address where the program is loaded needs to be a multiple
* of the program's starting link address. That way all data alignment
* in the program is presered. */
return module->header->module_link_start_address;
}
int rmodule_load(void *base, struct rmodule *module)
{
/*
* In order to load the module at a given address, the following steps
* take place:
* 1. Copy payload to base address.
* 2. Clear the bss segment.
* 3. Adjust relocations within the module to new base address.
*/
module->location = base;
rmodule_copy_payload(module);
rmodule_clear_bss(module);
return rmodule_relocate(module);
}
void *rmodule_find_region_below(void *addr, size_t rmodule_size,
void **program_start, void **rmodule_start)
{
unsigned long ceiling;
unsigned long program_base;
unsigned long placement_loc;
unsigned long program_begin;
ceiling = (unsigned long)addr;
/* Place the rmodule just under the ceiling. The rmodule files
* themselves are packed as a header and a payload, however the rmodule
* itself is linked along with the header. The header starts at address
* 0. Immediately following the header in the file is the program,
* however its starting address is determined by the rmodule linker
* script. In short, sizeof(struct rmodule_header) can be less than
* or equal to the linked address of the program. Therefore we want
* to place the rmodule so that the program falls on the aligned
* address with the header just before it. Therefore, we need at least
* a page to account for the size of the header. */
program_base = ALIGN((ceiling - (rmodule_size + 4096)), 4096);
/* The program starts immediately after the header. However,
* it needs to be aligned to a 4KiB boundary. Therefore, adjust the
* program location so that the program lands on a page boundary. The
* layout looks like the following:
*
* +--------------------------------+ ceiling
* | >= 0 bytes from alignment |
* +--------------------------------+ program end (4KiB aligned)
* | program size |
* +--------------------------------+ program_begin (4KiB aligned)
* | sizeof(struct rmodule_header) |
* +--------------------------------+ rmodule header start
* | >= 0 bytes from alignment |
* +--------------------------------+ program_base (4KiB aligned)
*/
placement_loc = ALIGN(program_base + sizeof(struct rmodule_header),
4096) - sizeof(struct rmodule_header);
program_begin = placement_loc + sizeof(struct rmodule_header);
*program_start = (void *)program_begin;
*rmodule_start = (void *)placement_loc;
return (void *)program_base;
}
|