Fix various bugs in *bsd. Freebsd64 on relocators

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-01-12 17:50:11 +01:00
parent 14933205d1
commit 73910decff
4 changed files with 73 additions and 82 deletions

View file

@ -263,9 +263,11 @@ grub_err_t grub_freebsd_load_elfmodule_obj64 (struct grub_relocator *relocator,
grub_file_t file, int argc, grub_file_t file, int argc,
char *argv[], char *argv[],
grub_addr_t *kern_end); grub_addr_t *kern_end);
grub_err_t grub_freebsd_load_elf_meta32 (grub_file_t file, grub_err_t grub_freebsd_load_elf_meta32 (struct grub_relocator *relocator,
grub_file_t file,
grub_addr_t *kern_end); grub_addr_t *kern_end);
grub_err_t grub_freebsd_load_elf_meta64 (grub_file_t file, grub_err_t grub_freebsd_load_elf_meta64 (struct grub_relocator *relocator,
grub_file_t file,
grub_addr_t *kern_end); grub_addr_t *kern_end);
grub_err_t grub_freebsd_add_meta (grub_uint32_t type, void *data, grub_err_t grub_freebsd_add_meta (grub_uint32_t type, void *data,

View file

@ -436,12 +436,6 @@ grub_freebsd_list_modules (void)
/* This function would be here but it's under different license. */ /* This function would be here but it's under different license. */
#include "bsd_pagetable.c" #include "bsd_pagetable.c"
struct gdt_descriptor
{
grub_uint16_t limit;
void *base;
} __attribute__ ((packed));
static grub_err_t static grub_err_t
grub_freebsd_boot (void) grub_freebsd_boot (void)
{ {
@ -499,15 +493,15 @@ grub_freebsd_boot (void)
p_size = ALIGN_PAGE (kern_end + p_size + mod_buf_len) - kern_end; p_size = ALIGN_PAGE (kern_end + p_size + mod_buf_len) - kern_end;
if (is_64bit) if (is_64bit)
p_size += 4096 * 4; p_size += 4096 * 3;
err = grub_relocator_alloc_chunk_addr (relocator, (void **) &p, err = grub_relocator_alloc_chunk_addr (relocator, (void **) &p,
kern_end, p_size); kern_end, p_size);
if (err) if (err)
return err; return err;
kern_end += p_size;
p0 = p;
p_target = kern_end; p_target = kern_end;
p0 = p;
kern_end += p_size;
grub_env_iterate (iterate_env); grub_env_iterate (iterate_env);
@ -547,49 +541,30 @@ grub_freebsd_boot (void)
if (is_64bit) if (is_64bit)
{ {
grub_uint32_t *gdt; struct grub_relocator64_state state;
grub_uint8_t *trampoline;
void (*launch_trampoline) (grub_addr_t entry_lo, ...)
__attribute__ ((cdecl, regparm (0)));
grub_uint8_t *pagetable; grub_uint8_t *pagetable;
grub_uint32_t *stack;
grub_addr_t stack_target;
struct gdt_descriptor *gdtdesc; err = grub_relocator_alloc_chunk_align (relocator, (void **) &stack,
&stack_target,
0x10000, 0x90000,
3 * sizeof (grub_uint32_t)
+ sizeof (bi), 4);
if (err)
return err;
pagetable = p - 16384; pagetable = p - (4096 * 3);
fill_bsd64_pagetable (pagetable); fill_bsd64_pagetable (pagetable, (pagetable - p0) + p_target);
/* Create GDT. */ state.cr3 = (pagetable - p0) + p_target;
gdt = (grub_uint32_t *) (p - 4096); state.rsp = stack_target;
gdt[0] = 0; state.rip = (((grub_uint64_t) entry_hi) << 32) | entry;
gdt[1] = 0;
gdt[2] = 0;
gdt[3] = 0x00209800;
gdt[4] = 0;
gdt[5] = 0x00008000;
/* Create GDT descriptor. */ stack[0] = entry;
gdtdesc = (struct gdt_descriptor *) (p - 4096 + 24); stack[1] = bi.bi_modulep;
gdtdesc->limit = 24; stack[2] = kern_end;
gdtdesc->base = gdt; return grub_relocator64_boot (relocator, state, 0, 0x40000000);
/* Prepare trampoline. */
trampoline = (grub_uint8_t *) (p - 4096 + 24
+ sizeof (struct gdt_descriptor));
launch_trampoline = (void __attribute__ ((cdecl, regparm (0)))
(*) (grub_addr_t entry_lo, ...)) trampoline;
grub_bsd64_trampoline_gdt = (grub_uint32_t) gdtdesc;
grub_bsd64_trampoline_selfjump
= (grub_uint32_t) (trampoline + 6
+ ((grub_uint8_t *) &grub_bsd64_trampoline_selfjump
- &grub_bsd64_trampoline_start));
/* Copy trampoline. */
grub_memcpy (trampoline, &grub_bsd64_trampoline_start,
&grub_bsd64_trampoline_end - &grub_bsd64_trampoline_start);
/* Launch trampoline. */
launch_trampoline (entry, entry_hi, pagetable, bi.bi_modulep,
kern_end);
} }
else else
{ {
@ -936,7 +911,7 @@ grub_bsd_elf32_size_hook (grub_elf_t elf __attribute__ ((unused)),
if (phdr->p_type != PT_LOAD if (phdr->p_type != PT_LOAD
&& phdr->p_type != PT_DYNAMIC) && phdr->p_type != PT_DYNAMIC)
return 1; return 0;
paddr = phdr->p_paddr & 0xFFFFFF; paddr = phdr->p_paddr & 0xFFFFFF;
@ -946,7 +921,7 @@ grub_bsd_elf32_size_hook (grub_elf_t elf __attribute__ ((unused)),
if (paddr + phdr->p_memsz > kern_end) if (paddr + phdr->p_memsz > kern_end)
kern_end = paddr + phdr->p_memsz; kern_end = paddr + phdr->p_memsz;
return 1; return 0;
} }
static grub_err_t static grub_err_t
@ -978,7 +953,7 @@ grub_bsd_elf64_size_hook (grub_elf_t elf __attribute__ ((unused)),
if (phdr->p_type != PT_LOAD if (phdr->p_type != PT_LOAD
&& phdr->p_type != PT_DYNAMIC) && phdr->p_type != PT_DYNAMIC)
return 1; return 0;
paddr = phdr->p_paddr & 0xffffff; paddr = phdr->p_paddr & 0xffffff;
@ -988,7 +963,7 @@ grub_bsd_elf64_size_hook (grub_elf_t elf __attribute__ ((unused)),
if (paddr + phdr->p_memsz > kern_end) if (paddr + phdr->p_memsz > kern_end)
kern_end = paddr + phdr->p_memsz; kern_end = paddr + phdr->p_memsz;
return 1; return 0;
} }
static grub_err_t static grub_err_t
@ -1054,6 +1029,9 @@ grub_bsd_load_elf (grub_elf_t elf)
err = grub_elf64_phdr_iterate (elf, grub_bsd_elf64_size_hook, NULL); err = grub_elf64_phdr_iterate (elf, grub_bsd_elf64_size_hook, NULL);
if (err) if (err)
return err; return err;
grub_dprintf ("bsd", "kern_start = %x, kern_end = %x\n", kern_start,
kern_end);
err = grub_relocator_alloc_chunk_addr (relocator, &kern_chunk_src, err = grub_relocator_alloc_chunk_addr (relocator, &kern_chunk_src,
kern_start, kern_end - kern_start); kern_start, kern_end - kern_start);
if (err) if (err)
@ -1154,9 +1132,9 @@ grub_cmd_freebsd (grub_extcmd_t cmd, int argc, char *argv[])
return grub_errno; return grub_errno;
if (is_64bit) if (is_64bit)
err = grub_freebsd_load_elf_meta64 (file, &kern_end); err = grub_freebsd_load_elf_meta64 (relocator, file, &kern_end);
else else
err = grub_freebsd_load_elf_meta32 (file, &kern_end); err = grub_freebsd_load_elf_meta32 (relocator, file, &kern_end);
if (err) if (err)
return err; return err;

View file

@ -249,24 +249,28 @@ SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator,
grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE, grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE,
argc - 1, argv + 1, module, argc - 1, argv + 1, module,
curload - module); curload - module);
return SUFFIX (grub_freebsd_load_elf_meta) (file, kern_end); return SUFFIX (grub_freebsd_load_elf_meta) (relocator, file, kern_end);
} }
#endif #endif
grub_err_t grub_err_t
SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end) SUFFIX (grub_freebsd_load_elf_meta) (struct grub_relocator *relocator,
grub_file_t file, grub_addr_t *kern_end)
{ {
grub_err_t err; grub_err_t err;
Elf_Ehdr e; Elf_Ehdr e;
Elf_Shdr *s; Elf_Shdr *s;
char *shdr; char *shdr;
unsigned symoff, stroff, symsize, strsize; unsigned symoff, stroff, symsize, strsize;
grub_addr_t curload;
grub_freebsd_addr_t symstart, symend, symentsize, dynamic; grub_freebsd_addr_t symstart, symend, symentsize, dynamic;
Elf_Sym *sym; Elf_Sym *sym;
void *sym_chunk;
grub_uint8_t *curload;
grub_freebsd_addr_t symtarget;
const char *str; const char *str;
unsigned i; unsigned i;
grub_size_t chunk_size;
err = read_headers (file, &e, &shdr); err = read_headers (file, &e, &shdr);
if (err) if (err)
@ -293,19 +297,24 @@ SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end)
stroff = s->sh_offset; stroff = s->sh_offset;
strsize = s->sh_size; strsize = s->sh_size;
if (*kern_end + 4 * sizeof (grub_freebsd_addr_t) + symsize + strsize chunk_size = 2 * sizeof (grub_freebsd_addr_t)
> grub_os_area_addr + grub_os_area_size) + ALIGN_UP (symsize + strsize, sizeof (grub_freebsd_addr_t));
return grub_error (GRUB_ERR_OUT_OF_RANGE, symtarget = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t));
"not enough memory for kernel symbols"); err = grub_relocator_alloc_chunk_addr (relocator, &sym_chunk,
symtarget, chunk_size);
if (err)
return err;
symstart = curload = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t)); symstart = symtarget;
*((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = symsize; symend = symstart + chunk_size;
curload = sym_chunk;
*((grub_freebsd_addr_t *) curload) = symsize;
curload += sizeof (grub_freebsd_addr_t); curload += sizeof (grub_freebsd_addr_t);
if (grub_file_seek (file, symoff) == (grub_off_t) -1) if (grub_file_seek (file, symoff) == (grub_off_t) -1)
return grub_errno; return grub_errno;
sym = (Elf_Sym *) UINT_TO_PTR (curload); sym = (Elf_Sym *) curload;
if (grub_file_read (file, UINT_TO_PTR (curload), symsize) != if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize)
(grub_ssize_t) symsize)
{ {
if (! grub_errno) if (! grub_errno)
return grub_error (GRUB_ERR_BAD_OS, "invalid ELF"); return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
@ -313,21 +322,17 @@ SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end)
} }
curload += symsize; curload += symsize;
*((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = strsize; *((grub_freebsd_addr_t *) curload) = strsize;
curload += sizeof (grub_freebsd_addr_t); curload += sizeof (grub_freebsd_addr_t);
if (grub_file_seek (file, stroff) == (grub_off_t) -1) if (grub_file_seek (file, stroff) == (grub_off_t) -1)
return grub_errno; return grub_errno;
str = (char *) UINT_TO_PTR (curload); str = (char *) curload;
if (grub_file_read (file, UINT_TO_PTR (curload), strsize) if (grub_file_read (file, curload, strsize) != (grub_ssize_t) strsize)
!= (grub_ssize_t) strsize)
{ {
if (! grub_errno) if (! grub_errno)
return grub_error (GRUB_ERR_BAD_OS, "invalid ELF"); return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
return grub_errno; return grub_errno;
} }
curload += strsize;
curload = ALIGN_UP (curload, sizeof (grub_freebsd_addr_t));
symend = curload;
for (i = 0; for (i = 0;
i * symentsize < symsize; i * symentsize < symsize;
@ -360,7 +365,8 @@ SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end)
sizeof (symend)); sizeof (symend));
if (err) if (err)
return err; return err;
*kern_end = ALIGN_PAGE (curload);
*kern_end = ALIGN_PAGE (symend);
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }

View file

@ -50,9 +50,10 @@
static void static void
fill_bsd64_pagetable (grub_uint8_t *target) fill_bsd64_pagetable (grub_uint8_t *src, grub_addr_t target)
{ {
grub_uint64_t *pt2, *pt3, *pt4; grub_uint64_t *pt2, *pt3, *pt4;
grub_addr_t pt2t, pt3t, pt4t;
int i; int i;
#define PG_V 0x001 #define PG_V 0x001
@ -60,11 +61,15 @@ fill_bsd64_pagetable (grub_uint8_t *target)
#define PG_U 0x004 #define PG_U 0x004
#define PG_PS 0x080 #define PG_PS 0x080
pt4 = (grub_uint64_t *) target; pt4 = (grub_uint64_t *) src;
pt3 = (grub_uint64_t *) (target + 4096); pt3 = (grub_uint64_t *) (src + 4096);
pt2 = (grub_uint64_t *) (target + 8192); pt2 = (grub_uint64_t *) (src + 8192);
grub_memset ((char *) target, 0, 4096 * 3); pt4t = target;
pt3t = target + 4096;
pt2t = target + 8192;
grub_memset (src, 0, 4096 * 3);
/* /*
* This is kinda brutal, but every single 1GB VM memory segment points to * This is kinda brutal, but every single 1GB VM memory segment points to
@ -74,11 +79,11 @@ fill_bsd64_pagetable (grub_uint8_t *target)
for (i = 0; i < 512; i++) for (i = 0; i < 512; i++)
{ {
/* Each slot of the level 4 pages points to the same level 3 page */ /* Each slot of the level 4 pages points to the same level 3 page */
pt4[i] = (grub_addr_t) &pt3[0]; pt4[i] = (grub_addr_t) pt3t;
pt4[i] |= PG_V | PG_RW | PG_U; pt4[i] |= PG_V | PG_RW | PG_U;
/* Each slot of the level 3 pages points to the same level 2 page */ /* Each slot of the level 3 pages points to the same level 2 page */
pt3[i] = (grub_addr_t) &pt2[0]; pt3[i] = (grub_addr_t) pt2t;
pt3[i] |= PG_V | PG_RW | PG_U; pt3[i] |= PG_V | PG_RW | PG_U;
/* The level 2 page slots are mapped with 2MB pages for 1GB. */ /* The level 2 page slots are mapped with 2MB pages for 1GB. */