blob: 56d7c6d64676e7eedb22a40efe68c3797347ad48 [file] [log] [blame]
Aaron Durbinad935522012-12-24 14:28:37 -06001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2012 ChromeOS Authors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19#include <stdint.h>
20#include <string.h>
21#include <console/console.h>
22#include <rmodule.h>
23
24/* Change this define to get more verbose debugging for module loading. */
25#define PK_ADJ_LEVEL BIOS_NEVER
26
27#if CONFIG_ARCH_X86
28/*
29 * On X86, the only relocations currently allowed are R_386_RELATIVE which
30 * have '0' for the symbol info in the relocation metadata (in r_info).
31 * The reason is that the module is fully linked and just has the relocations'
32 * locations.
33 */
34typedef struct {
35 u32 r_offset;
36 u32 r_info;
37} Elf32_Rel;
38
39#define R_386_RELATIVE 8
40
41#define RELOCTION_ENTRY_SIZE sizeof(Elf32_Rel)
42static inline int rmodule_reloc_offset(const void *reloc)
43{
44 const Elf32_Rel *rel = reloc;
45 return rel->r_offset;
46}
47
48static inline int rmodule_reloc_valid(const void *reloc)
49{
50 const Elf32_Rel *rel = reloc;
51 return (rel->r_info == R_386_RELATIVE);
52}
53
54static inline void *remodule_next_reloc(const void *reloc)
55{
56 const Elf32_Rel *rel = reloc;
57 rel++;
58 return (void *)rel;
59}
60
61#else
62#error Arch needs to add relocation information support for RMODULE
63#endif
64
65static inline int rmodule_is_loaded(const struct rmodule *module)
66{
67 return module->location != NULL;
68}
69
70/* Calculate a loaded program address based on the blob address. */
71static inline void *rmodule_load_addr(const struct rmodule *module,
72 u32 blob_addr)
73{
74 char *loc = module->location;
75 return &loc[blob_addr - module->header->module_link_start_address];
76}
77
78/* Initialize a rmodule structure based on raw data. */
79int rmodule_parse(void *ptr, struct rmodule *module)
80{
81 char *base;
82 struct rmodule_header *rhdr;
83
84 base = ptr;
85 rhdr = ptr;
86
87 if (rhdr == NULL)
88 return -1;
89
90 /* Sanity check the raw data. */
91 if (rhdr->magic != RMODULE_MAGIC)
92 return -1;
93 if (rhdr->version != RMODULE_VERSION_1)
94 return -1;
95
96 /* Indicate the module hasn't been loaded yet. */
97 module->location = NULL;
98
99 /* The rmodule only needs a reference to the reloc_header. */
100 module->header = rhdr;
101
102 /* The payload lives after the header. */
103 module->payload = &base[rhdr->payload_begin_offset];
104 module->payload_size = rhdr->payload_end_offset -
105 rhdr->payload_begin_offset;
106 module->relocations = &base[rhdr->relocations_begin_offset];
107
108 return 0;
109}
110
111int rmodule_memory_size(const struct rmodule *module)
112{
113 return module->header->module_program_size;
114}
115
116void *rmodule_parameters(const struct rmodule *module)
117{
118 if (!rmodule_is_loaded(module))
119 return NULL;
120
121 /* Indicate if there are no parameters. */
122 if (module->header->parameters_begin == module->header->parameters_end)
123 return NULL;
124
125 return rmodule_load_addr(module, module->header->parameters_begin);
126}
127
128int rmodule_entry_offset(const struct rmodule *module)
129{
130 return module->header->module_entry_point -
131 module->header->module_link_start_address;
132}
133
134void *rmodule_entry(const struct rmodule *module)
135{
136 if (!rmodule_is_loaded(module))
137 return NULL;
138
139 return rmodule_load_addr(module, module->header->module_entry_point);
140}
141
142static void rmodule_clear_bss(struct rmodule *module)
143{
144 char *begin;
145 int size;
146
147 begin = rmodule_load_addr(module, module->header->bss_begin);
148 size = module->header->bss_end - module->header->bss_begin;
149 memset(begin, 0, size);
150}
151
152static inline int rmodule_number_relocations(const struct rmodule *module)
153{
154 int r;
155
156 r = module->header->relocations_end_offset;
157 r -= module->header->relocations_begin_offset;
158 r /= RELOCTION_ENTRY_SIZE;
159 return r;
160}
161
162static void rmodule_copy_payload(const struct rmodule *module)
163{
164 printk(BIOS_DEBUG, "Loading module at %p with entry %p. "
165 "filesize: 0x%x memsize: 0x%x\n",
166 module->location, rmodule_entry(module),
167 module->payload_size, rmodule_memory_size(module));
168 memcpy(module->location, module->payload, module->payload_size);
169}
170
171static inline u32 *rmodule_adjustment_location(const struct rmodule *module,
172 const void *reloc)
173{
174 int reloc_offset;
175
176 /* Don't relocate header field entries -- only program relocations. */
177 reloc_offset = rmodule_reloc_offset(reloc);
178 if (reloc_offset < module->header->module_link_start_address)
179 return NULL;
180
181 return rmodule_load_addr(module, reloc_offset);
182}
183
184static int rmodule_relocate(const struct rmodule *module)
185{
186 int num_relocations;
187 const void *reloc;
188 u32 adjustment;
189
190 /* Each relocation needs to be adjusted relative to the beginning of
191 * the loaded program. */
192 adjustment = (u32)rmodule_load_addr(module, 0);
193
194 reloc = module->relocations;
195 num_relocations = rmodule_number_relocations(module);
196
197 printk(BIOS_DEBUG, "Processing %d relocs with adjust value of 0x%08x\n",
198 num_relocations, adjustment);
199
200 while (num_relocations > 0) {
201 u32 *adjust_loc;
202
203 if (!rmodule_reloc_valid(reloc))
204 return -1;
205
206 /* If the adjustment location is non-NULL adjust it. */
207 adjust_loc = rmodule_adjustment_location(module, reloc);
208 if (adjust_loc != NULL) {
209 printk(PK_ADJ_LEVEL, "Adjusting %p: 0x%08x -> 0x%08x\n",
210 adjust_loc, *adjust_loc,
211 *adjust_loc + adjustment);
212 *adjust_loc += adjustment;
213 }
214
215 reloc = remodule_next_reloc(reloc);
216 num_relocations--;
217 }
218
219 return 0;
220}
221
222int rmodule_load_alignment(const struct rmodule *module)
223{
224 /* The load alignment is the start of the program's linked address.
225 * The base address where the program is loaded needs to be a multiple
226 * of the program's starting link address. That way all data alignment
227 * in the program is presered. */
228 return module->header->module_link_start_address;
229}
230
231int rmodule_load(void *base, struct rmodule *module)
232{
233 /*
234 * In order to load the module at a given address, the following steps
235 * take place:
236 * 1. Copy payload to base address.
237 * 2. Clear the bss segment.
238 * 3. Adjust relocations within the module to new base address.
239 */
240 module->location = base;
241 rmodule_copy_payload(module);
242 rmodule_clear_bss(module);
243 return rmodule_relocate(module);
244}
245