From: phcoder Date: Sun, 3 May 2009 09:03:31 +0000 (+0000) Subject: 2009-05-03 Bean Vladimir Serbinenko X-Git-Tag: 1.98~956 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cef17233d47e185682d72fa5b3c9e845933b9cdf;p=thirdparty%2Fgrub.git 2009-05-03 Bean Vladimir Serbinenko FreeBSD 64-bit support * conf/i386-pc.rmk (bsd_mod_SOURCES): add loader/i386/bsd_helper.S and loader/i386/bsd_trampoline.S (bsd_mod_ASFLAGS): new variable * include/grub/i386/bsd.h (FREEBSD_MODINFOMD_SMAP): new definition (FREEBSD_MODTYPE_KERNEL64): likewise (grub_bsd64_trampoline_start): likewise (grub_bsd64_trampoline_end): likewise (grub_bsd64_trampoline_selfjump): likewise (grub_bsd64_trampoline_gdt): likewise * include/grub/i386/loader.h (grub_unix_real_boot): moved from here ... * include/grub/i386/bsd.h (grub_unix_real_boot): ... moved here * kern/i386/loader.S (grub_unix_real_boot): moved from here ... * loader/i386/bsd_helper.S (grub_unix_real_boot): moved here * include/grub/gpt_partition.h (grub_gpt_partentry): Corrected the type of "attrib" member * loader/i386/bsd_pagetable.c: new file * loader/i386/bsd_trampoline.S: likewise * loader/i386/bsd.c (ALIGN_QWORD): new macro (ALIGN_VAR): likewise (entry_hi): new variable (kern_end_mdofs): likewise (is_64bit): likewise (grub_freebsd_add_meta): use ALIGN_VAR (grub_e820_mmap): new declaration (grub_freebsd_add_mmap): new function (grub_freebsd_add_meta_module): support 64 bit kernels (grub_freebsd_list_modules): use ALIGN_VAR (gdt_descriptor): new declaration (grub_freebsd_boot): support 64 bit kernels (grub_bsd_elf64_hook): new function (grub_bsd_load_elf): support elf64 --- diff --git a/ChangeLog b/ChangeLog index 42b120c2c..2cf8f8f29 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ +2009-05-03 Bean Vladimir Serbinenko + + FreeBSD 64-bit support + + * conf/i386-pc.rmk (bsd_mod_SOURCES): add loader/i386/bsd_helper.S + and loader/i386/bsd_trampoline.S + (bsd_mod_ASFLAGS): new variable + * include/grub/i386/bsd.h (FREEBSD_MODINFOMD_SMAP): new definition + (FREEBSD_MODTYPE_KERNEL64): likewise + (grub_bsd64_trampoline_start): likewise + (grub_bsd64_trampoline_end): likewise + (grub_bsd64_trampoline_selfjump): likewise + (grub_bsd64_trampoline_gdt): likewise + * include/grub/i386/loader.h (grub_unix_real_boot): moved from here ... + * include/grub/i386/bsd.h (grub_unix_real_boot): ... moved here + * kern/i386/loader.S (grub_unix_real_boot): moved from here ... + * loader/i386/bsd_helper.S (grub_unix_real_boot): moved here + * include/grub/gpt_partition.h (grub_gpt_partentry): Corrected the type + of "attrib" member + * loader/i386/bsd_pagetable.c: new file + * loader/i386/bsd_trampoline.S: likewise + * loader/i386/bsd.c (ALIGN_QWORD): new macro + (ALIGN_VAR): likewise + (entry_hi): new variable + (kern_end_mdofs): likewise + (is_64bit): likewise + (grub_freebsd_add_meta): use ALIGN_VAR + (grub_e820_mmap): new declaration + (grub_freebsd_add_mmap): new function + (grub_freebsd_add_meta_module): support 64 bit kernels + (grub_freebsd_list_modules): use ALIGN_VAR + (gdt_descriptor): new declaration + (grub_freebsd_boot): support 64 bit kernels + (grub_bsd_elf64_hook): new function + (grub_bsd_load_elf): support elf64 + 2009-05-03 Bean * script/sh/execute.c (grub_script_execute_cmdif): Reset grub_errno diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 5eb50367a..6c9100f16 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -313,9 +313,10 @@ aout_mod_CFLAGS = $(COMMON_CFLAGS) aout_mod_LDFLAGS = $(COMMON_LDFLAGS) # For bsd.mod -bsd_mod_SOURCES = loader/i386/bsd.c +bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd_helper.S loader/i386/bsd_trampoline.S bsd_mod_CFLAGS = $(COMMON_CFLAGS) bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) +bsd_mod_ASFLAGS = $(COMMON_ASFLAGS) # For usb.mod usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c diff --git a/include/grub/i386/bsd.h b/include/grub/i386/bsd.h index 78fae7945..321b31fdf 100644 --- a/include/grub/i386/bsd.h +++ b/include/grub/i386/bsd.h @@ -80,9 +80,12 @@ #define FREEBSD_MODINFOMD_SHDR 0x0009 /* section header table */ #define FREEBSD_MODINFOMD_NOCOPY 0x8000 /* don't copy this metadata to the kernel */ +#define FREEBSD_MODINFOMD_SMAP 0x1001 + #define FREEBSD_MODINFOMD_DEPLIST (0x4001 | FREEBSD_MODINFOMD_NOCOPY) /* depends on */ #define FREEBSD_MODTYPE_KERNEL "elf kernel" +#define FREEBSD_MODTYPE_KERNEL64 "elf64 kernel" #define FREEBSD_MODTYPE_MODULE "elf module" #define FREEBSD_MODTYPE_RAW "raw" @@ -224,4 +227,11 @@ struct grub_netbsd_btinfo_bootdisk int partition; }; +void grub_unix_real_boot (grub_addr_t entry, ...) + __attribute__ ((cdecl,noreturn)); + +extern grub_uint8_t grub_bsd64_trampoline_start, grub_bsd64_trampoline_end; +extern grub_uint32_t grub_bsd64_trampoline_selfjump; +extern grub_uint32_t grub_bsd64_trampoline_gdt; + #endif /* ! GRUB_BSD_CPU_HEADER */ diff --git a/include/grub/i386/loader.h b/include/grub/i386/loader.h index afd3eb93f..72a44d088 100644 --- a/include/grub/i386/loader.h +++ b/include/grub/i386/loader.h @@ -32,7 +32,4 @@ extern grub_size_t EXPORT_VAR(grub_os_area_size); grub_err_t EXPORT_FUNC(grub_linux16_boot) (void); -void EXPORT_FUNC(grub_unix_real_boot) (grub_addr_t entry, ...) - __attribute__ ((cdecl,noreturn)); - #endif /* ! GRUB_LOADER_CPU_HEADER */ diff --git a/kern/i386/loader.S b/kern/i386/loader.S index bbd2187fb..3e9c71327 100644 --- a/kern/i386/loader.S +++ b/kern/i386/loader.S @@ -118,25 +118,3 @@ linux_setup_seg: .word 0 .code32 -/* - * Use cdecl calling convention for *BSD kernels. - */ - -FUNCTION(grub_unix_real_boot) - - call EXT_C(grub_dl_unload_all) - - /* Interrupts should be disabled. */ - cli - - /* Discard `grub_unix_real_boot' return address. */ - popl %eax - - /* Fetch `entry' address ... */ - popl %eax - - /* - * ... and put our return address in its place. The kernel will - * ignore it, but it expects %esp to point to it. - */ - call *%eax diff --git a/loader/i386/bsd.c b/loader/i386/bsd.c index ea51aa220..db76399cd 100644 --- a/loader/i386/bsd.c +++ b/loader/i386/bsd.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2008, 2009 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -33,17 +35,19 @@ #include #define ALIGN_DWORD(a) ALIGN_UP (a, 4) +#define ALIGN_QWORD(a) ALIGN_UP (a, 8) +#define ALIGN_VAR(a) ((is_64bit) ? (ALIGN_QWORD(a)) : (ALIGN_DWORD(a))) #define ALIGN_PAGE(a) ALIGN_UP (a, 4096) #define MOD_BUF_ALLOC_UNIT 4096 static int kernel_type; static grub_dl_t my_mod; -static grub_addr_t entry, kern_start, kern_end; +static grub_addr_t entry, entry_hi, kern_start, kern_end; static grub_uint32_t bootflags; static char *mod_buf; -static grub_uint32_t mod_buf_len, mod_buf_max; -static int is_elf_kernel; +static grub_uint32_t mod_buf_len, mod_buf_max, kern_end_mdofs; +static int is_elf_kernel, is_64bit; static const char freebsd_opts[] = "DhaCcdgmnpqrsv"; static const grub_uint32_t freebsd_flags[] = @@ -135,11 +139,121 @@ grub_freebsd_add_meta (grub_uint32_t type, void *data, grub_uint32_t len) if (len) grub_memcpy (mod_buf + mod_buf_len, data, len); - mod_buf_len = ALIGN_DWORD (mod_buf_len + len); + mod_buf_len = ALIGN_VAR (mod_buf_len + len); return GRUB_ERR_NONE; } +struct grub_e820_mmap +{ + grub_uint64_t addr; + grub_uint64_t size; + grub_uint32_t type; +} __attribute__((packed)); +#define GRUB_E820_RAM 1 +#define GRUB_E820_RESERVED 2 +#define GRUB_E820_ACPI 3 +#define GRUB_E820_NVS 4 +#define GRUB_E820_EXEC_CODE 5 + +static grub_err_t +grub_freebsd_add_mmap (void) +{ + grub_size_t len = 0; + struct grub_e820_mmap *mmap_buf = 0; + struct grub_e820_mmap *mmap = 0; + int isfirstrun = 1; + + auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); + int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, + grub_uint32_t type) + { + /* FreeBSD assumes that first 64KiB are available. + Not always true but try to prevent panic somehow. */ + if (isfirstrun && addr != 0) + { + if (mmap) + { + mmap->addr = 0; + mmap->size = (addr < 0x10000) ? addr : 0x10000; + mmap->type = GRUB_E820_RAM; + mmap++; + } + else + len += sizeof (struct grub_e820_mmap); + } + isfirstrun = 0; + if (mmap) + { + mmap->addr = addr; + mmap->size = size; + switch (type) + { + case GRUB_MACHINE_MEMORY_AVAILABLE: + mmap->type = GRUB_E820_RAM; + break; + +#ifdef GRUB_MACHINE_MEMORY_ACPI + case GRUB_MACHINE_MEMORY_ACPI: + mmap->type = GRUB_E820_ACPI; + break; +#endif + +#ifdef GRUB_MACHINE_MEMORY_NVS + case GRUB_MACHINE_MEMORY_NVS: + mmap->type = GRUB_E820_NVS; + break; +#endif + + default: +#ifdef GRUB_MACHINE_MEMORY_CODE + case GRUB_MACHINE_MEMORY_CODE: +#endif +#ifdef GRUB_MACHINE_MEMORY_RESERVED + case GRUB_MACHINE_MEMORY_RESERVED: +#endif + mmap->type = GRUB_E820_RESERVED; + break; + } + + /* Merge regions if possible. */ + if (mmap != mmap_buf && mmap->type == mmap[-1].type && + mmap->addr == mmap[-1].addr + mmap[-1].size) + mmap[-1].size += mmap->size; + else + mmap++; + } + else + len += sizeof (struct grub_e820_mmap); + + return 0; + } + + grub_mmap_iterate (hook); + mmap_buf = mmap = grub_malloc (len); + if (! mmap) + return grub_errno; + + isfirstrun = 1; + grub_mmap_iterate (hook); + + len = (mmap - mmap_buf) * sizeof (struct grub_e820_mmap); + int i; + for (i = 0; i < mmap - mmap_buf; i++) + grub_dprintf ("bsd", "smap %d, %d:%llx - %llx\n", i, + mmap_buf[i].type, + (unsigned long long) mmap_buf[i].addr, + (unsigned long long) mmap_buf[i].size); + + grub_dprintf ("bsd", "%d entries in smap\n", mmap - mmap_buf); + grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_SMAP, mmap_buf, len); + + grub_free (mmap_buf); + + return grub_errno; +} + static grub_err_t grub_freebsd_add_meta_module (int is_kern, int argc, char **argv, grub_addr_t addr, grub_uint32_t size) @@ -166,13 +280,31 @@ grub_freebsd_add_meta_module (int is_kern, int argc, char **argv, argv++; } else - type = (is_kern) ? FREEBSD_MODTYPE_KERNEL : FREEBSD_MODTYPE_RAW; + type = ((is_kern) ? + ((is_64bit) ? FREEBSD_MODTYPE_KERNEL64 : FREEBSD_MODTYPE_KERNEL) + : FREEBSD_MODTYPE_RAW); - if ((grub_freebsd_add_meta (FREEBSD_MODINFO_TYPE, type, + if (is_64bit) + { + grub_uint64_t addr64 = addr, size64 = size; + if ((grub_freebsd_add_meta (FREEBSD_MODINFO_TYPE, type, grub_strlen (type) + 1)) || - (grub_freebsd_add_meta (FREEBSD_MODINFO_ADDR, &addr, sizeof (addr))) || - (grub_freebsd_add_meta (FREEBSD_MODINFO_SIZE, &size, sizeof (size)))) - return grub_errno; + (grub_freebsd_add_meta (FREEBSD_MODINFO_ADDR, &addr64, + sizeof (addr64))) || + (grub_freebsd_add_meta (FREEBSD_MODINFO_SIZE, &size64, + sizeof (size64)))) + return grub_errno; + } + else + { + if ((grub_freebsd_add_meta (FREEBSD_MODINFO_TYPE, type, + grub_strlen (type) + 1)) || + (grub_freebsd_add_meta (FREEBSD_MODINFO_ADDR, &addr, + sizeof (addr))) || + (grub_freebsd_add_meta (FREEBSD_MODINFO_SIZE, &size, + sizeof (size)))) + return grub_errno; + } if (argc) { @@ -202,6 +334,23 @@ grub_freebsd_add_meta_module (int is_kern, int argc, char **argv, } } + if (is_kern) + { + int len = (is_64bit) ? 8 : 4; + grub_uint64_t data = 0; + + if ((grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_HOWTO, &data, 4)) || + (grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_ENVP, &data, len)) || + (grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_KERNEND, &data, len))) + return grub_errno; + kern_end_mdofs = mod_buf_len - len; + + return grub_freebsd_add_mmap (); + } + return GRUB_ERR_NONE; } @@ -241,10 +390,19 @@ grub_freebsd_list_modules (void) } } - pos = ALIGN_DWORD (pos + size); + pos = ALIGN_VAR (pos + size); } } +/* This function would be here but it's under different licence. */ +#include "bsd_pagetable.c" + +struct gdt_descriptor +{ + grub_uint16_t limit; + void *base; +} __attribute__ ((packed)); + static grub_err_t grub_freebsd_boot (void) { @@ -291,6 +449,9 @@ grub_freebsd_boot (void) if (is_elf_kernel) { + grub_addr_t md_ofs; + int ofs; + if (grub_freebsd_add_meta (FREEBSD_MODINFO_END, 0, 0)) return grub_errno; @@ -298,12 +459,70 @@ grub_freebsd_boot (void) bi.bi_modulep = kern_end; kern_end = ALIGN_PAGE (kern_end + mod_buf_len); + + if (is_64bit) + kern_end += 4096 * 4; + + md_ofs = bi.bi_modulep + kern_end_mdofs; + ofs = (is_64bit) ? 16 : 12; + *((grub_uint32_t *) md_ofs) = kern_end; + md_ofs -= ofs; + *((grub_uint32_t *) md_ofs) = bi.bi_envp; + md_ofs -= ofs; + *((grub_uint32_t *) md_ofs) = bootflags; } bi.bi_kernend = kern_end; - grub_unix_real_boot (entry, bootflags | FREEBSD_RB_BOOTINFO, bootdev, - 0, 0, 0, &bi, bi.bi_modulep, kern_end); + if (is_64bit) + { + grub_uint32_t *gdt; + grub_uint8_t *trampoline; + void (*launch_trampoline) (grub_addr_t entry, ...) + __attribute__ ((cdecl, regparm (0))); + grub_uint8_t *pagetable; + + struct gdt_descriptor *gdtdesc; + + pagetable = (grub_uint8_t *) (kern_end - 16384); + fill_bsd64_pagetable (pagetable); + + /* Create GDT. */ + gdt = (grub_uint32_t *) (kern_end - 4096); + gdt[0] = 0; + gdt[1] = 0; + gdt[2] = 0; + gdt[3] = 0x00209800; + gdt[4] = 0; + gdt[5] = 0x00008000; + + /* Create GDT descriptor. */ + gdtdesc = (struct gdt_descriptor *) (kern_end - 4096 + 24); + gdtdesc->limit = 24; + gdtdesc->base = gdt; + + /* Prepare trampoline. */ + trampoline = (grub_uint8_t *) (kern_end - 4096 + 24 + + sizeof (struct gdt_descriptor)); + launch_trampoline = (void __attribute__ ((cdecl, regparm (0))) + (*) (grub_addr_t entry, ...)) 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 + grub_unix_real_boot (entry, bootflags | FREEBSD_RB_BOOTINFO, bootdev, + 0, 0, 0, &bi, bi.bi_modulep, kern_end); /* Not reached. */ return GRUB_ERR_NONE; @@ -485,6 +704,29 @@ grub_bsd_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr) return GRUB_ERR_NONE; } +static grub_err_t +grub_bsd_elf64_hook (Elf64_Phdr * phdr, grub_addr_t * addr) +{ + Elf64_Addr paddr; + + paddr = phdr->p_paddr & 0xffffff; + + if ((paddr < grub_os_area_addr) + || (paddr + phdr->p_memsz > grub_os_area_addr + grub_os_area_size)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "Address 0x%x is out of range", + paddr); + + if ((!kern_start) || (paddr < kern_start)) + kern_start = paddr; + + if (paddr + phdr->p_memsz > kern_end) + kern_end = paddr + phdr->p_memsz; + + *addr = paddr; + + return GRUB_ERR_NONE; +} + static grub_err_t grub_bsd_load_elf (grub_elf_t elf) { @@ -495,6 +737,13 @@ grub_bsd_load_elf (grub_elf_t elf) entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFF; return grub_elf32_load (elf, grub_bsd_elf32_hook, 0, 0); } + else if (grub_elf_is_elf64 (elf)) + { + is_64bit = 1; + entry = elf->ehdr.ehdr64.e_entry & 0xffffffff; + entry_hi = (elf->ehdr.ehdr64.e_entry >> 32) & 0xffffffff; + return grub_elf64_load (elf, grub_bsd_elf64_hook, 0, 0); + } else return grub_error (GRUB_ERR_BAD_OS, "invalid elf"); }