2009-06-11 Pavel Roskin <proski@gnu.org>
[grub2/bean.git] / efiemu / loadcore.c
blobee4c80649ebca409a29fe682650cf98839dab74e
1 /* Load runtime image of EFIemu. Functions specific to 32/64-bit mode */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2009 Free Software Foundation, Inc.
6 * GRUB 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, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/err.h>
21 #include <grub/mm.h>
22 #include <grub/misc.h>
23 #include <grub/efiemu/efiemu.h>
24 #include <grub/cpu/efiemu.h>
25 #include <grub/machine/efiemu.h>
26 #include <grub/elf.h>
28 /* ELF symbols and their values */
29 static struct grub_efiemu_elf_sym *grub_efiemu_elfsyms = 0;
30 static int grub_efiemu_nelfsyms = 0;
32 /* Return the address of a section whose index is N. */
33 static grub_err_t
34 grub_efiemu_get_section_addr (grub_efiemu_segment_t segs, unsigned n,
35 int *handle, grub_off_t *off)
37 grub_efiemu_segment_t seg;
39 for (seg = segs; seg; seg = seg->next)
40 if (seg->section == n)
42 *handle = seg->handle;
43 *off = seg->off;
44 return GRUB_ERR_NONE;
47 return grub_error (GRUB_ERR_BAD_OS, "section %d not found", n);
50 grub_err_t
51 SUFFIX (grub_efiemu_loadcore_unload) (void)
53 grub_free (grub_efiemu_elfsyms);
54 grub_efiemu_elfsyms = 0;
55 return GRUB_ERR_NONE;
58 /* Check if EHDR is a valid ELF header. */
59 int
60 SUFFIX (grub_efiemu_check_header) (void *ehdr, grub_size_t size)
62 Elf_Ehdr *e = ehdr;
64 /* Check the header size. */
65 if (size < sizeof (Elf_Ehdr))
66 return 0;
68 /* Check the magic numbers. */
69 if (!SUFFIX (grub_arch_efiemu_check_header) (ehdr)
70 || e->e_ident[EI_MAG0] != ELFMAG0
71 || e->e_ident[EI_MAG1] != ELFMAG1
72 || e->e_ident[EI_MAG2] != ELFMAG2
73 || e->e_ident[EI_MAG3] != ELFMAG3
74 || e->e_ident[EI_VERSION] != EV_CURRENT
75 || e->e_version != EV_CURRENT)
76 return 0;
78 return 1;
81 /* Load all segments from memory specified by E. */
82 static grub_err_t
83 grub_efiemu_load_segments (grub_efiemu_segment_t segs, const Elf_Ehdr *e)
85 Elf_Shdr *s;
86 grub_efiemu_segment_t cur;
88 grub_dprintf ("efiemu", "loading segments\n");
90 for (cur=segs; cur; cur = cur->next)
92 s = (Elf_Shdr *)cur->srcptr;
94 if ((s->sh_flags & SHF_ALLOC) && s->sh_size)
96 void *addr;
98 addr = (grub_uint8_t *) grub_efiemu_mm_obtain_request (cur->handle)
99 + cur->off;
101 switch (s->sh_type)
103 case SHT_PROGBITS:
104 grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
105 break;
106 case SHT_NOBITS:
107 grub_memset (addr, 0, s->sh_size);
108 break;
113 return GRUB_ERR_NONE;
116 /* Get a string at offset OFFSET from strtab */
117 static char *
118 grub_efiemu_get_string (unsigned offset, const Elf_Ehdr *e)
120 unsigned i;
121 Elf_Shdr *s;
123 for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
124 i < e->e_shnum;
125 i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
126 if (s->sh_type == SHT_STRTAB && offset < s->sh_size)
127 return (char *) e + s->sh_offset + offset;
128 return 0;
131 /* Request memory for segments and fill segments info */
132 static grub_err_t
133 grub_efiemu_init_segments (grub_efiemu_segment_t *segs, const Elf_Ehdr *e)
135 unsigned i;
136 Elf_Shdr *s;
138 for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
139 i < e->e_shnum;
140 i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
142 if (s->sh_flags & SHF_ALLOC)
144 grub_efiemu_segment_t seg;
145 seg = (grub_efiemu_segment_t) grub_malloc (sizeof (*seg));
146 if (! seg)
147 return grub_errno;
149 if (s->sh_size)
151 seg->handle
152 = grub_efiemu_request_memalign
153 (s->sh_addralign, s->sh_size,
154 s->sh_flags & SHF_EXECINSTR ? GRUB_EFI_RUNTIME_SERVICES_CODE
155 : GRUB_EFI_RUNTIME_SERVICES_DATA);
156 if (seg->handle < 0)
157 return grub_errno;
158 seg->off = 0;
162 .text-physical doesn't need to be relocated when switching to
163 virtual mode
165 if (!grub_strcmp (grub_efiemu_get_string (s->sh_name, e),
166 ".text-physical"))
167 seg->ptv_rel_needed = 0;
168 else
169 seg->ptv_rel_needed = 1;
170 seg->size = s->sh_size;
171 seg->section = i;
172 seg->next = *segs;
173 seg->srcptr = s;
174 *segs = seg;
178 return GRUB_ERR_NONE;
181 /* Count symbols and relocators and allocate/request memory for them */
182 static grub_err_t
183 grub_efiemu_count_symbols (const Elf_Ehdr *e)
185 unsigned i;
186 Elf_Shdr *s;
187 int num = 0;
189 /* Symbols */
190 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
191 i < e->e_shnum;
192 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
193 if (s->sh_type == SHT_SYMTAB)
194 break;
196 if (i == e->e_shnum)
197 return grub_error (GRUB_ERR_BAD_OS, "no symbol table");
199 grub_efiemu_nelfsyms = (unsigned) s->sh_size / (unsigned) s->sh_entsize;
200 grub_efiemu_elfsyms = (struct grub_efiemu_elf_sym *)
201 grub_malloc (sizeof (struct grub_efiemu_elf_sym) * grub_efiemu_nelfsyms);
203 /* Relocators */
204 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
205 i < e->e_shnum;
206 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
207 if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA)
208 num += ((unsigned) s->sh_size) / ((unsigned) s->sh_entsize);
210 grub_efiemu_request_symbols (num);
212 return GRUB_ERR_NONE;
215 /* Fill grub_efiemu_elfsyms with symbol values */
216 static grub_err_t
217 grub_efiemu_resolve_symbols (grub_efiemu_segment_t segs, Elf_Ehdr *e)
219 unsigned i;
220 Elf_Shdr *s;
221 Elf_Sym *sym;
222 const char *str;
223 Elf_Word size, entsize;
225 grub_dprintf ("efiemu", "resolving symbols\n");
227 for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
228 i < e->e_shnum;
229 i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
230 if (s->sh_type == SHT_SYMTAB)
231 break;
233 if (i == e->e_shnum)
234 return grub_error (GRUB_ERR_BAD_OS, "no symbol table");
236 sym = (Elf_Sym *) ((char *) e + s->sh_offset);
237 size = s->sh_size;
238 entsize = s->sh_entsize;
240 s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
241 str = (char *) e + s->sh_offset;
243 for (i = 0;
244 i < size / entsize;
245 i++, sym = (Elf_Sym *) ((char *) sym + entsize))
247 unsigned char type = ELF_ST_TYPE (sym->st_info);
248 unsigned char bind = ELF_ST_BIND (sym->st_info);
249 int handle;
250 grub_off_t off;
251 grub_err_t err;
252 const char *name = str + sym->st_name;
253 grub_efiemu_elfsyms[i].section = sym->st_shndx;
254 switch (type)
256 case STT_NOTYPE:
257 /* Resolve a global symbol. */
258 if (sym->st_name != 0 && sym->st_shndx == 0)
260 if ((err = grub_efiemu_resolve_symbol (name, &handle, &off)))
261 return err;
262 grub_efiemu_elfsyms[i].handle = handle;
263 grub_efiemu_elfsyms[i].off = off;
265 else
266 sym->st_value = 0;
267 break;
269 case STT_OBJECT:
270 if ((err = grub_efiemu_get_section_addr
271 (segs, sym->st_shndx, &handle, &off)))
272 return err;
274 off += sym->st_value;
275 if (bind != STB_LOCAL)
276 if ((err = grub_efiemu_register_symbol (name, handle, off)))
277 return err;
278 grub_efiemu_elfsyms[i].handle = handle;
279 grub_efiemu_elfsyms[i].off = off;
280 break;
282 case STT_FUNC:
283 if ((err = grub_efiemu_get_section_addr
284 (segs, sym->st_shndx, &handle, &off)))
285 return err;
287 off += sym->st_value;
288 if (bind != STB_LOCAL)
289 if ((err = grub_efiemu_register_symbol (name, handle, off)))
290 return err;
291 grub_efiemu_elfsyms[i].handle = handle;
292 grub_efiemu_elfsyms[i].off = off;
293 break;
295 case STT_SECTION:
296 if ((err = grub_efiemu_get_section_addr
297 (segs, sym->st_shndx, &handle, &off)))
299 grub_efiemu_elfsyms[i].handle = 0;
300 grub_efiemu_elfsyms[i].off = 0;
301 grub_errno = GRUB_ERR_NONE;
302 break;
305 grub_efiemu_elfsyms[i].handle = handle;
306 grub_efiemu_elfsyms[i].off = off;
307 break;
309 case STT_FILE:
310 grub_efiemu_elfsyms[i].handle = 0;
311 grub_efiemu_elfsyms[i].off = 0;
312 break;
314 default:
315 return grub_error (GRUB_ERR_BAD_MODULE,
316 "unknown symbol type `%d'", (int) type);
320 return GRUB_ERR_NONE;
323 /* Load runtime to the memory and request memory for definitive location*/
324 grub_err_t
325 SUFFIX (grub_efiemu_loadcore_init) (void *core, grub_size_t core_size,
326 grub_efiemu_segment_t *segments)
328 Elf_Ehdr *e = (Elf_Ehdr *) core;
329 grub_err_t err;
331 if (e->e_type != ET_REL)
332 return grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type");
334 /* Make sure that every section is within the core. */
335 if ((grub_size_t) core_size < e->e_shoff + e->e_shentsize * e->e_shnum)
336 return grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
338 if ((err = grub_efiemu_init_segments (segments, core)))
339 return err;
340 if ((err = grub_efiemu_count_symbols (core)))
341 return err;
343 grub_efiemu_request_symbols (1);
344 return GRUB_ERR_NONE;
347 /* Load runtime definitively */
348 grub_err_t
349 SUFFIX (grub_efiemu_loadcore_load) (void *core,
350 grub_size_t core_size
351 __attribute__ ((unused)),
352 grub_efiemu_segment_t segments)
354 grub_err_t err;
355 if ((err = grub_efiemu_load_segments (segments, core)))
356 return err;
357 if ((err = grub_efiemu_resolve_symbols (segments, core)))
358 return err;
359 if ((err = SUFFIX (grub_arch_efiemu_relocate_symbols) (segments,
360 grub_efiemu_elfsyms,
361 core)))
362 return err;
364 return GRUB_ERR_NONE;