diff --git a/include/grub/i386/bsd.h b/include/grub/i386/bsd.h index b37a86c7f..4f5d5d1ee 100644 --- a/include/grub/i386/bsd.h +++ b/include/grub/i386/bsd.h @@ -263,9 +263,11 @@ grub_err_t grub_freebsd_load_elfmodule_obj64 (struct grub_relocator *relocator, grub_file_t file, int argc, char *argv[], 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_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_err_t grub_freebsd_add_meta (grub_uint32_t type, void *data, diff --git a/loader/i386/bsd.c b/loader/i386/bsd.c index 9ee8a4b12..90cd8a9a4 100644 --- a/loader/i386/bsd.c +++ b/loader/i386/bsd.c @@ -436,12 +436,6 @@ grub_freebsd_list_modules (void) /* This function would be here but it's under different license. */ #include "bsd_pagetable.c" -struct gdt_descriptor -{ - grub_uint16_t limit; - void *base; -} __attribute__ ((packed)); - static grub_err_t 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; if (is_64bit) - p_size += 4096 * 4; + p_size += 4096 * 3; err = grub_relocator_alloc_chunk_addr (relocator, (void **) &p, kern_end, p_size); if (err) return err; - kern_end += p_size; - p0 = p; p_target = kern_end; + p0 = p; + kern_end += p_size; grub_env_iterate (iterate_env); @@ -547,49 +541,30 @@ grub_freebsd_boot (void) if (is_64bit) { - grub_uint32_t *gdt; - grub_uint8_t *trampoline; - void (*launch_trampoline) (grub_addr_t entry_lo, ...) - __attribute__ ((cdecl, regparm (0))); + struct grub_relocator64_state state; 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; - fill_bsd64_pagetable (pagetable); + pagetable = p - (4096 * 3); + fill_bsd64_pagetable (pagetable, (pagetable - p0) + p_target); - /* Create GDT. */ - gdt = (grub_uint32_t *) (p - 4096); - gdt[0] = 0; - gdt[1] = 0; - gdt[2] = 0; - gdt[3] = 0x00209800; - gdt[4] = 0; - gdt[5] = 0x00008000; + state.cr3 = (pagetable - p0) + p_target; + state.rsp = stack_target; + state.rip = (((grub_uint64_t) entry_hi) << 32) | entry; - /* Create GDT descriptor. */ - gdtdesc = (struct gdt_descriptor *) (p - 4096 + 24); - gdtdesc->limit = 24; - gdtdesc->base = gdt; - - /* 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); + stack[0] = entry; + stack[1] = bi.bi_modulep; + stack[2] = kern_end; + return grub_relocator64_boot (relocator, state, 0, 0x40000000); } else { @@ -936,7 +911,7 @@ grub_bsd_elf32_size_hook (grub_elf_t elf __attribute__ ((unused)), if (phdr->p_type != PT_LOAD && phdr->p_type != PT_DYNAMIC) - return 1; + return 0; 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) kern_end = paddr + phdr->p_memsz; - return 1; + return 0; } 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 && phdr->p_type != PT_DYNAMIC) - return 1; + return 0; 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) kern_end = paddr + phdr->p_memsz; - return 1; + return 0; } 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); if (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, kern_start, kern_end - kern_start); if (err) @@ -1154,9 +1132,9 @@ grub_cmd_freebsd (grub_extcmd_t cmd, int argc, char *argv[]) return grub_errno; if (is_64bit) - err = grub_freebsd_load_elf_meta64 (file, &kern_end); + err = grub_freebsd_load_elf_meta64 (relocator, file, &kern_end); else - err = grub_freebsd_load_elf_meta32 (file, &kern_end); + err = grub_freebsd_load_elf_meta32 (relocator, file, &kern_end); if (err) return err; diff --git a/loader/i386/bsdXX.c b/loader/i386/bsdXX.c index 2622287f9..8f5cfa750 100644 --- a/loader/i386/bsdXX.c +++ b/loader/i386/bsdXX.c @@ -249,24 +249,28 @@ SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator, grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE, argc - 1, argv + 1, 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 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; Elf_Ehdr e; Elf_Shdr *s; char *shdr; unsigned symoff, stroff, symsize, strsize; - grub_addr_t curload; grub_freebsd_addr_t symstart, symend, symentsize, dynamic; Elf_Sym *sym; + void *sym_chunk; + grub_uint8_t *curload; + grub_freebsd_addr_t symtarget; const char *str; unsigned i; + grub_size_t chunk_size; err = read_headers (file, &e, &shdr); 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; strsize = s->sh_size; - if (*kern_end + 4 * sizeof (grub_freebsd_addr_t) + symsize + strsize - > grub_os_area_addr + grub_os_area_size) - return grub_error (GRUB_ERR_OUT_OF_RANGE, - "not enough memory for kernel symbols"); + chunk_size = 2 * sizeof (grub_freebsd_addr_t) + + ALIGN_UP (symsize + strsize, sizeof (grub_freebsd_addr_t)); + symtarget = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t)); + 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)); - *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = symsize; + symstart = symtarget; + symend = symstart + chunk_size; + + curload = sym_chunk; + *((grub_freebsd_addr_t *) curload) = symsize; curload += sizeof (grub_freebsd_addr_t); if (grub_file_seek (file, symoff) == (grub_off_t) -1) return grub_errno; - sym = (Elf_Sym *) UINT_TO_PTR (curload); - if (grub_file_read (file, UINT_TO_PTR (curload), symsize) != - (grub_ssize_t) symsize) + sym = (Elf_Sym *) curload; + if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize) { if (! grub_errno) 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; - *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = strsize; + *((grub_freebsd_addr_t *) curload) = strsize; curload += sizeof (grub_freebsd_addr_t); if (grub_file_seek (file, stroff) == (grub_off_t) -1) return grub_errno; - str = (char *) UINT_TO_PTR (curload); - if (grub_file_read (file, UINT_TO_PTR (curload), strsize) - != (grub_ssize_t) strsize) + str = (char *) curload; + if (grub_file_read (file, curload, strsize) != (grub_ssize_t) strsize) { if (! grub_errno) return grub_error (GRUB_ERR_BAD_OS, "invalid ELF"); return grub_errno; } - curload += strsize; - curload = ALIGN_UP (curload, sizeof (grub_freebsd_addr_t)); - symend = curload; for (i = 0; i * symentsize < symsize; @@ -360,7 +365,8 @@ SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end) sizeof (symend)); if (err) return err; - *kern_end = ALIGN_PAGE (curload); + + *kern_end = ALIGN_PAGE (symend); return GRUB_ERR_NONE; } diff --git a/loader/i386/bsd_pagetable.c b/loader/i386/bsd_pagetable.c index 0fd393707..13348cc83 100644 --- a/loader/i386/bsd_pagetable.c +++ b/loader/i386/bsd_pagetable.c @@ -50,9 +50,10 @@ 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_addr_t pt2t, pt3t, pt4t; int i; #define PG_V 0x001 @@ -60,11 +61,15 @@ fill_bsd64_pagetable (grub_uint8_t *target) #define PG_U 0x004 #define PG_PS 0x080 - pt4 = (grub_uint64_t *) target; - pt3 = (grub_uint64_t *) (target + 4096); - pt2 = (grub_uint64_t *) (target + 8192); + pt4 = (grub_uint64_t *) src; + pt3 = (grub_uint64_t *) (src + 4096); + 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 @@ -74,11 +79,11 @@ fill_bsd64_pagetable (grub_uint8_t *target) for (i = 0; i < 512; i++) { /* 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; /* 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; /* The level 2 page slots are mapped with 2MB pages for 1GB. */