diff --git a/ChangeLog b/ChangeLog
index ca0ab43..bd1776b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,40 @@
+2009-04-21 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-04-19 Vladimir Serbinenko
Correct GPT definition
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index 265b250..bf2516d 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -301,9 +301,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 f50f18e..3706f4d 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"
@@ -222,4 +225,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 afd3eb9..72a44d0 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 bbd2187..3e9c713 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 355cb3f..167cd0b 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
@@ -33,17 +33,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 +137,58 @@ 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));
+
+static grub_err_t
+grub_freebsd_add_mmap (void)
+{
+ grub_size_t len = 0;
+ struct grub_e820_mmap *mmap = 0;
+
+ 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)
+ {
+ if (mmap)
+ {
+ mmap->addr = addr;
+ mmap->size = size;
+ mmap->type = type;
+ mmap++;
+ }
+ else
+ len += sizeof (struct grub_e820_mmap);
+
+ return 0;
+ }
+
+ struct grub_e820_mmap *mmap_buf;
+
+ grub_machine_mmap_iterate (hook);
+ mmap_buf = mmap = grub_malloc (len);
+ if (! mmap)
+ return grub_errno;
+
+ grub_machine_mmap_iterate (hook);
+
+ 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 +215,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 +269,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 +325,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 +384,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 +394,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;
@@ -478,6 +632,29 @@ grub_bsd_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr)
}
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)
{
kern_start = kern_end = 0;
@@ -487,6 +664,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");
}
diff --git a/loader/i386/bsd_helper.S b/loader/i386/bsd_helper.S
new file mode 100644
index 0000000..23c8610
--- /dev/null
+++ b/loader/i386/bsd_helper.S
@@ -0,0 +1,45 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#include
+
+ .p2align 2
+
+
+ .code32
+
+/*
+ * Use cdecl calling convention for *BSD kernels.
+ */
+
+FUNCTION(grub_unix_real_boot)
+
+ /* 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_pagetable.c b/loader/i386/bsd_pagetable.c
new file mode 100644
index 0000000..522a19c
--- /dev/null
+++ b/loader/i386/bsd_pagetable.c
@@ -0,0 +1,84 @@
+
+/*-
+ * Copyright (c) 1998 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+
+static void
+fill_bsd64_pagetable (grub_uint8_t *target)
+{
+ grub_uint64_t *pt2, *pt3, *pt4;
+ int i;
+
+#define PG_V 0x001
+#define PG_RW 0x002
+#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);
+
+ grub_memset ((char *) target, 0, 4096 * 3);
+
+ /*
+ * This is kinda brutal, but every single 1GB VM memory segment points to
+ * the same first 1GB of physical memory. But it is how BSD expects
+ * it to be.
+ */
+ 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] |= 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] |= PG_V | PG_RW | PG_U;
+
+ /* The level 2 page slots are mapped with 2MB pages for 1GB. */
+ pt2[i] = i * (2 * 1024 * 1024);
+ pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
+ }
+}
diff --git a/loader/i386/bsd_trampoline.S b/loader/i386/bsd_trampoline.S
new file mode 100644
index 0000000..b283a87
--- /dev/null
+++ b/loader/i386/bsd_trampoline.S
@@ -0,0 +1,121 @@
+
+/*-
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+
+#define MSR_EFER 0xc0000080
+#define EFER_LME 0x00000100
+#define CR4_PAE 0x00000020
+#define CR4_PSE 0x00000010
+#define CR0_PG 0x80000000
+
+#include
+
+ .p2align 2
+
+ .code32
+
+
+VARIABLE(grub_bsd64_trampoline_start)
+
+ /* Discard `grub_unix_real_boot' return address. */
+ popl %eax
+
+ /* entry */
+ popl %edi
+
+ /* entry_hi */
+ popl %esi
+
+ cli
+
+ /* Turn on EFER.LME. */
+ movl $MSR_EFER, %ecx
+ rdmsr
+ orl $EFER_LME, %eax
+ wrmsr
+
+ /* Turn on PAE. */
+ movl %cr4, %eax
+ orl $(CR4_PAE | CR4_PSE), %eax
+ movl %eax, %cr4
+
+ /* Set %cr3 for PT4. */
+ popl %eax
+ movl %eax, %cr3
+
+ /* Push a dummy return address. */
+ pushl %eax
+
+ /* Turn on paging (implicitly sets EFER.LMA). */
+ movl %cr0, %eax
+ orl $CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Now we're in compatability mode. set %cs for long mode. */
+ /* lgdt */
+ .byte 0x0f
+ .byte 0x01
+ .byte 0x15
+VARIABLE (grub_bsd64_trampoline_gdt)
+ .long 0x0
+
+ /* ljmp */
+ .byte 0xea
+VARIABLE (grub_bsd64_trampoline_selfjump)
+ .long 0x0
+ .word 0x08
+
+ .code64
+
+bsd64_longmode:
+ /* We're still running V=P, jump to entry point. */
+ movl %esi, %eax
+ salq $32, %rax
+ orq %rdi, %rax
+ pushq %rax
+ ret
+VARIABLE(grub_bsd64_trampoline_end)