aboutsummaryrefslogtreecommitdiffstats
path: root/grub-core/loader/i386
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/loader/i386')
-rw-r--r--grub-core/loader/i386/bsd.c1992
-rw-r--r--grub-core/loader/i386/bsd32.c6
-rw-r--r--grub-core/loader/i386/bsd64.c6
-rw-r--r--grub-core/loader/i386/bsdXX.c621
-rw-r--r--grub-core/loader/i386/bsd_pagetable.c92
-rw-r--r--grub-core/loader/i386/linux.c1016
-rw-r--r--grub-core/loader/i386/multiboot_mbi.c731
-rw-r--r--grub-core/loader/i386/pc/chainloader.c177
-rw-r--r--grub-core/loader/i386/pc/linux.c450
-rw-r--r--grub-core/loader/i386/pc/ntldr.c159
-rw-r--r--grub-core/loader/i386/xnu.c1137
11 files changed, 6387 insertions, 0 deletions
diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c
new file mode 100644
index 0000000..6487dc3
--- /dev/null
+++ b/grub-core/loader/i386/bsd.c
@@ -0,0 +1,1992 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009,2010 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/i386/bsd.h>
+#include <grub/i386/cpuid.h>
+#include <grub/memory.h>
+#include <grub/i386/memory.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/elfload.h>
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/aout.h>
+#include <grub/command.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/ns8250.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#include <grub/video.h>
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/machine/biosnum.h>
+#endif
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#define NETBSD_DEFAULT_VIDEO_MODE "800x600"
+#else
+#define NETBSD_DEFAULT_VIDEO_MODE "text"
+#include <grub/i386/pc/vbe.h>
+#endif
+#include <grub/video.h>
+
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/partition.h>
+#include <grub/relocator.h>
+#include <grub/i386/relocator.h>
+
+#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)
+
+static int kernel_type = KERNEL_TYPE_NONE;
+static grub_dl_t my_mod;
+static grub_addr_t entry, entry_hi, kern_start, kern_end;
+static void *kern_chunk_src;
+static grub_uint32_t bootflags;
+static int is_elf_kernel, is_64bit;
+static grub_uint32_t openbsd_root;
+static struct grub_relocator *relocator = NULL;
+static struct grub_openbsd_ramdisk_descriptor openbsd_ramdisk;
+
+struct bsd_tag
+{
+ struct bsd_tag *next;
+ grub_size_t len;
+ grub_uint32_t type;
+ union {
+ grub_uint8_t a;
+ grub_uint16_t b;
+ grub_uint32_t c;
+ } data[0];
+};
+
+static struct bsd_tag *tags, *tags_last;
+
+struct netbsd_module
+{
+ struct netbsd_module *next;
+ struct grub_netbsd_btinfo_module mod;
+};
+
+static struct netbsd_module *netbsd_mods, *netbsd_mods_last;
+
+static const struct grub_arg_option freebsd_opts[] =
+ {
+ {"dual", 'D', 0, N_("Display output on all consoles."), 0, 0},
+ {"serial", 'h', 0, N_("Use serial console."), 0, 0},
+ {"askname", 'a', 0, N_("Ask for file name to reboot from."), 0, 0},
+ {"cdrom", 'C', 0, N_("Use CDROM as root."), 0, 0},
+ {"config", 'c', 0, N_("Invoke user configuration routing."), 0, 0},
+ {"kdb", 'd', 0, N_("Enter in KDB on boot."), 0, 0},
+ {"gdb", 'g', 0, N_("Use GDB remote debugger instead of DDB."), 0, 0},
+ {"mute", 'm', 0, N_("Disable all boot output."), 0, 0},
+ {"nointr", 'n', 0, "", 0, 0},
+ {"pause", 'p', 0, N_("Wait for keypress after every line of output."), 0, 0},
+ {"quiet", 'q', 0, "", 0, 0},
+ {"dfltroot", 'r', 0, N_("Use compiled-in rootdev."), 0, 0},
+ {"single", 's', 0, N_("Boot into single mode."), 0, 0},
+ {"verbose", 'v', 0, N_("Boot with verbose messages."), 0, 0},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static const grub_uint32_t freebsd_flags[] =
+{
+ FREEBSD_RB_DUAL, FREEBSD_RB_SERIAL, FREEBSD_RB_ASKNAME,
+ FREEBSD_RB_CDROM, FREEBSD_RB_CONFIG, FREEBSD_RB_KDB,
+ FREEBSD_RB_GDB, FREEBSD_RB_MUTE, FREEBSD_RB_NOINTR,
+ FREEBSD_RB_PAUSE, FREEBSD_RB_QUIET, FREEBSD_RB_DFLTROOT,
+ FREEBSD_RB_SINGLE, FREEBSD_RB_VERBOSE, 0
+};
+
+static const struct grub_arg_option openbsd_opts[] =
+ {
+ {"askname", 'a', 0, N_("Ask for file name to reboot from."), 0, 0},
+ {"halt", 'b', 0, N_("Don't reboot, just halt."), 0, 0},
+ {"config", 'c', 0, N_("Change configured devices."), 0, 0},
+ {"single", 's', 0, N_("Boot into single mode."), 0, 0},
+ {"kdb", 'd', 0, N_("Enter in KDB on boot."), 0, 0},
+ {"root", 'r', 0, N_("Set root device."), "wdXY", ARG_TYPE_STRING},
+ {"serial", 'h', GRUB_ARG_OPTION_OPTIONAL,
+ N_("Use serial console."), N_("comUNIT[,SPEED]"), ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static const grub_uint32_t openbsd_flags[] =
+{
+ OPENBSD_RB_ASKNAME, OPENBSD_RB_HALT, OPENBSD_RB_CONFIG,
+ OPENBSD_RB_SINGLE, OPENBSD_RB_KDB, 0
+};
+
+#define OPENBSD_ROOT_ARG (ARRAY_SIZE (openbsd_flags) - 1)
+#define OPENBSD_SERIAL_ARG (ARRAY_SIZE (openbsd_flags))
+
+static const struct grub_arg_option netbsd_opts[] =
+ {
+ {"no-smp", '1', 0, N_("Disable SMP."), 0, 0},
+ {"no-acpi", '2', 0, N_("Disable ACPI."), 0, 0},
+ {"askname", 'a', 0, N_("Ask for file name to reboot from."), 0, 0},
+ {"halt", 'b', 0, N_("Don't reboot, just halt."), 0, 0},
+ {"config", 'c', 0, N_("Change configured devices."), 0, 0},
+ {"kdb", 'd', 0, N_("Enter in KDB on boot."), 0, 0},
+ {"miniroot", 'm', 0, "", 0, 0},
+ {"quiet", 'q', 0, N_("Don't display boot diagnostic messages."), 0, 0},
+ {"single", 's', 0, N_("Boot into single mode."), 0, 0},
+ {"verbose", 'v', 0, N_("Boot with verbose messages."), 0, 0},
+ {"debug", 'x', 0, N_("Boot with debug messages."), 0, 0},
+ {"silent", 'z', 0, N_("Supress normal output (warnings remain)."), 0, 0},
+ {"root", 'r', 0, N_("Set root device."), N_("DEVICE"), ARG_TYPE_STRING},
+ {"serial", 'h', GRUB_ARG_OPTION_OPTIONAL,
+ N_("Use serial console."), N_("[ADDR|comUNIT][,SPEED]"), ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static const grub_uint32_t netbsd_flags[] =
+{
+ NETBSD_AB_NOSMP, NETBSD_AB_NOACPI, NETBSD_RB_ASKNAME,
+ NETBSD_RB_HALT, NETBSD_RB_USERCONFIG, NETBSD_RB_KDB,
+ NETBSD_RB_MINIROOT, NETBSD_AB_QUIET, NETBSD_RB_SINGLE,
+ NETBSD_AB_VERBOSE, NETBSD_AB_DEBUG, NETBSD_AB_SILENT, 0
+};
+
+#define NETBSD_ROOT_ARG (ARRAY_SIZE (netbsd_flags) - 1)
+#define NETBSD_SERIAL_ARG (ARRAY_SIZE (netbsd_flags))
+
+static void
+grub_bsd_get_device (grub_uint32_t * biosdev,
+ grub_uint32_t * unit,
+ grub_uint32_t * slice, grub_uint32_t * part)
+{
+ grub_device_t dev;
+
+#ifdef GRUB_MACHINE_PCBIOS
+ *biosdev = grub_get_root_biosnumber () & 0xff;
+#else
+ *biosdev = 0xff;
+#endif
+ *unit = (*biosdev & 0x7f);
+ *slice = 0xff;
+ *part = 0xff;
+ dev = grub_device_open (0);
+ if (dev && dev->disk && dev->disk->partition)
+ {
+ if (dev->disk->partition->parent)
+ {
+ *part = dev->disk->partition->number;
+ *slice = dev->disk->partition->parent->number + 1;
+ }
+ else
+ *slice = dev->disk->partition->number + 1;
+ }
+ if (dev)
+ grub_device_close (dev);
+}
+
+grub_err_t
+grub_bsd_add_meta (grub_uint32_t type, void *data, grub_uint32_t len)
+{
+ struct bsd_tag *newtag;
+
+ newtag = grub_malloc (len + sizeof (struct bsd_tag));
+ if (!newtag)
+ return grub_errno;
+ newtag->len = len;
+ newtag->type = type;
+ newtag->next = NULL;
+ if (len)
+ grub_memcpy (newtag->data, data, len);
+
+ if (kernel_type == KERNEL_TYPE_FREEBSD
+ && type == (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_SMAP))
+ {
+ struct bsd_tag *p;
+ for (p = tags;
+ p->type != (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_KERNEND);
+ p = p->next);
+
+ if (p)
+ {
+ newtag->next = p->next;
+ p->next = newtag;
+ if (newtag->next == NULL)
+ tags_last = newtag;
+ return GRUB_ERR_NONE;
+ }
+ }
+
+ if (tags_last)
+ tags_last->next = newtag;
+ else
+ tags = newtag;
+ tags_last = newtag;
+
+ 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_BADRAM 5
+
+static void
+generate_e820_mmap (grub_size_t *len, grub_size_t *cnt, void *buf)
+{
+ int count = 0;
+ struct grub_e820_mmap *mmap = buf;
+ struct grub_e820_mmap prev, cur;
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+ cur.addr = addr;
+ cur.size = size;
+ switch (type)
+ {
+ case GRUB_MEMORY_AVAILABLE:
+ cur.type = GRUB_E820_RAM;
+ break;
+
+ case GRUB_MEMORY_ACPI:
+ cur.type = GRUB_E820_ACPI;
+ break;
+
+ case GRUB_MEMORY_NVS:
+ cur.type = GRUB_E820_NVS;
+ break;
+
+ default:
+ case GRUB_MEMORY_CODE:
+ case GRUB_MEMORY_RESERVED:
+ cur.type = GRUB_E820_RESERVED;
+ break;
+ }
+
+ /* Merge regions if possible. */
+ if (count && cur.type == prev.type && cur.addr == prev.addr + prev.size)
+ {
+ prev.size += cur.size;
+ if (mmap)
+ mmap[-1] = prev;
+ }
+ else
+ {
+ if (mmap)
+ *mmap++ = cur;
+ prev = cur;
+ count++;
+ }
+
+ if (kernel_type == KERNEL_TYPE_OPENBSD && prev.addr < 0x100000
+ && prev.addr + prev.size > 0x100000)
+ {
+ cur.addr = 0x100000;
+ cur.size = prev.addr + prev.size - 0x100000;
+ cur.type = prev.type;
+ prev.size = 0x100000 - prev.addr;
+ if (mmap)
+ {
+ mmap[-1] = prev;
+ mmap[0] = cur;
+ mmap++;
+ }
+ prev = cur;
+ count++;
+ }
+
+ return 0;
+ }
+
+ grub_mmap_iterate (hook);
+
+ if (len)
+ *len = count * sizeof (struct grub_e820_mmap);
+ *cnt = count;
+
+ return;
+}
+
+static grub_err_t
+grub_bsd_add_mmap (void)
+{
+ grub_size_t len, cnt;
+ void *buf = NULL, *buf0;
+
+ generate_e820_mmap (&len, &cnt, buf);
+
+ if (kernel_type == KERNEL_TYPE_NETBSD)
+ len += sizeof (grub_uint32_t);
+
+ if (kernel_type == KERNEL_TYPE_OPENBSD)
+ len += sizeof (struct grub_e820_mmap);
+
+ buf = grub_malloc (len);
+ if (!buf)
+ return grub_errno;
+
+ buf0 = buf;
+ if (kernel_type == KERNEL_TYPE_NETBSD)
+ {
+ *(grub_uint32_t *) buf = cnt;
+ buf = ((grub_uint32_t *) buf + 1);
+ }
+
+ generate_e820_mmap (NULL, &cnt, buf);
+
+ if (kernel_type == KERNEL_TYPE_OPENBSD)
+ grub_memset ((grub_uint8_t *) buf + len - sizeof (struct grub_e820_mmap), 0,
+ sizeof (struct grub_e820_mmap));
+
+ grub_dprintf ("bsd", "%u entries in smap\n", (unsigned) cnt);
+ if (kernel_type == KERNEL_TYPE_NETBSD)
+ grub_bsd_add_meta (NETBSD_BTINFO_MEMMAP, buf0, len);
+ else if (kernel_type == KERNEL_TYPE_OPENBSD)
+ grub_bsd_add_meta (OPENBSD_BOOTARG_MMAP, buf0, len);
+ else
+ grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_SMAP, buf0, len);
+
+ grub_free (buf0);
+
+ return grub_errno;
+}
+
+grub_err_t
+grub_freebsd_add_meta_module (char *filename, char *type, int argc, char **argv,
+ grub_addr_t addr, grub_uint32_t size)
+{
+ char *name;
+ name = grub_strrchr (filename, '/');
+ if (name)
+ name++;
+ else
+ name = filename;
+ if (grub_strcmp (type, "/boot/zfs/zpool.cache") == 0)
+ name = "/boot/zfs/zpool.cache";
+
+ if (grub_bsd_add_meta (FREEBSD_MODINFO_NAME, name, grub_strlen (name) + 1))
+ return grub_errno;
+
+ if (is_64bit)
+ {
+ grub_uint64_t addr64 = addr, size64 = size;
+ if (grub_bsd_add_meta (FREEBSD_MODINFO_TYPE, type, grub_strlen (type) + 1)
+ || grub_bsd_add_meta (FREEBSD_MODINFO_ADDR, &addr64, sizeof (addr64))
+ || grub_bsd_add_meta (FREEBSD_MODINFO_SIZE, &size64, sizeof (size64)))
+ return grub_errno;
+ }
+ else
+ {
+ if (grub_bsd_add_meta (FREEBSD_MODINFO_TYPE, type, grub_strlen (type) + 1)
+ || grub_bsd_add_meta (FREEBSD_MODINFO_ADDR, &addr, sizeof (addr))
+ || grub_bsd_add_meta (FREEBSD_MODINFO_SIZE, &size, sizeof (size)))
+ return grub_errno;
+ }
+
+ if (argc)
+ {
+ int i, n;
+
+ n = 0;
+ for (i = 0; i < argc; i++)
+ {
+ n += grub_strlen (argv[i]) + 1;
+ }
+
+ if (n)
+ {
+ char cmdline[n], *p;
+
+ p = cmdline;
+ for (i = 0; i < argc; i++)
+ {
+ grub_strcpy (p, argv[i]);
+ p += grub_strlen (argv[i]);
+ *(p++) = ' ';
+ }
+ *p = 0;
+
+ if (grub_bsd_add_meta (FREEBSD_MODINFO_ARGS, cmdline, n))
+ return grub_errno;
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_freebsd_list_modules (void)
+{
+ struct bsd_tag *tag;
+
+ grub_printf (" %-18s %-18s%14s%14s\n", "name", "type", "addr", "size");
+
+ for (tag = tags; tag; tag = tag->next)
+ {
+ switch (tag->type)
+ {
+ case FREEBSD_MODINFO_NAME:
+ case FREEBSD_MODINFO_TYPE:
+ grub_printf (" %-18s", (char *) tag->data);
+ break;
+ case FREEBSD_MODINFO_ADDR:
+ {
+ grub_uint32_t addr;
+
+ addr = *((grub_uint32_t *) tag->data);
+ grub_printf (" 0x%08x", addr);
+ break;
+ }
+ case FREEBSD_MODINFO_SIZE:
+ {
+ grub_uint32_t len;
+
+ len = *((grub_uint32_t *) tag->data);
+ grub_printf (" 0x%08x\n", len);
+ }
+ }
+ }
+}
+
+static grub_err_t
+grub_netbsd_add_meta_module (char *filename, grub_uint32_t type,
+ grub_addr_t addr, grub_uint32_t size)
+{
+ char *name;
+ struct netbsd_module *mod;
+ name = grub_strrchr (filename, '/');
+
+ if (name)
+ name++;
+ else
+ name = filename;
+
+ mod = grub_zalloc (sizeof (*mod));
+ if (!mod)
+ return grub_errno;
+
+ grub_strncpy (mod->mod.name, name, sizeof (mod->mod.name) - 1);
+ mod->mod.addr = addr;
+ mod->mod.type = type;
+ mod->mod.size = size;
+
+ if (netbsd_mods_last)
+ netbsd_mods_last->next = mod;
+ else
+ netbsd_mods = mod;
+ netbsd_mods_last = mod;
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_netbsd_list_modules (void)
+{
+ struct netbsd_module *mod;
+
+ grub_printf (" %-18s%14s%14s%14s\n", "name", "type", "addr", "size");
+
+ for (mod = netbsd_mods; mod; mod = mod->next)
+ grub_printf (" %-18s 0x%08x 0x%08x 0x%08x", mod->mod.name,
+ mod->mod.type, mod->mod.addr, mod->mod.size);
+}
+
+/* This function would be here but it's under different license. */
+#include "bsd_pagetable.c"
+
+static grub_uint32_t freebsd_bootdev, freebsd_biosdev;
+
+static grub_err_t
+grub_freebsd_boot (void)
+{
+ struct grub_freebsd_bootinfo bi;
+ grub_uint8_t *p, *p0;
+ grub_addr_t p_target;
+ grub_size_t p_size = 0;
+ grub_err_t err;
+ grub_size_t tag_buf_len = 0;
+
+ auto int iterate_env (struct grub_env_var *var);
+ int iterate_env (struct grub_env_var *var)
+ {
+ if ((!grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1)) && (var->name[sizeof("kFreeBSD.") - 1]))
+ {
+ grub_strcpy ((char *) p, &var->name[sizeof("kFreeBSD.") - 1]);
+ p += grub_strlen ((char *) p);
+ *(p++) = '=';
+ grub_strcpy ((char *) p, var->value);
+ p += grub_strlen ((char *) p) + 1;
+ }
+
+ return 0;
+ }
+
+ auto int iterate_env_count (struct grub_env_var *var);
+ int iterate_env_count (struct grub_env_var *var)
+ {
+ if ((!grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1)) && (var->name[sizeof("kFreeBSD.") - 1]))
+ {
+ p_size += grub_strlen (&var->name[sizeof("kFreeBSD.") - 1]);
+ p_size++;
+ p_size += grub_strlen (var->value) + 1;
+ }
+
+ return 0;
+ }
+
+ grub_memset (&bi, 0, sizeof (bi));
+ bi.version = FREEBSD_BOOTINFO_VERSION;
+ bi.length = sizeof (bi);
+
+ bi.boot_device = freebsd_biosdev;
+
+ p_size = 0;
+ grub_env_iterate (iterate_env_count);
+
+ if (p_size)
+ p_size = ALIGN_PAGE (kern_end + p_size + 1) - kern_end;
+
+ if (is_elf_kernel)
+ {
+ struct bsd_tag *tag;
+
+ err = grub_bsd_add_mmap ();
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_END, 0, 0);
+ if (err)
+ return err;
+
+ tag_buf_len = 0;
+ for (tag = tags; tag; tag = tag->next)
+ tag_buf_len = ALIGN_VAR (tag_buf_len
+ + sizeof (struct freebsd_tag_header)
+ + tag->len);
+ p_size = ALIGN_PAGE (kern_end + p_size + tag_buf_len) - kern_end;
+ }
+
+ if (is_64bit)
+ p_size += 4096 * 3;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ kern_end, p_size);
+ if (err)
+ return err;
+ p = get_virtual_current_address (ch);
+ }
+ p_target = kern_end;
+ p0 = p;
+ kern_end += p_size;
+
+ grub_env_iterate (iterate_env);
+
+ if (p != p0)
+ {
+ *(p++) = 0;
+
+ bi.environment = p_target;
+ }
+
+ if (is_elf_kernel)
+ {
+ grub_uint8_t *p_tag = p;
+ struct bsd_tag *tag;
+
+ for (tag = tags; tag; tag = tag->next)
+ {
+ struct freebsd_tag_header *head
+ = (struct freebsd_tag_header *) p_tag;
+ head->type = tag->type;
+ head->len = tag->len;
+ p_tag += sizeof (struct freebsd_tag_header);
+ switch (tag->type)
+ {
+ case FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_HOWTO:
+ if (is_64bit)
+ *(grub_uint64_t *) p_tag = bootflags;
+ else
+ *(grub_uint32_t *) p_tag = bootflags;
+ break;
+
+ case FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_ENVP:
+ if (is_64bit)
+ *(grub_uint64_t *) p_tag = bi.environment;
+ else
+ *(grub_uint32_t *) p_tag = bi.environment;
+ break;
+
+ case FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_KERNEND:
+ if (is_64bit)
+ *(grub_uint64_t *) p_tag = kern_end;
+ else
+ *(grub_uint32_t *) p_tag = kern_end;
+ break;
+
+ default:
+ grub_memcpy (p_tag, tag->data, tag->len);
+ break;
+ }
+ p_tag += tag->len;
+ p_tag = ALIGN_VAR (p_tag - p) + p;
+ }
+
+ bi.tags = (p - p0) + p_target;
+
+ p = (ALIGN_PAGE ((p_tag - p0) + p_target) - p_target) + p0;
+ }
+
+ bi.kern_end = kern_end;
+
+ grub_video_set_mode ("text", 0, 0);
+
+ if (is_64bit)
+ {
+ struct grub_relocator64_state state;
+ grub_uint8_t *pagetable;
+ grub_uint32_t *stack;
+ grub_addr_t stack_target;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ 0x10000, 0x90000,
+ 3 * sizeof (grub_uint32_t)
+ + sizeof (bi), 4,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ if (err)
+ return err;
+ stack = get_virtual_current_address (ch);
+ stack_target = get_physical_target_address (ch);
+ }
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#endif
+
+ pagetable = p;
+ fill_bsd64_pagetable (pagetable, (pagetable - p0) + p_target);
+
+ state.cr3 = (pagetable - p0) + p_target;
+ state.rsp = stack_target;
+ state.rip = (((grub_uint64_t) entry_hi) << 32) | entry;
+
+ stack[0] = entry;
+ stack[1] = bi.tags;
+ stack[2] = kern_end;
+ return grub_relocator64_boot (relocator, state, 0, 0x40000000);
+ }
+ else
+ {
+ struct grub_relocator32_state state;
+ grub_uint32_t *stack;
+ grub_addr_t stack_target;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ 0x10000, 0x90000,
+ 9 * sizeof (grub_uint32_t)
+ + sizeof (bi), 4,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ if (err)
+ return err;
+ stack = get_virtual_current_address (ch);
+ stack_target = get_physical_target_address (ch);
+ }
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#endif
+
+ grub_memcpy (&stack[9], &bi, sizeof (bi));
+ state.eip = entry;
+ state.esp = stack_target;
+ state.ebp = stack_target;
+ stack[0] = entry; /* "Return" address. */
+ stack[1] = bootflags | FREEBSD_RB_BOOTINFO;
+ stack[2] = freebsd_bootdev;
+ stack[3] = 0;
+ stack[4] = 0;
+ stack[5] = 0;
+ stack[6] = stack_target + 9 * sizeof (grub_uint32_t);
+ stack[7] = bi.tags;
+ stack[8] = kern_end;
+ return grub_relocator32_boot (relocator, state);
+ }
+
+ /* Not reached. */
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_openbsd_boot (void)
+{
+ grub_uint32_t *stack;
+ struct grub_relocator32_state state;
+ void *curarg, *buf0, *arg0;
+ grub_addr_t buf_target;
+ grub_err_t err;
+ grub_size_t tag_buf_len;
+
+ err = grub_bsd_add_mmap ();
+ if (err)
+ return err;
+
+ {
+ struct bsd_tag *tag;
+ tag_buf_len = 0;
+ for (tag = tags; tag; tag = tag->next)
+ tag_buf_len = ALIGN_VAR (tag_buf_len
+ + sizeof (struct grub_openbsd_bootargs)
+ + tag->len);
+ }
+
+ buf_target = GRUB_BSD_TEMP_BUFFER - 9 * sizeof (grub_uint32_t);
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch, buf_target,
+ tag_buf_len
+ + sizeof (struct grub_openbsd_bootargs)
+ + 9 * sizeof (grub_uint32_t));
+ if (err)
+ return err;
+ buf0 = get_virtual_current_address (ch);
+ }
+
+ stack = (grub_uint32_t *) buf0;
+ arg0 = curarg = stack + 9;
+
+ {
+ struct bsd_tag *tag;
+ struct grub_openbsd_bootargs *head;
+
+ for (tag = tags; tag; tag = tag->next)
+ {
+ head = curarg;
+ head->ba_type = tag->type;
+ head->ba_size = tag->len + sizeof (*head);
+ curarg = head + 1;
+ grub_memcpy (curarg, tag->data, tag->len);
+ curarg = (grub_uint8_t *) curarg + tag->len;
+ head->ba_next = (grub_uint8_t *) curarg - (grub_uint8_t *) buf0
+ + buf_target;
+ }
+ head = curarg;
+ head->ba_type = OPENBSD_BOOTARG_END;
+ head->ba_size = 0;
+ head->ba_next = 0;
+ }
+
+ grub_video_set_mode ("text", 0, 0);
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#endif
+
+ state.eip = entry;
+ state.ebp = state.esp
+ = ((grub_uint8_t *) stack - (grub_uint8_t *) buf0) + buf_target;
+ stack[0] = entry;
+ stack[1] = bootflags;
+ stack[2] = openbsd_root;
+ stack[3] = OPENBSD_BOOTARG_APIVER;
+ stack[4] = 0;
+ stack[5] = grub_mmap_get_upper () >> 10;
+ stack[6] = grub_mmap_get_lower () >> 10;
+ stack[7] = (grub_uint8_t *) curarg - (grub_uint8_t *) arg0;
+ stack[8] = ((grub_uint8_t *) arg0 - (grub_uint8_t *) buf0) + buf_target;
+
+ return grub_relocator32_boot (relocator, state);
+}
+
+static grub_err_t
+grub_netbsd_setup_video (void)
+{
+ struct grub_video_mode_info mode_info;
+ void *framebuffer;
+ const char *modevar;
+ struct grub_netbsd_btinfo_framebuf params;
+ grub_err_t err;
+ grub_video_driver_id_t driv_id;
+
+ modevar = grub_env_get ("gfxpayload");
+
+ /* Now all graphical modes are acceptable.
+ May change in future if we have modes without framebuffer. */
+ if (modevar && *modevar != 0)
+ {
+ char *tmp;
+ tmp = grub_xasprintf ("%s;" NETBSD_DEFAULT_VIDEO_MODE, modevar);
+ if (! tmp)
+ return grub_errno;
+ err = grub_video_set_mode (tmp, 0, 0);
+ grub_free (tmp);
+ }
+ else
+ err = grub_video_set_mode (NETBSD_DEFAULT_VIDEO_MODE, 0, 0);
+
+ if (err)
+ return err;
+
+ driv_id = grub_video_get_driver_id ();
+ if (driv_id == GRUB_VIDEO_DRIVER_NONE)
+ return GRUB_ERR_NONE;
+
+ err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+
+ if (err)
+ return err;
+
+ params.width = mode_info.width;
+ params.height = mode_info.height;
+ params.bpp = mode_info.bpp;
+ params.pitch = mode_info.pitch;
+ params.flags = 0;
+
+ params.fbaddr = (grub_addr_t) framebuffer;
+
+ params.red_mask_size = mode_info.red_mask_size;
+ params.red_field_pos = mode_info.red_field_pos;
+ params.green_mask_size = mode_info.green_mask_size;
+ params.green_field_pos = mode_info.green_field_pos;
+ params.blue_mask_size = mode_info.blue_mask_size;
+ params.blue_field_pos = mode_info.blue_field_pos;
+
+#ifdef GRUB_MACHINE_PCBIOS
+ /* VESA packed modes may come with zeroed mask sizes, which need
+ to be set here according to DAC Palette width. If we don't,
+ this results in Linux displaying a black screen. */
+ if (mode_info.bpp <= 8 && driv_id == GRUB_VIDEO_DRIVER_VBE)
+ {
+ struct grub_vbe_info_block controller_info;
+ int status;
+ int width = 8;
+
+ status = grub_vbe_bios_get_controller_info (&controller_info);
+
+ if (status == GRUB_VBE_STATUS_OK &&
+ (controller_info.capabilities & GRUB_VBE_CAPABILITY_DACWIDTH))
+ status = grub_vbe_bios_set_dac_palette_width (&width);
+
+ if (status != GRUB_VBE_STATUS_OK)
+ /* 6 is default after mode reset. */
+ width = 6;
+
+ params.red_mask_size = params.green_mask_size
+ = params.blue_mask_size = width;
+ }
+#endif
+
+ err = grub_bsd_add_meta (NETBSD_BTINFO_FRAMEBUF, &params, sizeof (params));
+ return err;
+}
+
+static grub_err_t
+grub_netbsd_add_modules (void)
+{
+ struct netbsd_module *mod;
+ unsigned modcnt = 0;
+ struct grub_netbsd_btinfo_modules *mods;
+ unsigned i;
+ grub_err_t err;
+
+ for (mod = netbsd_mods; mod; mod = mod->next)
+ modcnt++;
+
+ mods = grub_malloc (sizeof (*mods) + sizeof (mods->mods[0]) * modcnt);
+ if (!mods)
+ return grub_errno;
+
+ mods->num = modcnt;
+ mods->last_addr = kern_end;
+ for (mod = netbsd_mods, i = 0; mod; i++, mod = mod->next)
+ mods->mods[i] = mod->mod;
+
+ err = grub_bsd_add_meta (NETBSD_BTINFO_MODULES, mods,
+ sizeof (*mods) + sizeof (mods->mods[0]) * modcnt);
+ grub_free (mods);
+ return err;
+}
+
+static grub_err_t
+grub_netbsd_boot (void)
+{
+ struct grub_netbsd_bootinfo *bootinfo;
+ void *curarg, *arg0;
+ grub_addr_t arg_target, stack_target;
+ grub_uint32_t *stack;
+ grub_err_t err;
+ struct grub_relocator32_state state;
+ grub_size_t tag_buf_len = 0;
+ int tag_count = 0;
+
+ err = grub_bsd_add_mmap ();
+ if (err)
+ return err;
+
+ err = grub_netbsd_setup_video ();
+ if (err)
+ {
+ grub_print_error ();
+ grub_printf ("Booting however\n");
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ err = grub_netbsd_add_modules ();
+ if (err)
+ return err;
+
+ {
+ struct bsd_tag *tag;
+ tag_buf_len = 0;
+ for (tag = tags; tag; tag = tag->next)
+ {
+ tag_buf_len = ALIGN_VAR (tag_buf_len
+ + sizeof (struct grub_netbsd_btinfo_common)
+ + tag->len);
+ tag_count++;
+ }
+ }
+
+ arg_target = kern_end;
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ arg_target, tag_buf_len
+ + sizeof (struct grub_netbsd_bootinfo)
+ + tag_count * sizeof (grub_uint32_t));
+ if (err)
+ return err;
+ curarg = get_virtual_current_address (ch);
+ }
+
+ arg0 = curarg;
+ bootinfo = (void *) ((grub_uint8_t *) arg0 + tag_buf_len);
+
+ {
+ struct bsd_tag *tag;
+ unsigned i;
+
+ bootinfo->bi_count = tag_count;
+ for (tag = tags, i = 0; tag; i++, tag = tag->next)
+ {
+ struct grub_netbsd_btinfo_common *head = curarg;
+ bootinfo->bi_data[i] = ((grub_uint8_t *) curarg - (grub_uint8_t *) arg0)
+ + arg_target;
+ head->type = tag->type;
+ head->len = tag->len + sizeof (*head);
+ curarg = head + 1;
+ grub_memcpy (curarg, tag->data, tag->len);
+ curarg = (grub_uint8_t *) curarg + tag->len;
+ }
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x10000, 0x90000,
+ 7 * sizeof (grub_uint32_t), 4,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ if (err)
+ return err;
+ stack = get_virtual_current_address (ch);
+ stack_target = get_physical_target_address (ch);
+ }
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#endif
+
+ state.eip = entry;
+ state.esp = stack_target;
+ state.ebp = stack_target;
+ stack[0] = entry;
+ stack[1] = bootflags;
+ stack[2] = 0;
+ stack[3] = ((grub_uint8_t *) bootinfo - (grub_uint8_t *) arg0) + arg_target;
+ stack[4] = 0;
+ stack[5] = grub_mmap_get_upper () >> 10;
+ stack[6] = grub_mmap_get_lower () >> 10;
+
+ return grub_relocator32_boot (relocator, state);
+}
+
+static grub_err_t
+grub_bsd_unload (void)
+{
+ struct bsd_tag *tag, *next;
+ for (tag = tags; tag; tag = next)
+ {
+ next = tag->next;
+ grub_free (tag);
+ }
+ tags = NULL;
+ tags_last = NULL;
+
+ kernel_type = KERNEL_TYPE_NONE;
+ grub_dl_unref (my_mod);
+
+ grub_relocator_unload (relocator);
+ relocator = NULL;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_bsd_load_aout (grub_file_t file)
+{
+ grub_addr_t load_addr, load_end;
+ int ofs, align_page;
+ union grub_aout_header ah;
+ grub_err_t err;
+ grub_size_t bss_size;
+
+ if ((grub_file_seek (file, 0)) == (grub_off_t) - 1)
+ return grub_errno;
+
+ if (grub_file_read (file, &ah, sizeof (ah)) != sizeof (ah))
+ return grub_error (GRUB_ERR_READ_ERROR, "cannot read the a.out header");
+
+ if (grub_aout_get_type (&ah) != AOUT_TYPE_AOUT32)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid a.out header");
+
+ entry = ah.aout32.a_entry & 0xFFFFFF;
+
+ if (AOUT_GETMAGIC (ah.aout32) == AOUT32_ZMAGIC)
+ {
+ load_addr = entry;
+ ofs = 0x1000;
+ align_page = 0;
+ }
+ else
+ {
+ load_addr = entry & 0xF00000;
+ ofs = sizeof (struct grub_aout32_header);
+ align_page = 1;
+ }
+
+ if (load_addr < 0x100000)
+ return grub_error (GRUB_ERR_BAD_OS, "load address below 1M");
+
+ kern_start = load_addr;
+ load_end = kern_end = load_addr + ah.aout32.a_text + ah.aout32.a_data;
+ if (align_page)
+ kern_end = ALIGN_PAGE (kern_end);
+
+ if (ah.aout32.a_bss)
+ {
+ kern_end += ah.aout32.a_bss;
+ if (align_page)
+ kern_end = ALIGN_PAGE (kern_end);
+
+ bss_size = kern_end - load_end;
+ }
+ else
+ bss_size = 0;
+
+ {
+ grub_relocator_chunk_t ch;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ kern_start, kern_end - kern_start);
+ if (err)
+ return err;
+ kern_chunk_src = get_virtual_current_address (ch);
+ }
+
+ return grub_aout_load (file, ofs, kern_chunk_src,
+ ah.aout32.a_text + ah.aout32.a_data,
+ bss_size);
+}
+
+static int NESTED_FUNC_ATTR
+grub_bsd_elf32_size_hook (grub_elf_t elf __attribute__ ((unused)),
+ Elf32_Phdr *phdr, void *arg __attribute__ ((unused)))
+{
+ Elf32_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ return 0;
+
+ paddr = phdr->p_paddr & 0xFFFFFF;
+
+ if (paddr < kern_start)
+ kern_start = paddr;
+
+ if (paddr + phdr->p_memsz > kern_end)
+ kern_end = paddr + phdr->p_memsz;
+
+ return 0;
+}
+
+static grub_err_t
+grub_bsd_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr, int *do_load)
+{
+ Elf32_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ {
+ *do_load = 0;
+ return 0;
+ }
+
+ *do_load = 1;
+ phdr->p_paddr &= 0xFFFFFF;
+ paddr = phdr->p_paddr;
+
+ *addr = (grub_addr_t) (paddr - kern_start + (grub_uint8_t *) kern_chunk_src);
+
+ return GRUB_ERR_NONE;
+}
+
+static int NESTED_FUNC_ATTR
+grub_bsd_elf64_size_hook (grub_elf_t elf __attribute__ ((unused)),
+ Elf64_Phdr *phdr, void *arg __attribute__ ((unused)))
+{
+ Elf64_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ return 0;
+
+ paddr = phdr->p_paddr & 0xffffff;
+
+ if (paddr < kern_start)
+ kern_start = paddr;
+
+ if (paddr + phdr->p_memsz > kern_end)
+ kern_end = paddr + phdr->p_memsz;
+
+ return 0;
+}
+
+static grub_err_t
+grub_bsd_elf64_hook (Elf64_Phdr * phdr, grub_addr_t * addr, int *do_load)
+{
+ Elf64_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ {
+ *do_load = 0;
+ return 0;
+ }
+
+ *do_load = 1;
+ paddr = phdr->p_paddr & 0xffffff;
+
+ *addr = (grub_addr_t) (paddr - kern_start + (grub_uint8_t *) kern_chunk_src);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_bsd_load_elf (grub_elf_t elf)
+{
+ grub_err_t err;
+
+ kern_end = 0;
+ kern_start = ~0;
+
+ if (grub_elf_is_elf32 (elf))
+ {
+ grub_relocator_chunk_t ch;
+
+ entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFF;
+ err = grub_elf32_phdr_iterate (elf, grub_bsd_elf32_size_hook, NULL);
+ if (err)
+ return err;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ kern_start, kern_end - kern_start);
+ if (err)
+ return err;
+
+ kern_chunk_src = get_virtual_current_address (ch);
+
+ err = grub_elf32_load (elf, grub_bsd_elf32_hook, 0, 0);
+ if (err)
+ return err;
+ if (kernel_type != KERNEL_TYPE_OPENBSD)
+ return GRUB_ERR_NONE;
+ return grub_openbsd_find_ramdisk32 (elf->file, kern_start,
+ kern_chunk_src, &openbsd_ramdisk);
+ }
+ else if (grub_elf_is_elf64 (elf))
+ {
+ is_64bit = 1;
+
+ if (! grub_cpuid_has_longmode)
+ return grub_error (GRUB_ERR_BAD_OS, "your CPU does not implement AMD64 architecture");
+
+ /* FreeBSD has 64-bit entry point. */
+ if (kernel_type == KERNEL_TYPE_FREEBSD)
+ {
+ entry = elf->ehdr.ehdr64.e_entry & 0xffffffff;
+ entry_hi = (elf->ehdr.ehdr64.e_entry >> 32) & 0xffffffff;
+ }
+ else
+ {
+ entry = elf->ehdr.ehdr64.e_entry & 0x0fffffff;
+ entry_hi = 0;
+ }
+
+ err = grub_elf64_phdr_iterate (elf, grub_bsd_elf64_size_hook, NULL);
+ if (err)
+ return err;
+
+ grub_dprintf ("bsd", "kern_start = %lx, kern_end = %lx\n",
+ (unsigned long) kern_start, (unsigned long) kern_end);
+ {
+ grub_relocator_chunk_t ch;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start,
+ kern_end - kern_start);
+ if (err)
+ return err;
+ kern_chunk_src = get_virtual_current_address (ch);
+ }
+
+ err = grub_elf64_load (elf, grub_bsd_elf64_hook, 0, 0);
+ if (err)
+ return err;
+ if (kernel_type != KERNEL_TYPE_OPENBSD)
+ return GRUB_ERR_NONE;
+ return grub_openbsd_find_ramdisk64 (elf->file, kern_start,
+ kern_chunk_src, &openbsd_ramdisk);
+ }
+ else
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+}
+
+static grub_err_t
+grub_bsd_load (int argc, char *argv[])
+{
+ grub_file_t file;
+ grub_elf_t elf;
+
+ grub_dl_ref (my_mod);
+
+ grub_loader_unset ();
+
+ grub_memset (&openbsd_ramdisk, 0, sizeof (openbsd_ramdisk));
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0]);
+ if (!file)
+ goto fail;
+
+ relocator = grub_relocator_new ();
+
+ elf = grub_elf_file (file);
+ if (elf)
+ {
+ is_elf_kernel = 1;
+ grub_bsd_load_elf (elf);
+ grub_elf_close (elf);
+ }
+ else
+ {
+ is_elf_kernel = 0;
+ grub_errno = 0;
+ grub_bsd_load_aout (file);
+ grub_file_close (file);
+ }
+
+ kern_end = ALIGN_PAGE (kern_end);
+
+fail:
+
+ if (grub_errno != GRUB_ERR_NONE)
+ grub_dl_unref (my_mod);
+
+ return grub_errno;
+}
+
+static grub_uint32_t
+grub_bsd_parse_flags (const struct grub_arg_list *state,
+ const grub_uint32_t * flags)
+{
+ grub_uint32_t result = 0;
+ unsigned i;
+
+ for (i = 0; flags[i]; i++)
+ if (state[i].set)
+ result |= flags[i];
+
+ return result;
+}
+
+static grub_err_t
+grub_cmd_freebsd (grub_extcmd_context_t ctxt, int argc, char *argv[])
+{
+ kernel_type = KERNEL_TYPE_FREEBSD;
+ bootflags = grub_bsd_parse_flags (ctxt->state, freebsd_flags);
+
+ if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
+ {
+ grub_uint32_t unit, slice, part;
+
+ kern_end = ALIGN_PAGE (kern_end);
+ if (is_elf_kernel)
+ {
+ grub_err_t err;
+ grub_uint64_t data = 0;
+ grub_file_t file;
+ int len = is_64bit ? 8 : 4;
+
+ err = grub_freebsd_add_meta_module (argv[0], is_64bit
+ ? FREEBSD_MODTYPE_KERNEL64
+ : FREEBSD_MODTYPE_KERNEL,
+ argc - 1, argv + 1,
+ kern_start,
+ kern_end - kern_start);
+ if (err)
+ return err;
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ return grub_errno;
+
+ if (is_64bit)
+ err = grub_freebsd_load_elf_meta64 (relocator, file, &kern_end);
+ else
+ err = grub_freebsd_load_elf_meta32 (relocator, file, &kern_end);
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_HOWTO, &data, 4);
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_ENVP, &data, len);
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_KERNEND, &data, len);
+ if (err)
+ return err;
+ }
+ grub_bsd_get_device (&freebsd_biosdev, &unit, &slice, &part);
+ freebsd_bootdev = (FREEBSD_B_DEVMAGIC + ((slice + 1) << FREEBSD_B_SLICESHIFT) +
+ (unit << FREEBSD_B_UNITSHIFT) + (part << FREEBSD_B_PARTSHIFT));
+
+ grub_loader_set (grub_freebsd_boot, grub_bsd_unload, 0);
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_openbsd (grub_extcmd_context_t ctxt, int argc, char *argv[])
+{
+ grub_uint32_t bootdev;
+
+ kernel_type = KERNEL_TYPE_OPENBSD;
+ bootflags = grub_bsd_parse_flags (ctxt->state, openbsd_flags);
+
+ if (ctxt->state[OPENBSD_ROOT_ARG].set)
+ {
+ const char *arg = ctxt->state[OPENBSD_ROOT_ARG].arg;
+ int unit, part;
+ if (*(arg++) != 'w' || *(arg++) != 'd')
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only device specifications of form "
+ "wd<number><lowercase letter> are supported");
+
+ unit = grub_strtoul (arg, (char **) &arg, 10);
+ if (! (arg && *arg >= 'a' && *arg <= 'z'))
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only device specifications of form "
+ "wd<number><lowercase letter> are supported");
+
+ part = *arg - 'a';
+
+ bootdev = (OPENBSD_B_DEVMAGIC + (unit << OPENBSD_B_UNITSHIFT) +
+ (part << OPENBSD_B_PARTSHIFT));
+ }
+ else
+ bootdev = 0;
+
+ if (ctxt->state[OPENBSD_SERIAL_ARG].set)
+ {
+ struct grub_openbsd_bootarg_console serial;
+ char *ptr;
+ unsigned port = 0;
+ unsigned speed = 9600;
+
+ grub_memset (&serial, 0, sizeof (serial));
+
+ if (ctxt->state[OPENBSD_SERIAL_ARG].arg)
+ {
+ ptr = ctxt->state[OPENBSD_SERIAL_ARG].arg;
+ if (grub_memcmp (ptr, "com", sizeof ("com") - 1) != 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only com0-com3 are supported");
+ ptr += sizeof ("com") - 1;
+ port = grub_strtoul (ptr, &ptr, 0);
+ if (port >= 4)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only com0-com3 are supported");
+ if (*ptr == ',')
+ {
+ ptr++;
+ speed = grub_strtoul (ptr, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+ }
+ }
+
+ serial.device = (GRUB_OPENBSD_COM_MAJOR << 8) | port;
+ serial.speed = speed;
+
+ grub_bsd_add_meta (OPENBSD_BOOTARG_CONSOLE, &serial, sizeof (serial));
+ bootflags |= OPENBSD_RB_SERCONS;
+ }
+ else
+ {
+ struct grub_openbsd_bootarg_console serial;
+
+ grub_memset (&serial, 0, sizeof (serial));
+ serial.device = (GRUB_OPENBSD_VGA_MAJOR << 8);
+ grub_bsd_add_meta (OPENBSD_BOOTARG_CONSOLE, &serial, sizeof (serial));
+ bootflags &= ~OPENBSD_RB_SERCONS;
+ }
+
+ if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
+ {
+ grub_loader_set (grub_openbsd_boot, grub_bsd_unload, 0);
+ openbsd_root = bootdev;
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_netbsd (grub_extcmd_context_t ctxt, int argc, char *argv[])
+{
+ grub_err_t err;
+ kernel_type = KERNEL_TYPE_NETBSD;
+ bootflags = grub_bsd_parse_flags (ctxt->state, netbsd_flags);
+
+ if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
+ {
+ if (is_elf_kernel)
+ {
+ grub_file_t file;
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ return grub_errno;
+
+ if (is_64bit)
+ err = grub_netbsd_load_elf_meta64 (relocator, file, &kern_end);
+ else
+ err = grub_netbsd_load_elf_meta32 (relocator, file, &kern_end);
+ if (err)
+ return err;
+ }
+
+ {
+ char bootpath[GRUB_NETBSD_MAX_BOOTPATH_LEN];
+ char *name;
+ name = grub_strrchr (argv[0], '/');
+ if (name)
+ name++;
+ else
+ name = argv[0];
+ grub_memset (bootpath, 0, sizeof (bootpath));
+ grub_strncpy (bootpath, name, sizeof (bootpath) - 1);
+ grub_bsd_add_meta (NETBSD_BTINFO_BOOTPATH, bootpath, sizeof (bootpath));
+ }
+
+ if (ctxt->state[NETBSD_ROOT_ARG].set)
+ {
+ char root[GRUB_NETBSD_MAX_ROOTDEVICE_LEN];
+ grub_memset (root, 0, sizeof (root));
+ grub_strncpy (root, ctxt->state[NETBSD_ROOT_ARG].arg,
+ sizeof (root) - 1);
+ grub_bsd_add_meta (NETBSD_BTINFO_ROOTDEVICE, root, sizeof (root));
+ }
+ if (ctxt->state[NETBSD_SERIAL_ARG].set)
+ {
+ struct grub_netbsd_btinfo_serial serial;
+ char *ptr;
+
+ grub_memset (&serial, 0, sizeof (serial));
+ grub_strcpy (serial.devname, "com");
+
+ serial.addr = grub_ns8250_hw_get_port (0);
+ serial.speed = 9600;
+
+ if (ctxt->state[NETBSD_SERIAL_ARG].arg)
+ {
+ ptr = ctxt->state[NETBSD_SERIAL_ARG].arg;
+ if (grub_memcmp (ptr, "com", sizeof ("com") - 1) == 0)
+ {
+ ptr += sizeof ("com") - 1;
+ serial.addr
+ = grub_ns8250_hw_get_port (grub_strtoul (ptr, &ptr, 0));
+ }
+ else
+ serial.addr = grub_strtoul (ptr, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+
+ if (*ptr == ',')
+ {
+ ptr++;
+ serial.speed = grub_strtoul (ptr, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+ }
+ }
+
+ grub_bsd_add_meta (NETBSD_BTINFO_CONSOLE, &serial, sizeof (serial));
+ }
+ else
+ {
+ struct grub_netbsd_btinfo_serial cons;
+
+ grub_memset (&cons, 0, sizeof (cons));
+ grub_strcpy (cons.devname, "pc");
+
+ grub_bsd_add_meta (NETBSD_BTINFO_CONSOLE, &cons, sizeof (cons));
+ }
+
+ grub_loader_set (grub_netbsd_boot, grub_bsd_unload, 0);
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_freebsd_loadenv (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ char *buf = 0, *curr, *next;
+ int len;
+
+ if (! grub_loader_is_loaded ())
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "you need to load the kernel first");
+
+ if (kernel_type != KERNEL_TYPE_FREEBSD)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only FreeBSD supports environment");
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no filename");
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0]);
+ if ((!file) || (!file->size))
+ goto fail;
+
+ len = file->size;
+ buf = grub_malloc (len + 1);
+ if (!buf)
+ goto fail;
+
+ if (grub_file_read (file, buf, len) != len)
+ goto fail;
+
+ buf[len] = 0;
+
+ next = buf;
+ while (next)
+ {
+ char *p;
+
+ curr = next;
+ next = grub_strchr (curr, '\n');
+ if (next)
+ {
+
+ p = next - 1;
+ while (p > curr)
+ {
+ if ((*p != '\r') && (*p != ' ') && (*p != '\t'))
+ break;
+ p--;
+ }
+
+ if ((p > curr) && (*p == '"'))
+ p--;
+
+ *(p + 1) = 0;
+ next++;
+ }
+
+ if (*curr == '#')
+ continue;
+
+ p = grub_strchr (curr, '=');
+ if (!p)
+ continue;
+
+ *(p++) = 0;
+
+ if (*curr)
+ {
+ char *name;
+
+ if (*p == '"')
+ p++;
+
+ name = grub_xasprintf ("kFreeBSD.%s", curr);
+ if (!name)
+ goto fail;
+ if (grub_env_set (name, p))
+ {
+ grub_free (name);
+ goto fail;
+ }
+ grub_free (name);
+ }
+ }
+
+fail:
+ grub_free (buf);
+
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ int modargc;
+ char **modargv;
+ char *type;
+ grub_err_t err;
+ void *src;
+
+ if (kernel_type != KERNEL_TYPE_FREEBSD)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no FreeBSD loaded");
+
+ if (!is_elf_kernel)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only ELF kernel supports module");
+
+ /* List the current modules if no parameter. */
+ if (!argc)
+ {
+ grub_freebsd_list_modules ();
+ return 0;
+ }
+
+ file = grub_file_open (argv[0]);
+ if ((!file) || (!file->size))
+ goto fail;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_end,
+ file->size);
+ if (err)
+ goto fail;
+ src = get_virtual_current_address (ch);
+ }
+
+
+ grub_file_read (file, src, file->size);
+ if (grub_errno)
+ goto fail;
+
+ modargc = argc - 1;
+ modargv = argv + 1;
+
+ if (modargc && (! grub_memcmp (modargv[0], "type=", 5)))
+ {
+ type = &modargv[0][5];
+ modargc--;
+ modargv++;
+ }
+ else
+ type = FREEBSD_MODTYPE_RAW;
+
+ err = grub_freebsd_add_meta_module (argv[0], type, modargc, modargv,
+ kern_end, file->size);
+ if (err)
+ goto fail;
+
+ kern_end = ALIGN_PAGE (kern_end + file->size);
+
+fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_netbsd_module_load (char *filename, grub_uint32_t type)
+{
+ grub_file_t file = 0;
+ void *src;
+ grub_err_t err;
+
+ file = grub_file_open (filename);
+ if ((!file) || (!file->size))
+ goto fail;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_end,
+ file->size);
+ if (err)
+ goto fail;
+
+ src = get_virtual_current_address (ch);
+ }
+
+ grub_file_read (file, src, file->size);
+ if (grub_errno)
+ goto fail;
+
+ err = grub_netbsd_add_meta_module (filename, type, kern_end, file->size);
+
+ if (err)
+ goto fail;
+
+ kern_end = ALIGN_PAGE (kern_end + file->size);
+
+fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_netbsd_module (grub_command_t cmd,
+ int argc, char *argv[])
+{
+ grub_uint32_t type;
+
+ if (kernel_type != KERNEL_TYPE_NETBSD)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no NetBSD loaded");
+
+ if (!is_elf_kernel)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only ELF kernel supports module");
+
+ /* List the current modules if no parameter. */
+ if (!argc)
+ {
+ grub_netbsd_list_modules ();
+ return 0;
+ }
+
+ if (grub_strcmp (cmd->name, "knetbsd_module_elf") == 0)
+ type = GRUB_NETBSD_MODULE_ELF;
+ else
+ type = GRUB_NETBSD_MODULE_RAW;
+
+ return grub_netbsd_module_load (argv[0], type);
+}
+
+static grub_err_t
+grub_cmd_freebsd_module_elf (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_err_t err;
+
+ if (! grub_loader_is_loaded ())
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "you need to load the kernel first");
+
+ if (kernel_type != KERNEL_TYPE_FREEBSD)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only FreeBSD supports module");
+
+ if (! is_elf_kernel)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only ELF kernel supports module");
+
+ /* List the current modules if no parameter. */
+ if (! argc)
+ {
+ grub_freebsd_list_modules ();
+ return 0;
+ }
+
+ file = grub_file_open (argv[0]);
+ if (!file)
+ return grub_errno;
+ if (!file->size)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+
+ if (is_64bit)
+ err = grub_freebsd_load_elfmodule_obj64 (relocator, file,
+ argc, argv, &kern_end);
+ else
+ err = grub_freebsd_load_elfmodule32 (relocator, file,
+ argc, argv, &kern_end);
+ grub_file_close (file);
+
+ return err;
+}
+
+static grub_err_t
+grub_cmd_openbsd_ramdisk (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ grub_size_t size;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ if (kernel_type != KERNEL_TYPE_OPENBSD)
+ return grub_error (GRUB_ERR_BAD_OS, "no kOpenBSD loaded");
+
+ if (!openbsd_ramdisk.max_size)
+ return grub_error (GRUB_ERR_BAD_OS, "your kOpenBSD doesn't support ramdisk");
+
+ file = grub_file_open (args[0]);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "couldn't load ramdisk");
+
+ size = grub_file_size (file);
+
+ if (size > openbsd_ramdisk.max_size)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS, "your kOpenBSD supports ramdisk only"
+ " up to %u bytes, however you supplied a %u bytes one",
+ openbsd_ramdisk.max_size, size);
+ }
+
+ if (grub_file_read (file, openbsd_ramdisk.target, size)
+ != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
+ }
+ grub_memset (openbsd_ramdisk.target + size, 0,
+ openbsd_ramdisk.max_size - size);
+ *openbsd_ramdisk.size = ALIGN_UP (size, 512);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_extcmd_t cmd_freebsd, cmd_openbsd, cmd_netbsd;
+static grub_command_t cmd_freebsd_loadenv, cmd_freebsd_module;
+static grub_command_t cmd_netbsd_module, cmd_freebsd_module_elf;
+static grub_command_t cmd_netbsd_module_elf, cmd_openbsd_ramdisk;
+
+GRUB_MOD_INIT (bsd)
+{
+ /* Net and OpenBSD kernels are often compressed. */
+ grub_dl_load ("gzio");
+
+ cmd_freebsd = grub_register_extcmd ("kfreebsd", grub_cmd_freebsd, 0,
+ N_("FILE"), N_("Load kernel of FreeBSD."),
+ freebsd_opts);
+ cmd_openbsd = grub_register_extcmd ("kopenbsd", grub_cmd_openbsd, 0,
+ N_("FILE"), N_("Load kernel of OpenBSD."),
+ openbsd_opts);
+ cmd_netbsd = grub_register_extcmd ("knetbsd", grub_cmd_netbsd, 0,
+ N_("FILE"), N_("Load kernel of NetBSD."),
+ netbsd_opts);
+ cmd_freebsd_loadenv =
+ grub_register_command ("kfreebsd_loadenv", grub_cmd_freebsd_loadenv,
+ 0, N_("Load FreeBSD env."));
+ cmd_freebsd_module =
+ grub_register_command ("kfreebsd_module", grub_cmd_freebsd_module,
+ 0, N_("Load FreeBSD kernel module."));
+ cmd_netbsd_module =
+ grub_register_command ("knetbsd_module", grub_cmd_netbsd_module,
+ 0, N_("Load NetBSD kernel module."));
+ cmd_netbsd_module_elf =
+ grub_register_command ("knetbsd_module_elf", grub_cmd_netbsd_module,
+ 0, N_("Load NetBSD kernel module (ELF)."));
+ cmd_freebsd_module_elf =
+ grub_register_command ("kfreebsd_module_elf", grub_cmd_freebsd_module_elf,
+ 0, N_("Load FreeBSD kernel module (ELF)."));
+
+ cmd_openbsd_ramdisk = grub_register_command ("kopenbsd_ramdisk",
+ grub_cmd_openbsd_ramdisk, 0,
+ "Load kOpenBSD ramdisk. ");
+
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI (bsd)
+{
+ grub_unregister_extcmd (cmd_freebsd);
+ grub_unregister_extcmd (cmd_openbsd);
+ grub_unregister_extcmd (cmd_netbsd);
+
+ grub_unregister_command (cmd_freebsd_loadenv);
+ grub_unregister_command (cmd_freebsd_module);
+ grub_unregister_command (cmd_netbsd_module);
+ grub_unregister_command (cmd_freebsd_module_elf);
+ grub_unregister_command (cmd_netbsd_module_elf);
+ grub_unregister_command (cmd_openbsd_ramdisk);
+
+ grub_bsd_unload ();
+}
diff --git a/grub-core/loader/i386/bsd32.c b/grub-core/loader/i386/bsd32.c
new file mode 100644
index 0000000..26704c4
--- /dev/null
+++ b/grub-core/loader/i386/bsd32.c
@@ -0,0 +1,6 @@
+#define SUFFIX(x) x ## 32
+#define GRUB_TARGET_WORDSIZE 32
+#define OBJSYM 0
+#include <grub/types.h>
+typedef grub_uint32_t grub_freebsd_addr_t;
+#include "bsdXX.c"
diff --git a/grub-core/loader/i386/bsd64.c b/grub-core/loader/i386/bsd64.c
new file mode 100644
index 0000000..f8aad1c
--- /dev/null
+++ b/grub-core/loader/i386/bsd64.c
@@ -0,0 +1,6 @@
+#define SUFFIX(x) x ## 64
+#define GRUB_TARGET_WORDSIZE 64
+#define OBJSYM 1
+#include <grub/types.h>
+typedef grub_uint64_t grub_freebsd_addr_t;
+#include "bsdXX.c"
diff --git a/grub-core/loader/i386/bsdXX.c b/grub-core/loader/i386/bsdXX.c
new file mode 100644
index 0000000..92d2675
--- /dev/null
+++ b/grub-core/loader/i386/bsdXX.c
@@ -0,0 +1,621 @@
+#include <grub/loader.h>
+#include <grub/i386/bsd.h>
+#include <grub/mm.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/i386/relocator.h>
+
+#define ALIGN_PAGE(a) ALIGN_UP (a, 4096)
+
+static inline grub_err_t
+load (grub_file_t file, void *where, grub_off_t off, grub_size_t size)
+{
+ if (grub_file_seek (file, off) == (grub_off_t) -1)
+ return grub_errno;
+ if (grub_file_read (file, where, size) != (grub_ssize_t) size)
+ {
+ if (grub_errno)
+ return grub_errno;
+ else
+ return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
+ }
+ return GRUB_ERR_NONE;
+}
+
+static inline grub_err_t
+read_headers (grub_file_t file, Elf_Ehdr *e, char **shdr)
+{
+ if (grub_file_seek (file, 0) == (grub_off_t) -1)
+ return grub_errno;
+
+ if (grub_file_read (file, (char *) e, sizeof (*e)) != sizeof (*e))
+ {
+ if (grub_errno)
+ return grub_errno;
+ else
+ return grub_error (GRUB_ERR_BAD_OS, "file is too short");
+ }
+
+ if (e->e_ident[EI_MAG0] != ELFMAG0
+ || e->e_ident[EI_MAG1] != ELFMAG1
+ || e->e_ident[EI_MAG2] != ELFMAG2
+ || e->e_ident[EI_MAG3] != ELFMAG3
+ || e->e_ident[EI_VERSION] != EV_CURRENT
+ || e->e_version != EV_CURRENT)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
+
+ if (e->e_ident[EI_CLASS] != SUFFIX (ELFCLASS))
+ return grub_error (GRUB_ERR_BAD_OS, "invalid arch dependent ELF magic");
+
+ *shdr = grub_malloc (e->e_shnum * e->e_shentsize);
+ if (! *shdr)
+ return grub_errno;
+
+ if (grub_file_seek (file, e->e_shoff) == (grub_off_t) -1)
+ return grub_errno;
+
+ if (grub_file_read (file, *shdr, e->e_shnum * e->e_shentsize)
+ != e->e_shnum * e->e_shentsize)
+ {
+ if (grub_errno)
+ return grub_errno;
+ else
+ return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/* On i386 FreeBSD uses "elf module" approarch for 32-bit variant
+ and "elf obj module" for 64-bit variant. However it may differ on other
+ platforms. So I keep both versions. */
+#if OBJSYM
+grub_err_t
+SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator,
+ grub_file_t file, int argc,
+ char *argv[], grub_addr_t *kern_end)
+{
+ Elf_Ehdr e;
+ Elf_Shdr *s;
+ char *shdr = 0;
+ grub_addr_t curload, module;
+ grub_err_t err;
+ grub_size_t chunk_size = 0;
+ void *chunk_src;
+
+ err = read_headers (file, &e, &shdr);
+ if (err)
+ return err;
+
+ curload = module = ALIGN_PAGE (*kern_end);
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ if (s->sh_size == 0)
+ continue;
+
+ if (s->sh_addralign)
+ chunk_size = ALIGN_UP (chunk_size + *kern_end, s->sh_addralign)
+ - *kern_end;
+
+ chunk_size += s->sh_size;
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ module, chunk_size);
+ if (err)
+ return err;
+ chunk_src = get_virtual_current_address (ch);
+ }
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ if (s->sh_size == 0)
+ continue;
+
+ if (s->sh_addralign)
+ curload = ALIGN_UP (curload, s->sh_addralign);
+ s->sh_addr = curload;
+
+ grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n",
+ (unsigned) curload, (int) s->sh_size,
+ (int) s->sh_addralign);
+
+ switch (s->sh_type)
+ {
+ default:
+ case SHT_PROGBITS:
+ err = load (file, (grub_uint8_t *) chunk_src + curload - *kern_end,
+ s->sh_offset, s->sh_size);
+ if (err)
+ return err;
+ break;
+ case SHT_NOBITS:
+ grub_memset ((grub_uint8_t *) chunk_src + curload - *kern_end, 0,
+ s->sh_size);
+ break;
+ }
+ curload += s->sh_size;
+ }
+
+ *kern_end = ALIGN_PAGE (curload);
+
+ err = grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE_OBJ,
+ argc - 1, argv + 1, module,
+ curload - module);
+ if (! err)
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA
+ | FREEBSD_MODINFOMD_ELFHDR,
+ &e, sizeof (e));
+ if (! err)
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA
+ | FREEBSD_MODINFOMD_SHDR,
+ shdr, e.e_shnum * e.e_shentsize);
+
+ return err;
+}
+
+#else
+
+grub_err_t
+SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator,
+ grub_file_t file, int argc, char *argv[],
+ grub_addr_t *kern_end)
+{
+ Elf_Ehdr e;
+ Elf_Shdr *s;
+ char *shdr = 0;
+ grub_addr_t curload, module;
+ grub_err_t err;
+ grub_size_t chunk_size = 0;
+ void *chunk_src;
+
+ err = read_headers (file, &e, &shdr);
+ if (err)
+ return err;
+
+ curload = module = ALIGN_PAGE (*kern_end);
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ if (s->sh_size == 0)
+ continue;
+
+ if (! (s->sh_flags & SHF_ALLOC))
+ continue;
+ if (chunk_size < s->sh_addr + s->sh_size)
+ chunk_size = s->sh_addr + s->sh_size;
+ }
+
+ if (chunk_size < sizeof (e))
+ chunk_size = sizeof (e);
+ chunk_size += e.e_phnum * e.e_phentsize;
+ chunk_size += e.e_shnum * e.e_shentsize;
+
+ {
+ grub_relocator_chunk_t ch;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ module, chunk_size);
+ if (err)
+ return err;
+
+ chunk_src = get_virtual_current_address (ch);
+ }
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ if (s->sh_size == 0)
+ continue;
+
+ if (! (s->sh_flags & SHF_ALLOC))
+ continue;
+
+ grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n",
+ (unsigned) curload, (int) s->sh_size,
+ (int) s->sh_addralign);
+
+ switch (s->sh_type)
+ {
+ default:
+ case SHT_PROGBITS:
+ err = load (file, (grub_uint8_t *) chunk_src + module
+ + s->sh_addr - *kern_end,
+ s->sh_offset, s->sh_size);
+ if (err)
+ return err;
+ break;
+ case SHT_NOBITS:
+ grub_memset ((grub_uint8_t *) chunk_src + module
+ + s->sh_addr - *kern_end, 0, s->sh_size);
+ break;
+ }
+ if (curload < module + s->sh_addr + s->sh_size)
+ curload = module + s->sh_addr + s->sh_size;
+ }
+
+ load (file, UINT_TO_PTR (module), 0, sizeof (e));
+ if (curload < module + sizeof (e))
+ curload = module + sizeof (e);
+
+ load (file, UINT_TO_PTR (curload), e.e_shoff,
+ e.e_shnum * e.e_shentsize);
+ e.e_shoff = curload - module;
+ curload += e.e_shnum * e.e_shentsize;
+
+ load (file, UINT_TO_PTR (curload), e.e_phoff,
+ e.e_phnum * e.e_phentsize);
+ e.e_phoff = curload - module;
+ curload += e.e_phnum * e.e_phentsize;
+
+ *kern_end = curload;
+
+ 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) (relocator, file, kern_end);
+}
+
+#endif
+
+grub_err_t
+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 = 0;
+ unsigned symoff, stroff, symsize, strsize;
+ 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)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_ELFHDR, &e,
+ sizeof (e));
+ if (err)
+ return err;
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+ if (s >= (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize))
+ return grub_error (GRUB_ERR_BAD_OS, "no symbol table");
+ symoff = s->sh_offset;
+ symsize = s->sh_size;
+ symentsize = s->sh_entsize;
+ s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
+ stroff = s->sh_offset;
+ strsize = s->sh_size;
+
+ chunk_size = ALIGN_UP (symsize + strsize, sizeof (grub_freebsd_addr_t))
+ + 2 * sizeof (grub_freebsd_addr_t);
+
+ symtarget = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t));
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ symtarget, chunk_size);
+ if (err)
+ return err;
+ sym_chunk = get_virtual_current_address (ch);
+ }
+
+ 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 *) curload;
+ if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize)
+ {
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+ curload += symsize;
+
+ *((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 *) 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;
+ }
+
+ for (i = 0;
+ i * symentsize < symsize;
+ i++, sym = (Elf_Sym *) ((char *) sym + symentsize))
+ {
+ const char *name = str + sym->st_name;
+ if (grub_strcmp (name, "_DYNAMIC") == 0)
+ break;
+ }
+
+ if (i * symentsize < symsize)
+ {
+ dynamic = sym->st_value;
+ grub_dprintf ("bsd", "dynamic = %llx\n", (unsigned long long) dynamic);
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_DYNAMIC, &dynamic,
+ sizeof (dynamic));
+ if (err)
+ return err;
+ }
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_SSYM, &symstart,
+ sizeof (symstart));
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_ESYM, &symend,
+ sizeof (symend));
+ if (err)
+ return err;
+
+ *kern_end = ALIGN_PAGE (symend);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+SUFFIX (grub_netbsd_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, *symsh, *strsh;
+ char *shdr = NULL;
+ unsigned symsize, strsize;
+ void *sym_chunk;
+ grub_uint8_t *curload;
+ grub_size_t chunk_size;
+ Elf_Ehdr *e2;
+ struct grub_netbsd_btinfo_symtab symtab;
+ grub_addr_t symtarget;
+
+ err = read_headers (file, &e, &shdr);
+ if (err)
+ return err;
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+ if (s >= (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize))
+ return GRUB_ERR_NONE;
+ symsize = s->sh_size;
+ symsh = s;
+ s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
+ strsize = s->sh_size;
+ strsh = s;
+
+ chunk_size = ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t))
+ + ALIGN_UP (strsize, sizeof (grub_freebsd_addr_t))
+ + sizeof (e) + e.e_shnum * e.e_shentsize;
+
+ symtarget = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t));
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ symtarget, chunk_size);
+ if (err)
+ return err;
+ sym_chunk = get_virtual_current_address (ch);
+ }
+
+ symtab.nsyms = 1;
+ symtab.ssyms = symtarget;
+ symtab.esyms = symtarget + chunk_size;
+
+ curload = sym_chunk;
+
+ e2 = (Elf_Ehdr *) curload;
+ grub_memcpy (curload, &e, sizeof (e));
+ e2->e_phoff = 0;
+ e2->e_phnum = 0;
+ e2->e_phentsize = 0;
+ e2->e_shstrndx = 0;
+ e2->e_shoff = sizeof (e);
+
+ curload += sizeof (e);
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ Elf_Shdr *s2;
+ s2 = (Elf_Shdr *) curload;
+ grub_memcpy (curload, s, e.e_shentsize);
+ if (s == symsh)
+ s2->sh_offset = sizeof (e) + e.e_shnum * e.e_shentsize;
+ else if (s == strsh)
+ s2->sh_offset = ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t))
+ + sizeof (e) + e.e_shnum * e.e_shentsize;
+ else
+ s2->sh_offset = 0;
+ s2->sh_addr = s2->sh_offset;
+ curload += e.e_shentsize;
+ }
+
+ if (grub_file_seek (file, symsh->sh_offset) == (grub_off_t) -1)
+ return grub_errno;
+ if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize)
+ {
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+ curload += ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t));
+
+ if (grub_file_seek (file, strsh->sh_offset) == (grub_off_t) -1)
+ return grub_errno;
+ 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;
+ }
+
+ err = grub_bsd_add_meta (NETBSD_BTINFO_SYMTAB,
+ &symtab,
+ sizeof (symtab));
+ if (err)
+ return err;
+
+ *kern_end = ALIGN_PAGE (symtarget + chunk_size);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+SUFFIX(grub_openbsd_find_ramdisk) (grub_file_t file,
+ grub_addr_t kern_start,
+ void *kern_chunk_src,
+ struct grub_openbsd_ramdisk_descriptor *desc)
+{
+ unsigned symoff, stroff, symsize, strsize, symentsize;
+
+ {
+ grub_err_t err;
+ Elf_Ehdr e;
+ Elf_Shdr *s;
+ char *shdr = NULL;
+
+ err = read_headers (file, &e, &shdr);
+ if (err)
+ return err;
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+ if (s >= (Elf_Shdr *) ((char *) shdr + e.e_shnum * e.e_shentsize))
+ {
+ grub_free (shdr);
+ return GRUB_ERR_NONE;
+ }
+
+ symsize = s->sh_size;
+ symentsize = s->sh_entsize;
+ symoff = s->sh_offset;
+
+ s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
+ stroff = s->sh_offset;
+ strsize = s->sh_size;
+ grub_free (shdr);
+ }
+ {
+ Elf_Sym *syms, *sym, *imagesym = NULL, *sizesym = NULL;
+ unsigned i;
+ char *strs;
+
+ syms = grub_malloc (symsize);
+ if (!syms)
+ return grub_errno;
+
+ if (grub_file_seek (file, symoff) == (grub_off_t) -1)
+ {
+ grub_free (syms);
+ return grub_errno;
+ }
+ if (grub_file_read (file, syms, symsize) != (grub_ssize_t) symsize)
+ {
+ grub_free (syms);
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+
+ strs = grub_malloc (strsize);
+ if (!strs)
+ {
+ grub_free (syms);
+ return grub_errno;
+ }
+
+ if (grub_file_seek (file, stroff) == (grub_off_t) -1)
+ return grub_errno;
+ if (grub_file_read (file, strs, strsize) != (grub_ssize_t) strsize)
+ {
+ grub_free (syms);
+ grub_free (strs);
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+
+ for (i = 0, sym = syms; i < symsize / symentsize;
+ i++, sym = (Elf_Sym *) ((char *) sym + symentsize))
+ {
+ if (ELF_ST_TYPE (sym->st_info) != STT_OBJECT)
+ continue;
+ if (!sym->st_name)
+ continue;
+ if (grub_strcmp (strs + sym->st_name, "rd_root_image") == 0)
+ imagesym = sym;
+ if (grub_strcmp (strs + sym->st_name, "rd_root_size") == 0)
+ sizesym = sym;
+ if (imagesym && sizesym)
+ break;
+ }
+ if (!imagesym || !sizesym)
+ {
+ grub_free (syms);
+ grub_free (strs);
+ return GRUB_ERR_NONE;
+ }
+ if (sizeof (*desc->size) != sizesym->st_size)
+ {
+ grub_free (syms);
+ grub_free (strs);
+ return grub_error (GRUB_ERR_BAD_OS, "unexpected size of rd_root_size");
+ }
+ desc->max_size = imagesym->st_size;
+ desc->target = (imagesym->st_value & 0xFFFFFF) - kern_start
+ + (grub_uint8_t *) kern_chunk_src;
+ desc->size = (grub_uint32_t *) ((sizesym->st_value & 0xFFFFFF) - kern_start
+ + (grub_uint8_t *) kern_chunk_src);
+ grub_free (syms);
+ grub_free (strs);
+
+ return GRUB_ERR_NONE;
+ }
+}
diff --git a/grub-core/loader/i386/bsd_pagetable.c b/grub-core/loader/i386/bsd_pagetable.c
new file mode 100644
index 0000000..9ec5abf
--- /dev/null
+++ b/grub-core/loader/i386/bsd_pagetable.c
@@ -0,0 +1,92 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* Based on the code from FreeBSD originally distributed under the
+ following terms: */
+
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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$
+ */
+
+
+static void
+fill_bsd64_pagetable (grub_uint8_t *src, grub_addr_t target)
+{
+ grub_uint64_t *pt2, *pt3, *pt4;
+ grub_addr_t pt2t, pt3t;
+ int i;
+
+#define PG_V 0x001
+#define PG_RW 0x002
+#define PG_U 0x004
+#define PG_PS 0x080
+
+ pt4 = (grub_uint64_t *) src;
+ pt3 = (grub_uint64_t *) (src + 4096);
+ pt2 = (grub_uint64_t *) (src + 8192);
+
+ 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
+ * 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) 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) pt2t;
+ 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/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
new file mode 100644
index 0000000..f19f471
--- /dev/null
+++ b/grub-core/loader/i386/linux.c
@@ -0,0 +1,1016 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,2007,2008,2009,2010 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/memory.h>
+#include <grub/normal.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/term.h>
+#include <grub/cpu/linux.h>
+#include <grub/video.h>
+#include <grub/video_fb.h>
+#include <grub/command.h>
+#include <grub/i386/relocator.h>
+#include <grub/i18n.h>
+#include <grub/lib/cmdline.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/i386/pc/vesa_modes_table.h>
+#endif
+
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#define HAS_VGA_TEXT 0
+#define DEFAULT_VIDEO_MODE "auto"
+#elif defined (GRUB_MACHINE_IEEE1275)
+#include <grub/ieee1275/ieee1275.h>
+#define HAS_VGA_TEXT 0
+#define DEFAULT_VIDEO_MODE "text"
+#else
+#include <grub/i386/pc/vbe.h>
+#include <grub/i386/pc/console.h>
+#define HAS_VGA_TEXT 1
+#define DEFAULT_VIDEO_MODE "text"
+#endif
+
+#define GRUB_LINUX_CL_OFFSET 0x1000
+#define GRUB_LINUX_CL_END_OFFSET 0x2000
+
+static grub_dl_t my_mod;
+
+static grub_size_t linux_mem_size;
+static int loaded;
+static void *real_mode_mem;
+static grub_addr_t real_mode_target;
+static void *prot_mode_mem;
+static grub_addr_t prot_mode_target;
+static void *initrd_mem;
+static grub_addr_t initrd_mem_target;
+static grub_uint32_t real_mode_pages;
+static grub_uint32_t prot_mode_pages;
+static grub_uint32_t initrd_pages;
+static struct grub_relocator *relocator = NULL;
+static void *efi_mmap_buf;
+#ifdef GRUB_MACHINE_EFI
+static grub_efi_uintn_t efi_mmap_size;
+#else
+static const grub_size_t efi_mmap_size = 0;
+#endif
+
+/* FIXME */
+#if 0
+struct idt_descriptor
+{
+ grub_uint16_t limit;
+ void *base;
+} __attribute__ ((packed));
+
+static struct idt_descriptor idt_desc =
+ {
+ 0,
+ 0
+ };
+#endif
+
+static inline grub_size_t
+page_align (grub_size_t size)
+{
+ return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
+}
+
+#ifdef GRUB_MACHINE_EFI
+/* Find the optimal number of pages for the memory map. Is it better to
+ move this code to efi/mm.c? */
+static grub_efi_uintn_t
+find_efi_mmap_size (void)
+{
+ static grub_efi_uintn_t mmap_size = 0;
+
+ if (mmap_size != 0)
+ return mmap_size;
+
+ mmap_size = (1 << 12);
+ while (1)
+ {
+ int ret;
+ grub_efi_memory_descriptor_t *mmap;
+ grub_efi_uintn_t desc_size;
+
+ mmap = grub_malloc (mmap_size);
+ if (! mmap)
+ return 0;
+
+ ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
+ grub_free (mmap);
+
+ if (ret < 0)
+ grub_fatal ("cannot get memory map");
+ else if (ret > 0)
+ break;
+
+ mmap_size += (1 << 12);
+ }
+
+ /* Increase the size a bit for safety, because GRUB allocates more on
+ later, and EFI itself may allocate more. */
+ mmap_size += (1 << 12);
+
+ mmap_size = page_align (mmap_size);
+ return mmap_size;
+}
+
+#endif
+
+/* Find the optimal number of pages for the memory map. */
+static grub_size_t
+find_mmap_size (void)
+{
+ grub_size_t count = 0, mmap_size;
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr __attribute__ ((unused)),
+ grub_uint64_t size __attribute__ ((unused)),
+ grub_memory_type_t type __attribute__ ((unused)))
+ {
+ count++;
+ return 0;
+ }
+
+ grub_mmap_iterate (hook);
+
+ mmap_size = count * sizeof (struct grub_e820_mmap);
+
+ /* Increase the size a bit for safety, because GRUB allocates more on
+ later. */
+ mmap_size += (1 << 12);
+
+ return page_align (mmap_size);
+}
+
+static void
+free_pages (void)
+{
+ grub_relocator_unload (relocator);
+ relocator = NULL;
+ real_mode_mem = prot_mode_mem = initrd_mem = 0;
+ real_mode_target = prot_mode_target = initrd_mem_target = 0;
+}
+
+/* Allocate pages for the real mode code and the protected mode code
+ for linux as well as a memory map buffer. */
+static grub_err_t
+allocate_pages (grub_size_t prot_size)
+{
+ grub_size_t real_size, mmap_size;
+ grub_err_t err;
+
+ /* Make sure that each size is aligned to a page boundary. */
+ real_size = GRUB_LINUX_CL_END_OFFSET;
+ prot_size = page_align (prot_size);
+ mmap_size = find_mmap_size ();
+
+#ifdef GRUB_MACHINE_EFI
+ efi_mmap_size = find_efi_mmap_size ();
+#endif
+
+ grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
+ (unsigned) real_size, (unsigned) prot_size, (unsigned) mmap_size);
+
+ /* Calculate the number of pages; Combine the real mode code with
+ the memory map buffer for simplicity. */
+ real_mode_pages = ((real_size + mmap_size + efi_mmap_size) >> 12);
+ prot_mode_pages = (prot_size >> 12);
+
+ /* Initialize the memory pointers with NULL for convenience. */
+ free_pages ();
+
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ {
+ err = grub_errno;
+ goto fail;
+ }
+
+ /* FIXME: Should request low memory from the heap when this feature is
+ implemented. */
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+ /* We must put real mode code in the traditional space. */
+
+ if (type == GRUB_MEMORY_AVAILABLE
+ && addr <= 0x90000)
+ {
+ if (addr < 0x10000)
+ {
+ size += addr - 0x10000;
+ addr = 0x10000;
+ }
+
+ if (addr + size > 0x90000)
+ size = 0x90000 - addr;
+
+ if (real_size + mmap_size + efi_mmap_size > size)
+ return 0;
+
+ real_mode_target = ((addr + size) - (real_size + mmap_size + efi_mmap_size));
+ return 1;
+ }
+
+ return 0;
+ }
+ grub_mmap_iterate (hook);
+ if (! real_mode_target)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
+ goto fail;
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ real_mode_target,
+ (real_size + mmap_size
+ + efi_mmap_size));
+ if (err)
+ goto fail;
+ real_mode_mem = get_virtual_current_address (ch);
+ }
+ efi_mmap_buf = (grub_uint8_t *) real_mode_mem + real_size + mmap_size;
+
+ prot_mode_target = GRUB_LINUX_BZIMAGE_ADDR;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ prot_mode_target, prot_size);
+ if (err)
+ goto fail;
+ prot_mode_mem = get_virtual_current_address (ch);
+ }
+
+ grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, "
+ "prot_mode_mem = %lx, prot_mode_pages = %x\n",
+ (unsigned long) real_mode_mem, (unsigned) real_mode_pages,
+ (unsigned long) prot_mode_mem, (unsigned) prot_mode_pages);
+
+ return GRUB_ERR_NONE;
+
+ fail:
+ free_pages ();
+ return err;
+}
+
+static void
+grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num,
+ grub_uint64_t start, grub_uint64_t size,
+ grub_uint32_t type)
+{
+ int n = *e820_num;
+
+ if (n >= GRUB_E820_MAX_ENTRY)
+ grub_fatal ("Too many e820 memory map entries");
+
+ if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) &&
+ (e820_map[n - 1].type == type))
+ e820_map[n - 1].size += size;
+ else
+ {
+ e820_map[n].addr = start;
+ e820_map[n].size = size;
+ e820_map[n].type = type;
+ (*e820_num)++;
+ }
+}
+
+static grub_err_t
+grub_linux_setup_video (struct linux_kernel_params *params)
+{
+ struct grub_video_mode_info mode_info;
+ void *framebuffer;
+ grub_err_t err;
+ grub_video_driver_id_t driver_id;
+ char *gfxlfbvar = grub_env_get ("gfxpayloadforcelfb");
+
+ driver_id = grub_video_get_driver_id ();
+
+ if (driver_id == GRUB_VIDEO_DRIVER_NONE)
+ return 1;
+
+ err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+
+ if (err)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ return 1;
+ }
+
+ params->lfb_width = mode_info.width;
+ params->lfb_height = mode_info.height;
+ params->lfb_depth = mode_info.bpp;
+ params->lfb_line_len = mode_info.pitch;
+
+ params->lfb_base = (grub_size_t) framebuffer;
+ params->lfb_size = ALIGN_UP (params->lfb_line_len * params->lfb_height, 65536);
+
+ params->red_mask_size = mode_info.red_mask_size;
+ params->red_field_pos = mode_info.red_field_pos;
+ params->green_mask_size = mode_info.green_mask_size;
+ params->green_field_pos = mode_info.green_field_pos;
+ params->blue_mask_size = mode_info.blue_mask_size;
+ params->blue_field_pos = mode_info.blue_field_pos;
+ params->reserved_mask_size = mode_info.reserved_mask_size;
+ params->reserved_field_pos = mode_info.reserved_field_pos;
+
+ if (gfxlfbvar && (gfxlfbvar[0] == '1' || gfxlfbvar[0] == 'y'))
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
+ else
+ {
+ switch (driver_id)
+ {
+ case GRUB_VIDEO_DRIVER_VBE:
+ params->lfb_size >>= 16;
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_VESA;
+ break;
+
+ case GRUB_VIDEO_DRIVER_EFI_UGA:
+ case GRUB_VIDEO_DRIVER_EFI_GOP:
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_EFIFB;
+ break;
+
+ /* FIXME: check if better id is available. */
+ case GRUB_VIDEO_DRIVER_SM712:
+ case GRUB_VIDEO_DRIVER_VGA:
+ case GRUB_VIDEO_DRIVER_CIRRUS:
+ case GRUB_VIDEO_DRIVER_BOCHS:
+ /* Make gcc happy. */
+ case GRUB_VIDEO_DRIVER_SDL:
+ case GRUB_VIDEO_DRIVER_NONE:
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
+ break;
+ }
+ }
+
+#ifdef GRUB_MACHINE_PCBIOS
+ /* VESA packed modes may come with zeroed mask sizes, which need
+ to be set here according to DAC Palette width. If we don't,
+ this results in Linux displaying a black screen. */
+ if (driver_id == GRUB_VIDEO_DRIVER_VBE && mode_info.bpp <= 8)
+ {
+ struct grub_vbe_info_block controller_info;
+ int status;
+ int width = 8;
+
+ status = grub_vbe_bios_get_controller_info (&controller_info);
+
+ if (status == GRUB_VBE_STATUS_OK &&
+ (controller_info.capabilities & GRUB_VBE_CAPABILITY_DACWIDTH))
+ status = grub_vbe_bios_set_dac_palette_width (&width);
+
+ if (status != GRUB_VBE_STATUS_OK)
+ /* 6 is default after mode reset. */
+ width = 6;
+
+ params->red_mask_size = params->green_mask_size
+ = params->blue_mask_size = width;
+ params->reserved_mask_size = 0;
+ }
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ struct linux_kernel_params *params;
+ int e820_num;
+ grub_err_t err = 0;
+ char *modevar, *tmp;
+ struct grub_relocator32_state state;
+
+ params = real_mode_mem;
+
+#ifdef GRUB_MACHINE_IEEE1275
+ {
+ char *bootpath;
+ grub_ssize_t len;
+
+ bootpath = grub_env_get ("root");
+ if (bootpath)
+ grub_ieee1275_set_property (grub_ieee1275_chosen,
+ "bootpath", bootpath,
+ grub_strlen (bootpath) + 1,
+ &len);
+ }
+#endif
+
+ grub_dprintf ("linux", "code32_start = %x\n",
+ (unsigned) params->code32_start);
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+ switch (type)
+ {
+ case GRUB_MEMORY_AVAILABLE:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_RAM);
+ break;
+
+ case GRUB_MEMORY_ACPI:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_ACPI);
+ break;
+
+ case GRUB_MEMORY_NVS:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_NVS);
+ break;
+
+ case GRUB_MEMORY_BADRAM:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_BADRAM);
+ break;
+
+ default:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_RESERVED);
+ }
+ return 0;
+ }
+
+ e820_num = 0;
+ grub_mmap_iterate (hook);
+ params->mmap_size = e820_num;
+
+ modevar = grub_env_get ("gfxpayload");
+
+ /* Now all graphical modes are acceptable.
+ May change in future if we have modes without framebuffer. */
+ if (modevar && *modevar != 0)
+ {
+ tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
+ if (! tmp)
+ return grub_errno;
+ err = grub_video_set_mode (tmp, 0, 0);
+ grub_free (tmp);
+ }
+ else
+ err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
+
+ if (err)
+ {
+ grub_print_error ();
+ grub_printf ("Booting however\n");
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ if (grub_linux_setup_video (params))
+ {
+#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_TEXT;
+ params->video_mode = 0x3;
+#else
+ params->have_vga = 0;
+ params->video_mode = 0;
+ params->video_width = 0;
+ params->video_height = 0;
+#endif
+ }
+
+ /* Initialize these last, because terminal position could be affected by printfs above. */
+#ifndef GRUB_MACHINE_IEEE1275
+ if (params->have_vga == GRUB_VIDEO_LINUX_TYPE_TEXT)
+#endif
+ {
+ grub_term_output_t term;
+ int found = 0;
+ FOR_ACTIVE_TERM_OUTPUTS(term)
+ if (grub_strcmp (term->name, "vga_text") == 0
+ || grub_strcmp (term->name, "console") == 0
+ || grub_strcmp (term->name, "ofconsole") == 0)
+ {
+ grub_uint16_t pos = grub_term_getxy (term);
+ params->video_cursor_x = pos >> 8;
+ params->video_cursor_y = pos & 0xff;
+ params->video_width = grub_term_width (term);
+ params->video_height = grub_term_height (term);
+ found = 1;
+ break;
+ }
+ if (!found)
+ {
+ params->video_cursor_x = 0;
+ params->video_cursor_y = 0;
+ params->video_width = 80;
+ params->video_height = 25;
+ }
+ }
+
+#ifdef GRUB_MACHINE_IEEE1275
+ {
+ params->ofw_signature = GRUB_LINUX_OFW_SIGNATURE;
+ params->ofw_num_items = 1;
+ params->ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn;
+ params->ofw_idt = 0;
+ }
+#endif
+
+#ifdef GRUB_MACHINE_EFI
+ {
+ grub_efi_uintn_t efi_desc_size;
+ grub_size_t efi_mmap_target;
+ grub_efi_uint32_t efi_desc_version;
+ err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL,
+ &efi_desc_size, &efi_desc_version);
+ if (err)
+ return err;
+
+ /* Note that no boot services are available from here. */
+ efi_mmap_target = real_mode_target
+ + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem);
+ /* Pass EFI parameters. */
+ if (grub_le_to_cpu16 (params->version) >= 0x0206)
+ {
+ params->v0206.efi_mem_desc_size = efi_desc_size;
+ params->v0206.efi_mem_desc_version = efi_desc_version;
+ params->v0206.efi_mmap = efi_mmap_target;
+ params->v0206.efi_mmap_size = efi_mmap_size;
+#ifdef __x86_64__
+ params->v0206.efi_mmap_hi = (efi_mmap_target >> 32);
+#endif
+ }
+ else if (grub_le_to_cpu16 (params->version) >= 0x0204)
+ {
+ params->v0204.efi_mem_desc_size = efi_desc_size;
+ params->v0204.efi_mem_desc_version = efi_desc_version;
+ params->v0204.efi_mmap = efi_mmap_target;
+ params->v0204.efi_mmap_size = efi_mmap_size;
+ }
+ }
+#endif
+
+ /* FIXME. */
+ /* asm volatile ("lidt %0" : : "m" (idt_desc)); */
+ state.ebp = state.edi = state.ebx = 0;
+ state.esi = real_mode_target;
+ state.esp = real_mode_target;
+ state.eip = params->code32_start;
+ return grub_relocator32_boot (relocator, state);
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ struct linux_kernel_header lh;
+ struct linux_kernel_params *params;
+ grub_uint8_t setup_sects;
+ grub_size_t real_size, prot_size;
+ grub_ssize_t len;
+ int i;
+
+ grub_dl_ref (my_mod);
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
+ goto fail;
+ }
+
+ if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
+ goto fail;
+ }
+
+ if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
+ goto fail;
+ }
+
+ if (! (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "zImage doesn't support 32-bit boot"
+#ifdef GRUB_MACHINE_PCBIOS
+ " (try with `linux16')"
+#endif
+ );
+ goto fail;
+ }
+
+ /* FIXME: 2.03 is not always good enough (Linux 2.4 can be 2.03 and
+ still not support 32-bit boot. */
+ if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
+ || grub_le_to_cpu16 (lh.version) < 0x0203)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "version too old for 32-bit boot"
+#ifdef GRUB_MACHINE_PCBIOS
+ " (try with `linux16')"
+#endif
+ );
+ goto fail;
+ }
+
+ setup_sects = lh.setup_sects;
+
+ /* If SETUP_SECTS is not set, set it to the default (4). */
+ if (! setup_sects)
+ setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
+
+ real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
+ prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
+
+ if (allocate_pages (prot_size))
+ goto fail;
+
+ params = (struct linux_kernel_params *) real_mode_mem;
+ grub_memset (params, 0, GRUB_LINUX_CL_END_OFFSET);
+ grub_memcpy (&params->setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
+
+ params->ps_mouse = params->padding10 = 0;
+
+ len = 0x400 - sizeof (lh);
+ if (grub_file_read (file, (char *) real_mode_mem + sizeof (lh), len) != len)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ params->type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
+
+ /* These two are used (instead of cmd_line_ptr) by older versions of Linux,
+ and otherwise ignored. */
+ params->cl_magic = GRUB_LINUX_CL_MAGIC;
+ params->cl_offset = 0x1000;
+
+ params->cmd_line_ptr = real_mode_target + 0x1000;
+ params->ramdisk_image = 0;
+ params->ramdisk_size = 0;
+
+ params->heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET;
+ params->loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
+
+ /* These are not needed to be precise, because Linux uses these values
+ only to raise an error when the decompression code cannot find good
+ space. */
+ params->ext_mem = ((32 * 0x100000) >> 10);
+ params->alt_mem = ((32 * 0x100000) >> 10);
+
+ /* Ignored by Linux. */
+ params->video_page = 0;
+
+ /* Only used when `video_mode == 0x7', otherwise ignored. */
+ params->video_ega_bx = 0;
+
+ params->font_size = 16; /* XXX */
+
+#ifdef GRUB_MACHINE_EFI
+ if (grub_le_to_cpu16 (params->version) >= 0x0206)
+ {
+ params->v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
+ params->v0206.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
+#ifdef __x86_64__
+ params->v0206.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32);
+#endif
+ }
+ else if (grub_le_to_cpu16 (params->version) >= 0x0204)
+ {
+ params->v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204;
+ params->v0204.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
+ }
+#endif
+
+ /* The other parameters are filled when booting. */
+
+ grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
+
+ grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
+ (unsigned) real_size, (unsigned) prot_size);
+
+ /* Look for memory size and video mode specified on the command line. */
+ linux_mem_size = 0;
+ for (i = 1; i < argc; i++)
+#ifdef GRUB_MACHINE_PCBIOS
+ if (grub_memcmp (argv[i], "vga=", 4) == 0)
+ {
+ /* Video mode selection support. */
+ char *val = argv[i] + 4;
+ unsigned vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
+ struct grub_vesa_mode_table_entry *linux_mode;
+ grub_err_t err;
+ char *buf;
+
+ grub_dl_load ("vbe");
+
+ if (grub_strcmp (val, "normal") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
+ else if (grub_strcmp (val, "ext") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
+ else if (grub_strcmp (val, "ask") == 0)
+ {
+ grub_printf ("Legacy `ask' parameter no longer supported.\n");
+
+ /* We usually would never do this in a loader, but "vga=ask" means user
+ requested interaction, so it can't hurt to request keyboard input. */
+ grub_wait_after_message ();
+
+ goto fail;
+ }
+ else
+ vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
+
+ switch (vid_mode)
+ {
+ case 0:
+ case GRUB_LINUX_VID_MODE_NORMAL:
+ grub_env_set ("gfxpayload", "text");
+ grub_printf ("%s is deprecated. "
+ "Use set gfxpayload=text before "
+ "linux command instead.\n",
+ argv[i]);
+ break;
+
+ case 1:
+ case GRUB_LINUX_VID_MODE_EXTENDED:
+ /* FIXME: support 80x50 text. */
+ grub_env_set ("gfxpayload", "text");
+ grub_printf ("%s is deprecated. "
+ "Use set gfxpayload=text before "
+ "linux command instead.\n",
+ argv[i]);
+ break;
+ default:
+ /* Ignore invalid values. */
+ if (vid_mode < GRUB_VESA_MODE_TABLE_START ||
+ vid_mode > GRUB_VESA_MODE_TABLE_END)
+ {
+ grub_env_set ("gfxpayload", "text");
+ grub_printf ("%s is deprecated. Mode %d isn't recognized. "
+ "Use set gfxpayload=WIDTHxHEIGHT[xDEPTH] before "
+ "linux command instead.\n",
+ argv[i], vid_mode);
+ break;
+ }
+
+ linux_mode = &grub_vesa_mode_table[vid_mode
+ - GRUB_VESA_MODE_TABLE_START];
+
+ buf = grub_xasprintf ("%ux%ux%u,%ux%u",
+ linux_mode->width, linux_mode->height,
+ linux_mode->depth,
+ linux_mode->width, linux_mode->height);
+ if (! buf)
+ goto fail;
+
+ grub_printf ("%s is deprecated. "
+ "Use set gfxpayload=%s before "
+ "linux command instead.\n",
+ argv[i], buf);
+ err = grub_env_set ("gfxpayload", buf);
+ grub_free (buf);
+ if (err)
+ goto fail;
+ }
+ }
+ else
+#endif /* GRUB_MACHINE_PCBIOS */
+ if (grub_memcmp (argv[i], "mem=", 4) == 0)
+ {
+ char *val = argv[i] + 4;
+
+ linux_mem_size = grub_strtoul (val, &val, 0);
+
+ if (grub_errno)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ linux_mem_size = 0;
+ }
+ else
+ {
+ int shift = 0;
+
+ switch (grub_tolower (val[0]))
+ {
+ case 'g':
+ shift += 10;
+ case 'm':
+ shift += 10;
+ case 'k':
+ shift += 10;
+ default:
+ break;
+ }
+
+ /* Check an overflow. */
+ if (linux_mem_size > (~0UL >> shift))
+ linux_mem_size = 0;
+ else
+ linux_mem_size <<= shift;
+ }
+ }
+ else if (grub_memcmp (argv[i], "quiet", sizeof ("quiet") - 1) == 0)
+ {
+ params->loadflags |= GRUB_LINUX_FLAG_QUIET;
+ }
+
+ /* Create kernel command line. */
+ grub_memcpy ((char *)real_mode_mem + GRUB_LINUX_CL_OFFSET, LINUX_IMAGE,
+ sizeof (LINUX_IMAGE));
+ grub_create_loader_cmdline (argc, argv,
+ (char *)real_mode_mem + GRUB_LINUX_CL_OFFSET
+ + sizeof (LINUX_IMAGE) - 1,
+ GRUB_LINUX_CL_END_OFFSET - GRUB_LINUX_CL_OFFSET
+ - (sizeof (LINUX_IMAGE) - 1));
+
+ len = prot_size;
+ if (grub_file_read (file, prot_mode_mem, len) != len)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ grub_loader_set (grub_linux_boot, grub_linux_unload,
+ 0 /* set noreturn=0 in order to avoid grub_console_fini() */);
+ loaded = 1;
+ }
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_ssize_t size;
+ grub_addr_t addr_min, addr_max;
+ grub_addr_t addr;
+ grub_err_t err;
+ struct linux_kernel_header *lh;
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
+ goto fail;
+ }
+
+ if (! loaded)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
+ goto fail;
+ }
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ size = grub_file_size (file);
+ initrd_pages = (page_align (size) >> 12);
+
+ lh = (struct linux_kernel_header *) real_mode_mem;
+
+ /* Get the highest address available for the initrd. */
+ if (grub_le_to_cpu16 (lh->version) >= 0x0203)
+ {
+ addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);
+
+ /* XXX in reality, Linux specifies a bogus value, so
+ it is necessary to make sure that ADDR_MAX does not exceed
+ 0x3fffffff. */
+ if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
+ addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
+ }
+ else
+ addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
+
+ if (linux_mem_size != 0 && linux_mem_size < addr_max)
+ addr_max = linux_mem_size;
+
+ /* Linux 2.3.xx has a bug in the memory range check, so avoid
+ the last page.
+ Linux 2.2.xx has a bug in the memory range check, which is
+ worse than that of Linux 2.3.xx, so avoid the last 64kb. */
+ addr_max -= 0x10000;
+
+ /* Usually, the compression ratio is about 50%. */
+ addr_min = (grub_addr_t) prot_mode_target + ((prot_mode_pages * 3) << 12)
+ + page_align (size);
+
+ /* Put the initrd as high as possible, 4KiB aligned. */
+ addr = (addr_max - size) & ~0xFFF;
+
+ if (addr < addr_min)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big");
+ goto fail;
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ addr_min, addr, size, 0x1000,
+ GRUB_RELOCATOR_PREFERENCE_HIGH);
+ if (err)
+ return err;
+ initrd_mem = get_virtual_current_address (ch);
+ initrd_mem_target = get_physical_target_address (ch);
+ }
+
+ if (grub_file_read (file, initrd_mem, size) != size)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
+ (unsigned) addr, (unsigned) size);
+
+ lh->ramdisk_image = initrd_mem_target;
+ lh->ramdisk_size = size;
+ lh->root_dev = 0x0100; /* XXX */
+
+ fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_linux, cmd_initrd;
+
+GRUB_MOD_INIT(linux)
+{
+ cmd_linux = grub_register_command ("linux", grub_cmd_linux,
+ 0, N_("Load Linux."));
+ cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
+ 0, N_("Load initrd."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(linux)
+{
+ grub_unregister_command (cmd_linux);
+ grub_unregister_command (cmd_initrd);
+}
diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c
new file mode 100644
index 0000000..bef5342
--- /dev/null
+++ b/grub-core/loader/i386/multiboot_mbi.c
@@ -0,0 +1,731 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/memory.h>
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/machine/biosnum.h>
+#include <grub/machine/apm.h>
+#include <grub/machine/memory.h>
+#endif
+#include <grub/multiboot.h>
+#include <grub/cpu/relocator.h>
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/partition.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/relocator.h>
+#include <grub/video.h>
+#include <grub/file.h>
+
+/* The bits in the required part of flags field we don't support. */
+#define UNSUPPORTED_FLAGS 0x0000fff8
+
+struct module
+{
+ struct module *next;
+ grub_addr_t start;
+ grub_size_t size;
+ char *cmdline;
+ int cmdline_size;
+};
+
+static struct module *modules, *modules_last;
+static grub_size_t cmdline_size;
+static grub_size_t total_modcmd;
+static unsigned modcnt;
+static char *cmdline = NULL;
+static grub_uint32_t bootdev;
+static int bootdev_set;
+static grub_size_t elf_sec_num, elf_sec_entsize;
+static unsigned elf_sec_shstrndx;
+static void *elf_sections;
+
+
+grub_err_t
+grub_multiboot_load (grub_file_t file)
+{
+ char *buffer;
+ grub_ssize_t len;
+ struct multiboot_header *header;
+ grub_err_t err;
+
+ buffer = grub_malloc (MULTIBOOT_SEARCH);
+ if (!buffer)
+ return grub_errno;
+
+ len = grub_file_read (file, buffer, MULTIBOOT_SEARCH);
+ if (len < 32)
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_BAD_OS, "file too small");
+ }
+
+ /* Look for the multiboot header in the buffer. The header should
+ be at least 12 bytes and aligned on a 4-byte boundary. */
+ for (header = (struct multiboot_header *) buffer;
+ ((char *) header <= buffer + len - 12) || (header = 0);
+ header = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN))
+ {
+ if (header->magic == MULTIBOOT_HEADER_MAGIC
+ && !(header->magic + header->flags + header->checksum))
+ break;
+ }
+
+ if (header == 0)
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found");
+ }
+
+ if (header->flags & UNSUPPORTED_FLAGS)
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ "unsupported flag: 0x%x", header->flags);
+ }
+
+ if (header->flags & MULTIBOOT_AOUT_KLUDGE)
+ {
+ int offset = ((char *) header - buffer -
+ (header->header_addr - header->load_addr));
+ int load_size = ((header->load_end_addr == 0) ? file->size - offset :
+ header->load_end_addr - header->load_addr);
+ grub_size_t code_size;
+ void *source;
+ grub_relocator_chunk_t ch;
+
+ if (header->bss_end_addr)
+ code_size = (header->bss_end_addr - header->load_addr);
+ else
+ code_size = load_size;
+
+ err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator,
+ &ch, header->load_addr,
+ code_size);
+ if (err)
+ {
+ grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
+ grub_free (buffer);
+ return err;
+ }
+ source = get_virtual_current_address (ch);
+
+ if ((grub_file_seek (file, offset)) == (grub_off_t) -1)
+ {
+ grub_free (buffer);
+ return grub_errno;
+ }
+
+ grub_file_read (file, source, load_size);
+ if (grub_errno)
+ {
+ grub_free (buffer);
+ return grub_errno;
+ }
+
+ if (header->bss_end_addr)
+ grub_memset ((grub_uint8_t *) source + load_size, 0,
+ header->bss_end_addr - header->load_addr - load_size);
+
+ grub_multiboot_payload_eip = header->entry_addr;
+ }
+ else
+ {
+ err = grub_multiboot_load_elf (file, buffer);
+ if (err)
+ {
+ grub_free (buffer);
+ return err;
+ }
+ }
+
+ if (header->flags & MULTIBOOT_VIDEO_MODE)
+ {
+ switch (header->mode_type)
+ {
+ case 1:
+ err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
+ GRUB_MULTIBOOT_CONSOLE_EGA_TEXT
+ | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
+ 0, 0, 0, 0);
+ break;
+ case 0:
+ err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
+ GRUB_MULTIBOOT_CONSOLE_EGA_TEXT
+ | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
+ header->width, header->height,
+ header->depth, 0);
+ break;
+ default:
+ err = grub_error (GRUB_ERR_BAD_OS,
+ "unsupported graphical mode type %d",
+ header->mode_type);
+ break;
+ }
+ }
+ else
+ err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
+ GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
+ 0, 0, 0, 0);
+ return err;
+}
+
+#if GRUB_MACHINE_HAS_VBE || GRUB_MACHINE_HAS_VGA_TEXT
+#include <grub/i386/pc/vbe.h>
+#endif
+
+static grub_size_t
+grub_multiboot_get_mbi_size (void)
+{
+ return sizeof (struct multiboot_info) + ALIGN_UP (cmdline_size, 4)
+ + modcnt * sizeof (struct multiboot_mod_list) + total_modcmd
+ + ALIGN_UP (sizeof(PACKAGE_STRING), 4)
+ + grub_get_multiboot_mmap_count () * sizeof (struct multiboot_mmap_entry)
+ + elf_sec_entsize * elf_sec_num
+ + 256 * sizeof (struct multiboot_color)
+#if GRUB_MACHINE_HAS_VBE || GRUB_MACHINE_HAS_VGA_TEXT
+ + sizeof (struct grub_vbe_info_block)
+ + sizeof (struct grub_vbe_mode_info_block)
+#endif
+ + ALIGN_UP (sizeof (struct multiboot_apm_info), 4);
+}
+
+/* Fill previously allocated Multiboot mmap. */
+static void
+grub_fill_multiboot_mmap (struct multiboot_mmap_entry *first_entry)
+{
+ struct multiboot_mmap_entry *mmap_entry = (struct multiboot_mmap_entry *) first_entry;
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+ mmap_entry->addr = addr;
+ mmap_entry->len = size;
+ switch (type)
+ {
+ case GRUB_MEMORY_AVAILABLE:
+ mmap_entry->type = MULTIBOOT_MEMORY_AVAILABLE;
+ break;
+
+ case GRUB_MEMORY_ACPI:
+ mmap_entry->type = MULTIBOOT_MEMORY_ACPI_RECLAIMABLE;
+ break;
+
+ case GRUB_MEMORY_NVS:
+ mmap_entry->type = MULTIBOOT_MEMORY_NVS;
+ break;
+
+ case GRUB_MEMORY_BADRAM:
+ mmap_entry->type = MULTIBOOT_MEMORY_BADRAM;
+ break;
+
+ default:
+ mmap_entry->type = MULTIBOOT_MEMORY_RESERVED;
+ break;
+ }
+ mmap_entry->size = sizeof (struct multiboot_mmap_entry) - sizeof (mmap_entry->size);
+ mmap_entry++;
+
+ return 0;
+ }
+
+ grub_mmap_iterate (hook);
+}
+
+#if GRUB_MACHINE_HAS_VBE || GRUB_MACHINE_HAS_VGA_TEXT
+
+static grub_err_t
+fill_vbe_info (struct multiboot_info *mbi, grub_uint8_t *ptrorig,
+ grub_uint32_t ptrdest, int fill_generic)
+{
+ grub_uint32_t vbe_mode;
+ struct grub_vbe_mode_info_block *mode_info;
+#if GRUB_MACHINE_HAS_VBE
+ grub_vbe_status_t status;
+ void *scratch = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+
+ status = grub_vbe_bios_get_controller_info (scratch);
+ if (status != GRUB_VBE_STATUS_OK)
+ return grub_error (GRUB_ERR_IO, "Can't get controller info.");
+
+ mbi->vbe_control_info = ptrdest;
+ grub_memcpy (ptrorig, scratch, sizeof (struct grub_vbe_info_block));
+ ptrorig += sizeof (struct grub_vbe_info_block);
+ ptrdest += sizeof (struct grub_vbe_info_block);
+#else
+ mbi->vbe_control_info = 0;
+#endif
+
+#if GRUB_MACHINE_HAS_VBE
+ status = grub_vbe_bios_get_mode (scratch);
+ vbe_mode = *(grub_uint32_t *) scratch;
+ if (status != GRUB_VBE_STATUS_OK)
+ return grub_error (GRUB_ERR_IO, "can't get VBE mode");
+#else
+ vbe_mode = 3;
+#endif
+ mbi->vbe_mode = vbe_mode;
+
+ mode_info = (struct grub_vbe_mode_info_block *) ptrorig;
+ mbi->vbe_mode_info = ptrdest;
+ /* get_mode_info isn't available for mode 3. */
+ if (vbe_mode == 3)
+ {
+ grub_memset (mode_info, 0, sizeof (struct grub_vbe_mode_info_block));
+ mode_info->memory_model = GRUB_VBE_MEMORY_MODEL_TEXT;
+ mode_info->x_resolution = 80;
+ mode_info->y_resolution = 25;
+ }
+ else
+ {
+#if GRUB_MACHINE_HAS_VBE
+ status = grub_vbe_bios_get_mode_info (vbe_mode, scratch);
+ if (status != GRUB_VBE_STATUS_OK)
+ return grub_error (GRUB_ERR_IO, "can't get mode info");
+ grub_memcpy (mode_info, scratch,
+ sizeof (struct grub_vbe_mode_info_block));
+#endif
+ }
+ ptrorig += sizeof (struct grub_vbe_mode_info_block);
+ ptrdest += sizeof (struct grub_vbe_mode_info_block);
+
+#if GRUB_MACHINE_HAS_VBE
+ grub_vbe_bios_get_pm_interface (&mbi->vbe_interface_seg,
+ &mbi->vbe_interface_off,
+ &mbi->vbe_interface_len);
+#endif
+
+ mbi->flags |= MULTIBOOT_INFO_VBE_INFO;
+
+ if (fill_generic && mode_info->memory_model == GRUB_VBE_MEMORY_MODEL_TEXT)
+ {
+ mbi->framebuffer_addr = 0xb8000;
+
+ mbi->framebuffer_pitch = 2 * mode_info->x_resolution;
+ mbi->framebuffer_width = mode_info->x_resolution;
+ mbi->framebuffer_height = mode_info->y_resolution;
+
+ mbi->framebuffer_bpp = 16;
+
+ mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
+
+ mbi->flags |= MULTIBOOT_INFO_FRAMEBUFFER_INFO;
+ }
+
+ return GRUB_ERR_NONE;
+}
+#endif
+
+static grub_err_t
+retrieve_video_parameters (struct multiboot_info *mbi,
+ grub_uint8_t *ptrorig, grub_uint32_t ptrdest)
+{
+ grub_err_t err;
+ struct grub_video_mode_info mode_info;
+ void *framebuffer;
+ grub_video_driver_id_t driv_id;
+ struct grub_video_palette_data palette[256];
+
+ err = grub_multiboot_set_video_mode ();
+ if (err)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ grub_video_get_palette (0, ARRAY_SIZE (palette), palette);
+
+ driv_id = grub_video_get_driver_id ();
+#if GRUB_MACHINE_HAS_VGA_TEXT
+ if (driv_id == GRUB_VIDEO_DRIVER_NONE)
+ return fill_vbe_info (mbi, ptrorig, ptrdest, 1);
+#else
+ if (driv_id == GRUB_VIDEO_DRIVER_NONE)
+ return GRUB_ERR_NONE;
+#endif
+
+ err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+ if (err)
+ return err;
+
+ mbi->framebuffer_addr = (grub_addr_t) framebuffer;
+ mbi->framebuffer_pitch = mode_info.pitch;
+
+ mbi->framebuffer_width = mode_info.width;
+ mbi->framebuffer_height = mode_info.height;
+
+ mbi->framebuffer_bpp = mode_info.bpp;
+
+ if (mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)
+ {
+ struct multiboot_color *mb_palette;
+ unsigned i;
+ mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED;
+ mbi->framebuffer_palette_addr = ptrdest;
+ mbi->framebuffer_palette_num_colors = mode_info.number_of_colors;
+ if (mbi->framebuffer_palette_num_colors > ARRAY_SIZE (palette))
+ mbi->framebuffer_palette_num_colors = ARRAY_SIZE (palette);
+ mb_palette = (struct multiboot_color *) ptrorig;
+ for (i = 0; i < mbi->framebuffer_palette_num_colors; i++)
+ {
+ mb_palette[i].red = palette[i].r;
+ mb_palette[i].green = palette[i].g;
+ mb_palette[i].blue = palette[i].b;
+ }
+ ptrorig += mbi->framebuffer_palette_num_colors
+ * sizeof (struct multiboot_color);
+ ptrdest += mbi->framebuffer_palette_num_colors
+ * sizeof (struct multiboot_color);
+ }
+ else
+ {
+ mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
+ mbi->framebuffer_red_field_position = mode_info.red_field_pos;
+ mbi->framebuffer_red_mask_size = mode_info.red_mask_size;
+ mbi->framebuffer_green_field_position = mode_info.green_field_pos;
+ mbi->framebuffer_green_mask_size = mode_info.green_mask_size;
+ mbi->framebuffer_blue_field_position = mode_info.blue_field_pos;
+ mbi->framebuffer_blue_mask_size = mode_info.blue_mask_size;
+ }
+
+ mbi->flags |= MULTIBOOT_INFO_FRAMEBUFFER_INFO;
+
+#if GRUB_MACHINE_HAS_VBE
+ if (driv_id == GRUB_VIDEO_DRIVER_VBE)
+ return fill_vbe_info (mbi, ptrorig, ptrdest, 0);
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_multiboot_make_mbi (grub_uint32_t *target)
+{
+ struct multiboot_info *mbi;
+ struct multiboot_mod_list *modlist;
+ unsigned i;
+ struct module *cur;
+ grub_size_t mmap_size;
+ grub_uint8_t *ptrorig;
+ grub_addr_t ptrdest;
+
+ grub_err_t err;
+ grub_size_t bufsize;
+ grub_relocator_chunk_t ch;
+
+ bufsize = grub_multiboot_get_mbi_size ();
+
+ err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
+ 0x10000, 0x100000 - bufsize,
+ bufsize, 4,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ if (err)
+ return err;
+ ptrorig = get_virtual_current_address (ch);
+ ptrdest = get_physical_target_address (ch);
+
+ *target = ptrdest;
+
+ mbi = (struct multiboot_info *) ptrorig;
+ ptrorig += sizeof (*mbi);
+ ptrdest += sizeof (*mbi);
+ grub_memset (mbi, 0, sizeof (*mbi));
+
+ grub_memcpy (ptrorig, cmdline, cmdline_size);
+ mbi->flags |= MULTIBOOT_INFO_CMDLINE;
+ mbi->cmdline = ptrdest;
+ ptrorig += ALIGN_UP (cmdline_size, 4);
+ ptrdest += ALIGN_UP (cmdline_size, 4);
+
+ grub_memcpy (ptrorig, PACKAGE_STRING, sizeof(PACKAGE_STRING));
+ mbi->flags |= MULTIBOOT_INFO_BOOT_LOADER_NAME;
+ mbi->boot_loader_name = ptrdest;
+ ptrorig += ALIGN_UP (sizeof(PACKAGE_STRING), 4);
+ ptrdest += ALIGN_UP (sizeof(PACKAGE_STRING), 4);
+
+#ifdef GRUB_MACHINE_PCBIOS
+ {
+ struct grub_apm_info info;
+ if (grub_apm_get_info (&info))
+ {
+ struct multiboot_apm_info *mbinfo = (void *) ptrorig;
+
+ mbinfo->cseg = info.cseg;
+ mbinfo->offset = info.offset;
+ mbinfo->cseg_16 = info.cseg_16;
+ mbinfo->dseg = info.dseg;
+ mbinfo->flags = info.flags;
+ mbinfo->cseg_len = info.cseg_len;
+ mbinfo->dseg_len = info.dseg_len;
+ mbinfo->cseg_16_len = info.cseg_16_len;
+ mbinfo->version = info.version;
+
+ ptrorig += ALIGN_UP (sizeof (struct multiboot_apm_info), 4);
+ ptrdest += ALIGN_UP (sizeof (struct multiboot_apm_info), 4);
+ }
+ }
+#endif
+
+ if (modcnt)
+ {
+ mbi->flags |= MULTIBOOT_INFO_MODS;
+ mbi->mods_addr = ptrdest;
+ mbi->mods_count = modcnt;
+ modlist = (struct multiboot_mod_list *) ptrorig;
+ ptrorig += modcnt * sizeof (struct multiboot_mod_list);
+ ptrdest += modcnt * sizeof (struct multiboot_mod_list);
+
+ for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next)
+ {
+ modlist[i].mod_start = cur->start;
+ modlist[i].mod_end = modlist[i].mod_start + cur->size;
+ modlist[i].cmdline = ptrdest;
+ grub_memcpy (ptrorig, cur->cmdline, cur->cmdline_size);
+ ptrorig += ALIGN_UP (cur->cmdline_size, 4);
+ ptrdest += ALIGN_UP (cur->cmdline_size, 4);
+ }
+ }
+ else
+ {
+ mbi->mods_addr = 0;
+ mbi->mods_count = 0;
+ }
+
+ mmap_size = grub_get_multiboot_mmap_count ()
+ * sizeof (struct multiboot_mmap_entry);
+ grub_fill_multiboot_mmap ((struct multiboot_mmap_entry *) ptrorig);
+ mbi->mmap_length = mmap_size;
+ mbi->mmap_addr = ptrdest;
+ mbi->flags |= MULTIBOOT_INFO_MEM_MAP;
+ ptrorig += mmap_size;
+ ptrdest += mmap_size;
+
+ /* Convert from bytes to kilobytes. */
+ mbi->mem_lower = grub_mmap_get_lower () / 1024;
+ mbi->mem_upper = grub_mmap_get_upper () / 1024;
+ mbi->flags |= MULTIBOOT_INFO_MEMORY;
+
+ if (bootdev_set)
+ {
+ mbi->boot_device = bootdev;
+ mbi->flags |= MULTIBOOT_INFO_BOOTDEV;
+ }
+
+ if (elf_sec_num)
+ {
+ mbi->u.elf_sec.addr = ptrdest;
+ grub_memcpy (ptrorig, elf_sections, elf_sec_entsize * elf_sec_num);
+ mbi->u.elf_sec.num = elf_sec_num;
+ mbi->u.elf_sec.size = elf_sec_entsize;
+ mbi->u.elf_sec.shndx = elf_sec_shstrndx;
+
+ mbi->flags |= MULTIBOOT_INFO_ELF_SHDR;
+
+ ptrorig += elf_sec_entsize * elf_sec_num;
+ ptrdest += elf_sec_entsize * elf_sec_num;
+ }
+
+ err = retrieve_video_parameters (mbi, ptrorig, ptrdest);
+ if (err)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ if ((mbi->flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO)
+ && mbi->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED)
+ {
+ ptrorig += mbi->framebuffer_palette_num_colors
+ * sizeof (struct multiboot_color);
+ ptrdest += mbi->framebuffer_palette_num_colors
+ * sizeof (struct multiboot_color);
+ }
+
+#if GRUB_MACHINE_HAS_VBE
+ ptrorig += sizeof (struct grub_vbe_info_block);
+ ptrdest += sizeof (struct grub_vbe_info_block);
+ ptrorig += sizeof (struct grub_vbe_mode_info_block);
+ ptrdest += sizeof (struct grub_vbe_mode_info_block);
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_multiboot_add_elfsyms (grub_size_t num, grub_size_t entsize,
+ unsigned shndx, void *data)
+{
+ elf_sec_num = num;
+ elf_sec_shstrndx = shndx;
+ elf_sec_entsize = entsize;
+ elf_sections = data;
+}
+
+void
+grub_multiboot_free_mbi (void)
+{
+ struct module *cur, *next;
+
+ cmdline_size = 0;
+ total_modcmd = 0;
+ modcnt = 0;
+ grub_free (cmdline);
+ cmdline = NULL;
+ bootdev_set = 0;
+
+ for (cur = modules; cur; cur = next)
+ {
+ next = cur->next;
+ grub_free (cur->cmdline);
+ grub_free (cur);
+ }
+ modules = NULL;
+ modules_last = NULL;
+
+ grub_free (elf_sections);
+ elf_sections = NULL;
+ elf_sec_entsize = 0;
+ elf_sec_num = 0;
+}
+
+grub_err_t
+grub_multiboot_init_mbi (int argc, char *argv[])
+{
+ grub_ssize_t len = 0;
+ char *p;
+ int i;
+
+ grub_multiboot_free_mbi ();
+
+ for (i = 0; i < argc; i++)
+ len += grub_strlen (argv[i]) + 1;
+ if (len == 0)
+ len = 1;
+
+ cmdline = p = grub_malloc (len);
+ if (! cmdline)
+ return grub_errno;
+ cmdline_size = len;
+
+ for (i = 0; i < argc; i++)
+ {
+ p = grub_stpcpy (p, argv[i]);
+ *(p++) = ' ';
+ }
+
+ /* Remove the space after the last word. */
+ if (p != cmdline)
+ p--;
+ *p = '\0';
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_multiboot_add_module (grub_addr_t start, grub_size_t size,
+ int argc, char *argv[])
+{
+ struct module *newmod;
+ char *p;
+ grub_ssize_t len = 0;
+ int i;
+
+ newmod = grub_malloc (sizeof (*newmod));
+ if (!newmod)
+ return grub_errno;
+ newmod->start = start;
+ newmod->size = size;
+ newmod->next = 0;
+
+ for (i = 0; i < argc; i++)
+ len += grub_strlen (argv[i]) + 1;
+
+ if (len == 0)
+ len = 1;
+
+ newmod->cmdline = p = grub_malloc (len);
+ if (! newmod->cmdline)
+ {
+ grub_free (newmod);
+ return grub_errno;
+ }
+ newmod->cmdline_size = len;
+ total_modcmd += ALIGN_UP (len, 4);
+
+ for (i = 0; i < argc; i++)
+ {
+ p = grub_stpcpy (p, argv[i]);
+ *(p++) = ' ';
+ }
+
+ /* Remove the space after the last word. */
+ if (p != newmod->cmdline)
+ p--;
+ *p = '\0';
+
+ if (modules_last)
+ modules_last->next = newmod;
+ else
+ {
+ modules = newmod;
+ modules_last->next = NULL;
+ }
+ modules_last = newmod;
+
+ modcnt++;
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_multiboot_set_bootdev (void)
+{
+ grub_uint32_t biosdev, slice = ~0, part = ~0;
+ grub_device_t dev;
+
+#ifdef GRUB_MACHINE_PCBIOS
+ biosdev = grub_get_root_biosnumber ();
+#else
+ biosdev = 0xffffffff;
+#endif
+
+ if (biosdev == 0xffffffff)
+ return;
+
+ dev = grub_device_open (0);
+ if (dev && dev->disk && dev->disk->partition)
+ {
+ if (dev->disk->partition->parent)
+ {
+ part = dev->disk->partition->number;
+ slice = dev->disk->partition->parent->number;
+ }
+ else
+ slice = dev->disk->partition->number;
+ }
+ if (dev)
+ grub_device_close (dev);
+
+ bootdev = ((biosdev & 0xff) << 24) | ((slice & 0xff) << 16)
+ | ((part & 0xff) << 8) | 0xff;
+ bootdev_set = 1;
+}
diff --git a/grub-core/loader/i386/pc/chainloader.c b/grub-core/loader/i386/pc/chainloader.c
new file mode 100644
index 0000000..794316b
--- /dev/null
+++ b/grub-core/loader/i386/pc/chainloader.c
@@ -0,0 +1,177 @@
+/* chainloader.c - boot another boot loader */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2004,2007,2009,2010 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/machine/loader.h>
+#include <grub/machine/chainloader.h>
+#include <grub/machine/memory.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/device.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/partition.h>
+#include <grub/memory.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/msdos_partition.h>
+#include <grub/machine/biosnum.h>
+#include <grub/cpu/floppy.h>
+#include <grub/i18n.h>
+#include <grub/video.h>
+#include <grub/mm.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+static int boot_drive;
+static void *boot_part_addr;
+
+static grub_err_t
+grub_chainloader_boot (void)
+{
+ grub_video_set_mode ("text", 0, 0);
+ grub_chainloader_real_boot (boot_drive, boot_part_addr);
+
+ /* Never reach here. */
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_chainloader_unload (void)
+{
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_chainloader_cmd (const char *filename, grub_chainloader_flags_t flags)
+{
+ grub_file_t file = 0;
+ grub_uint16_t signature;
+ grub_device_t dev;
+ int drive = -1;
+ void *part_addr = 0;
+
+ grub_dl_ref (my_mod);
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (filename);
+ if (! file)
+ goto fail;
+
+ /* Read the first block. */
+ if (grub_file_read (file, (void *) 0x7C00, GRUB_DISK_SECTOR_SIZE)
+ != GRUB_DISK_SECTOR_SIZE)
+ {
+ if (grub_errno == GRUB_ERR_NONE)
+ grub_error (GRUB_ERR_BAD_OS, "too small");
+
+ goto fail;
+ }
+
+ /* Check the signature. */
+ signature = *((grub_uint16_t *) (0x7C00 + GRUB_DISK_SECTOR_SIZE - 2));
+ if (signature != grub_le_to_cpu16 (0xaa55)
+ && ! (flags & GRUB_CHAINLOADER_FORCE))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid signature");
+ goto fail;
+ }
+
+ grub_file_close (file);
+
+ /* Obtain the partition table from the root device. */
+ drive = grub_get_root_biosnumber ();
+ dev = grub_device_open (0);
+ if (dev && dev->disk && dev->disk->partition)
+ {
+ grub_disk_t disk = dev->disk;
+
+ if (disk)
+ {
+ grub_partition_t p = disk->partition;
+
+ if (p && grub_strcmp (p->partmap->name, "msdos") == 0)
+ {
+ disk->partition = p->parent;
+ grub_disk_read (disk, p->offset, 446, 64,
+ (void *) GRUB_MEMORY_MACHINE_PART_TABLE_ADDR);
+ part_addr = (void *) (GRUB_MEMORY_MACHINE_PART_TABLE_ADDR
+ + (p->index << 4));
+ disk->partition = p;
+ }
+ }
+ }
+
+ if (dev)
+ grub_device_close (dev);
+
+ /* Ignore errors. Perhaps it's not fatal. */
+ grub_errno = GRUB_ERR_NONE;
+
+ boot_drive = drive;
+ boot_part_addr = part_addr;
+
+ grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 1);
+ return;
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ grub_dl_unref (my_mod);
+}
+
+static grub_err_t
+grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_chainloader_flags_t flags = 0;
+
+ if (argc > 0 && grub_strcmp (argv[0], "--force") == 0)
+ {
+ flags |= GRUB_CHAINLOADER_FORCE;
+ argc--;
+ argv++;
+ }
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified");
+ else
+ grub_chainloader_cmd (argv[0], flags);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(chainloader)
+{
+ cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
+ 0, N_("Load another boot loader."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(chainloader)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
new file mode 100644
index 0000000..c6e6b67
--- /dev/null
+++ b/grub-core/loader/i386/pc/linux.c
@@ -0,0 +1,450 @@
+/* linux.c - boot Linux zImage or bzImage */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/machine/loader.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/device.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/memory.h>
+#include <grub/dl.h>
+#include <grub/cpu/linux.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/mm.h>
+#include <grub/cpu/relocator.h>
+#include <grub/video.h>
+#include <grub/i386/floppy.h>
+#include <grub/lib/cmdline.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define GRUB_LINUX_CL_OFFSET 0x9000
+#define GRUB_LINUX_CL_END_OFFSET 0x90FF
+
+static grub_dl_t my_mod;
+
+static grub_size_t linux_mem_size;
+static int loaded;
+static struct grub_relocator *relocator = NULL;
+static grub_addr_t grub_linux_real_target;
+static char *grub_linux_real_chunk;
+static grub_size_t grub_linux16_prot_size;
+
+static grub_err_t
+grub_linux16_boot (void)
+{
+ grub_uint16_t segment;
+ struct grub_relocator16_state state;
+
+ segment = grub_linux_real_target >> 4;
+ state.gs = state.fs = state.es = state.ds = state.ss = segment;
+ state.sp = GRUB_LINUX_SETUP_STACK;
+ state.cs = segment + 0x20;
+ state.ip = 0;
+
+ grub_video_set_mode ("text", 0, 0);
+
+ grub_stop_floppy ();
+
+ return grub_relocator16_boot (relocator, state);
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ grub_relocator_unload (relocator);
+ relocator = NULL;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ struct linux_kernel_header lh;
+ grub_uint8_t setup_sects;
+ grub_size_t real_size;
+ grub_ssize_t len;
+ int i;
+ char *grub_linux_prot_chunk;
+ int grub_linux_is_bzimage;
+ grub_addr_t grub_linux_prot_target;
+ grub_err_t err;
+
+ grub_dl_ref (my_mod);
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
+ goto fail;
+ }
+
+ if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
+ goto fail;
+ }
+
+ if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
+ goto fail;
+ }
+
+ grub_linux_is_bzimage = 0;
+ setup_sects = lh.setup_sects;
+ linux_mem_size = 0;
+
+ if (lh.header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
+ && grub_le_to_cpu16 (lh.version) >= 0x0200)
+ {
+ grub_linux_is_bzimage = (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL);
+ lh.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
+
+ /* Put the real mode part at as a high location as possible. */
+ grub_linux_real_target = grub_mmap_get_lower ()
+ - GRUB_LINUX_SETUP_MOVE_SIZE;
+ /* But it must not exceed the traditional area. */
+ if (grub_linux_real_target > GRUB_LINUX_OLD_REAL_MODE_ADDR)
+ grub_linux_real_target = GRUB_LINUX_OLD_REAL_MODE_ADDR;
+
+ if (grub_le_to_cpu16 (lh.version) >= 0x0201)
+ {
+ lh.heap_end_ptr = grub_cpu_to_le16 (GRUB_LINUX_HEAP_END_OFFSET);
+ lh.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
+ }
+
+ if (grub_le_to_cpu16 (lh.version) >= 0x0202)
+ lh.cmd_line_ptr = grub_linux_real_target + GRUB_LINUX_CL_OFFSET;
+ else
+ {
+ lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
+ lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
+ lh.setup_move_size = grub_cpu_to_le16 (GRUB_LINUX_SETUP_MOVE_SIZE);
+ }
+ }
+ else
+ {
+ /* Your kernel is quite old... */
+ lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
+ lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
+
+ setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
+
+ grub_linux_real_target = GRUB_LINUX_OLD_REAL_MODE_ADDR;
+ }
+
+ /* If SETUP_SECTS is not set, set it to the default (4). */
+ if (! setup_sects)
+ setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
+
+ real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
+ grub_linux16_prot_size = grub_file_size (file)
+ - real_size - GRUB_DISK_SECTOR_SIZE;
+
+ if (! grub_linux_is_bzimage
+ && GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size
+ > grub_linux_real_target)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "too big zImage (0x%x > 0x%x), use bzImage instead",
+ (char *) GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size,
+ (grub_size_t) grub_linux_real_target);
+ goto fail;
+ }
+
+ if (grub_linux_real_target + GRUB_LINUX_SETUP_MOVE_SIZE
+ > grub_mmap_get_lower ())
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "too small lower memory (0x%x > 0x%x)",
+ grub_linux_real_target + GRUB_LINUX_SETUP_MOVE_SIZE,
+ (int) grub_mmap_get_lower ());
+ goto fail;
+ }
+
+ grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
+ grub_linux_is_bzimage ? "bzImage" : "zImage", real_size,
+ grub_linux16_prot_size);
+
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ goto fail;
+
+ for (i = 1; i < argc; i++)
+ if (grub_memcmp (argv[i], "vga=", 4) == 0)
+ {
+ /* Video mode selection support. */
+ grub_uint16_t vid_mode;
+ char *val = argv[i] + 4;
+
+ if (grub_strcmp (val, "normal") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
+ else if (grub_strcmp (val, "ext") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
+ else if (grub_strcmp (val, "ask") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_ASK;
+ else
+ vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
+
+ if (grub_errno)
+ goto fail;
+
+ lh.vid_mode = grub_cpu_to_le16 (vid_mode);
+ }
+ else if (grub_memcmp (argv[i], "mem=", 4) == 0)
+ {
+ char *val = argv[i] + 4;
+
+ linux_mem_size = grub_strtoul (val, &val, 0);
+
+ if (grub_errno)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ linux_mem_size = 0;
+ }
+ else
+ {
+ int shift = 0;
+
+ switch (grub_tolower (val[0]))
+ {
+ case 'g':
+ shift += 10;
+ case 'm':
+ shift += 10;
+ case 'k':
+ shift += 10;
+ default:
+ break;
+ }
+
+ /* Check an overflow. */
+ if (linux_mem_size > (~0UL >> shift))
+ linux_mem_size = 0;
+ else
+ linux_mem_size <<= shift;
+ }
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ grub_linux_real_target,
+ GRUB_LINUX_SETUP_MOVE_SIZE);
+ if (err)
+ return err;
+ grub_linux_real_chunk = get_virtual_current_address (ch);
+ }
+
+ /* Put the real mode code at the temporary address. */
+ grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh));
+
+ len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh);
+ if (grub_file_read (file, grub_linux_real_chunk + sizeof (lh), len) != len)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
+ || grub_le_to_cpu16 (lh.version) < 0x0200)
+ /* Clear the heap space. */
+ grub_memset (grub_linux_real_chunk
+ + ((setup_sects + 1) << GRUB_DISK_SECTOR_BITS),
+ 0,
+ ((GRUB_LINUX_MAX_SETUP_SECTS - setup_sects - 1)
+ << GRUB_DISK_SECTOR_BITS));
+
+ /* Create kernel command line. */
+ grub_memcpy ((char *)grub_linux_real_chunk + GRUB_LINUX_CL_OFFSET,
+ LINUX_IMAGE, sizeof (LINUX_IMAGE));
+ grub_create_loader_cmdline (argc, argv,
+ (char *)grub_linux_real_chunk
+ + GRUB_LINUX_CL_OFFSET + sizeof (LINUX_IMAGE) - 1,
+ GRUB_LINUX_CL_END_OFFSET - GRUB_LINUX_CL_OFFSET
+ - (sizeof (LINUX_IMAGE) - 1));
+
+ if (grub_linux_is_bzimage)
+ grub_linux_prot_target = GRUB_LINUX_BZIMAGE_ADDR;
+ else
+ grub_linux_prot_target = GRUB_LINUX_ZIMAGE_ADDR;
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ grub_linux_prot_target,
+ grub_linux16_prot_size);
+ if (err)
+ return err;
+ grub_linux_prot_chunk = get_virtual_current_address (ch);
+ }
+
+ len = grub_linux16_prot_size;
+ if (grub_file_read (file, grub_linux_prot_chunk, grub_linux16_prot_size)
+ != (grub_ssize_t) grub_linux16_prot_size)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ grub_loader_set (grub_linux16_boot, grub_linux_unload, 0);
+ loaded = 1;
+ }
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ grub_relocator_unload (relocator);
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_ssize_t size;
+ grub_addr_t addr_max, addr_min;
+ struct linux_kernel_header *lh;
+ grub_uint8_t *initrd_chunk;
+ grub_addr_t initrd_addr;
+ grub_err_t err;
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
+ goto fail;
+ }
+
+ if (!loaded)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
+ goto fail;
+ }
+
+ lh = (struct linux_kernel_header *) grub_linux_real_chunk;
+
+ if (!(lh->header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
+ && grub_le_to_cpu16 (lh->version) >= 0x0200))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "the kernel is too old for initrd");
+ goto fail;
+ }
+
+ /* Get the highest address available for the initrd. */
+ if (grub_le_to_cpu16 (lh->version) >= 0x0203)
+ {
+ addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);
+
+ /* XXX in reality, Linux specifies a bogus value, so
+ it is necessary to make sure that ADDR_MAX does not exceed
+ 0x3fffffff. */
+ if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
+ addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
+ }
+ else
+ addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
+
+ if (linux_mem_size != 0 && linux_mem_size < addr_max)
+ addr_max = linux_mem_size;
+
+ /* Linux 2.3.xx has a bug in the memory range check, so avoid
+ the last page.
+ Linux 2.2.xx has a bug in the memory range check, which is
+ worse than that of Linux 2.3.xx, so avoid the last 64kb. */
+ addr_max -= 0x10000;
+
+ addr_min = GRUB_LINUX_BZIMAGE_ADDR + grub_linux16_prot_size;
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (argv[0]);
+ if (!file)
+ goto fail;
+
+ size = grub_file_size (file);
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ addr_min, addr_max - size,
+ size, 0x1000,
+ GRUB_RELOCATOR_PREFERENCE_HIGH);
+ if (err)
+ return err;
+ initrd_chunk = get_virtual_current_address (ch);
+ initrd_addr = get_physical_target_address (ch);
+ }
+
+ if (grub_file_read (file, initrd_chunk, size) != size)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ lh->ramdisk_image = initrd_addr;
+ lh->ramdisk_size = size;
+
+ fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_linux, cmd_initrd;
+
+GRUB_MOD_INIT(linux16)
+{
+ cmd_linux =
+ grub_register_command ("linux16", grub_cmd_linux,
+ 0, N_("Load Linux."));
+ cmd_initrd =
+ grub_register_command ("initrd16", grub_cmd_initrd,
+ 0, N_("Load initrd."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(linux16)
+{
+ grub_unregister_command (cmd_linux);
+ grub_unregister_command (cmd_initrd);
+}
diff --git a/grub-core/loader/i386/pc/ntldr.c b/grub-core/loader/i386/pc/ntldr.c
new file mode 100644
index 0000000..4a08b54
--- /dev/null
+++ b/grub-core/loader/i386/pc/ntldr.c
@@ -0,0 +1,159 @@
+/* chainloader.c - boot another boot loader */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2004,2007,2009,2010 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/device.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/partition.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/machine/biosnum.h>
+#include <grub/i18n.h>
+#include <grub/video.h>
+#include <grub/mm.h>
+#include <grub/cpu/relocator.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+static struct grub_relocator *rel;
+static grub_uint32_t edx = 0xffffffff;
+
+#define GRUB_NTLDR_SEGMENT 0x2000
+
+static grub_err_t
+grub_ntldr_boot (void)
+{
+ struct grub_relocator16_state state = {
+ .cs = GRUB_NTLDR_SEGMENT,
+ .ip = 0,
+ .ds = 0,
+ .es = 0,
+ .fs = 0,
+ .gs = 0,
+ .ss = 0,
+ .sp = 0x7c00,
+ .edx = edx
+ };
+ grub_video_set_mode ("text", 0, 0);
+
+ return grub_relocator16_boot (rel, state);
+}
+
+static grub_err_t
+grub_ntldr_unload (void)
+{
+ grub_relocator_unload (rel);
+ rel = NULL;
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_ntldr (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_err_t err;
+ void *bs, *ntldr;
+ grub_size_t ntldrsize;
+ grub_device_t dev;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified");
+
+ grub_dl_ref (my_mod);
+
+ rel = grub_relocator_new ();
+ if (!rel)
+ goto fail;
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, 0x7C00,
+ GRUB_DISK_SECTOR_SIZE);
+ if (err)
+ goto fail;
+ bs = get_virtual_current_address (ch);
+ }
+
+ edx = grub_get_root_biosnumber ();
+ dev = grub_device_open (0);
+
+ if (dev && dev->disk)
+ {
+ err = grub_disk_read (dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, bs);
+ if (err)
+ {
+ grub_device_close (dev);
+ goto fail;
+ }
+ }
+
+ if (dev)
+ grub_device_close (dev);
+
+ ntldrsize = grub_file_size (file);
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_NTLDR_SEGMENT << 4,
+ ntldrsize);
+ if (err)
+ goto fail;
+ ntldr = get_virtual_current_address (ch);
+ }
+
+ if (grub_file_read (file, ntldr, ntldrsize)
+ != (grub_ssize_t) ntldrsize)
+ goto fail;
+
+ grub_loader_set (grub_ntldr_boot, grub_ntldr_unload, 1);
+ return GRUB_ERR_NONE;
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ grub_ntldr_unload ();
+
+ return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(ntldr)
+{
+ cmd = grub_register_command ("ntldr", grub_cmd_ntldr,
+ 0, N_("Load NTLDR or BootMGR."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(ntldr)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c
new file mode 100644
index 0000000..b877b0e
--- /dev/null
+++ b/grub-core/loader/i386/xnu.c
@@ -0,0 +1,1137 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/loader.h>
+#include <grub/autoefi.h>
+#include <grub/i386/tsc.h>
+#include <grub/efi/api.h>
+#include <grub/i386/pit.h>
+#include <grub/misc.h>
+#include <grub/charset.h>
+#include <grub/term.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/bitmap_scale.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+#define DEFAULT_VIDEO_MODE "auto"
+
+char grub_xnu_cmdline[1024];
+grub_uint32_t grub_xnu_entry_point, grub_xnu_arg1, grub_xnu_stack;
+
+/* Aliases set for some tables. */
+struct tbl_alias
+{
+ grub_efi_guid_t guid;
+ char *name;
+};
+
+static struct tbl_alias table_aliases[] =
+ {
+ {GRUB_EFI_ACPI_20_TABLE_GUID, "ACPI_20"},
+ {GRUB_EFI_ACPI_TABLE_GUID, "ACPI"},
+ };
+
+struct grub_xnu_devprop_device_descriptor
+{
+ struct grub_xnu_devprop_device_descriptor *next;
+ struct property_descriptor *properties;
+ struct grub_efi_device_path *path;
+ int pathlen;
+};
+
+static int
+utf16_strlen (grub_uint16_t *in)
+{
+ int i;
+ for (i = 0; in[i]; i++);
+ return i;
+}
+
+/* Read frequency from a string in MHz and return it in Hz. */
+static grub_uint64_t
+readfrequency (const char *str)
+{
+ grub_uint64_t num = 0;
+ int mul = 1000000;
+ int found = 0;
+
+ while (*str)
+ {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9)
+ break;
+
+ found = 1;
+
+ num = num * 10 + digit;
+ str++;
+ }
+ num *= 1000000;
+ if (*str == '.')
+ {
+ str++;
+ while (*str)
+ {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9)
+ break;
+
+ found = 1;
+
+ mul /= 10;
+ num = num + mul * digit;
+ str++;
+ }
+ }
+ if (! found)
+ return 0;
+
+ return num;
+}
+
+/* Thanks to Kabyl for precious information about Intel architecture. */
+static grub_uint64_t
+guessfsb (void)
+{
+ const grub_uint64_t sane_value = 100000000;
+ grub_uint32_t manufacturer[3], max_cpuid, capabilities, msrlow;
+ grub_uint64_t start_tsc;
+ grub_uint64_t end_tsc;
+ grub_uint64_t tsc_ticks_per_ms;
+
+ if (! grub_cpu_is_cpuid_supported ())
+ return sane_value;
+
+#ifdef APPLE_CC
+ asm volatile ("movl $0, %%eax\n"
+#ifdef __x86_64__
+ "push %%rbx\n"
+#else
+ "push %%ebx\n"
+#endif
+ "cpuid\n"
+#ifdef __x86_64__
+ "pop %%rbx\n"
+#else
+ "pop %%ebx\n"
+#endif
+ : "=a" (max_cpuid),
+ "=d" (manufacturer[1]), "=c" (manufacturer[2]));
+
+ /* Only Intel for now is done. */
+ if (grub_memcmp (manufacturer + 1, "ineIntel", 12) != 0)
+ return sane_value;
+
+#else
+ asm volatile ("movl $0, %%eax\n"
+ "cpuid"
+ : "=a" (max_cpuid), "=b" (manufacturer[0]),
+ "=d" (manufacturer[1]), "=c" (manufacturer[2]));
+
+ /* Only Intel for now is done. */
+ if (grub_memcmp (manufacturer, "GenuineIntel", 12) != 0)
+ return sane_value;
+#endif
+
+ /* Check Speedstep. */
+ if (max_cpuid < 1)
+ return sane_value;
+
+#ifdef APPLE_CC
+ asm volatile ("movl $1, %%eax\n"
+#ifdef __x86_64__
+ "push %%rbx\n"
+#else
+ "push %%ebx\n"
+#endif
+ "cpuid\n"
+#ifdef __x86_64__
+ "pop %%rbx\n"
+#else
+ "pop %%ebx\n"
+#endif
+ : "=c" (capabilities):
+ : "%rax", "%rdx");
+#else
+ asm volatile ("movl $1, %%eax\n"
+ "cpuid"
+ : "=c" (capabilities):
+ : "%rax", "%rbx", "%rdx");
+#endif
+
+ if (! (capabilities & (1 << 7)))
+ return sane_value;
+
+ /* Calibrate the TSC rate. */
+
+ start_tsc = grub_get_tsc ();
+ grub_pit_wait (0xffff);
+ end_tsc = grub_get_tsc ();
+
+ tsc_ticks_per_ms = grub_divmod64 (end_tsc - start_tsc, 55, 0);
+
+ /* Read the multiplier. */
+ asm volatile ("movl $0x198, %%ecx\n"
+ "rdmsr"
+ : "=d" (msrlow)
+ :
+ : "%ecx", "%eax");
+
+ return grub_divmod64 (2000 * tsc_ticks_per_ms,
+ ((msrlow >> 7) & 0x3e) + ((msrlow >> 14) & 1), 0);
+}
+
+struct property_descriptor
+{
+ struct property_descriptor *next;
+ grub_uint8_t *name;
+ grub_uint16_t *name16;
+ int name16len;
+ int length;
+ void *data;
+};
+
+static struct grub_xnu_devprop_device_descriptor *devices = 0;
+
+grub_err_t
+grub_xnu_devprop_remove_property (struct grub_xnu_devprop_device_descriptor *dev,
+ char *name)
+{
+ struct property_descriptor *prop;
+ prop = grub_named_list_find (GRUB_AS_NAMED_LIST_P (&dev->properties), name);
+ if (!prop)
+ return GRUB_ERR_NONE;
+
+ grub_free (prop->name);
+ grub_free (prop->name16);
+ grub_free (prop->data);
+
+ grub_list_remove (GRUB_AS_LIST_P (&dev->properties), GRUB_AS_LIST (prop));
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_devprop_remove_device (struct grub_xnu_devprop_device_descriptor *dev)
+{
+ void *t;
+ struct property_descriptor *prop;
+
+ grub_list_remove (GRUB_AS_LIST_P (&devices), GRUB_AS_LIST (dev));
+
+ for (prop = dev->properties; prop; )
+ {
+ grub_free (prop->name);
+ grub_free (prop->name16);
+ grub_free (prop->data);
+ t = prop;
+ prop = prop->next;
+ grub_free (t);
+ }
+
+ grub_free (dev->path);
+ grub_free (dev);
+
+ return GRUB_ERR_NONE;
+}
+
+struct grub_xnu_devprop_device_descriptor *
+grub_xnu_devprop_add_device (struct grub_efi_device_path *path, int length)
+{
+ struct grub_xnu_devprop_device_descriptor *ret;
+
+ ret = grub_zalloc (sizeof (*ret));
+ if (!ret)
+ return 0;
+
+ ret->path = grub_malloc (length);
+ if (!ret->path)
+ {
+ grub_free (ret);
+ return 0;
+ }
+ ret->pathlen = length;
+ grub_memcpy (ret->path, path, length);
+
+ grub_list_push (GRUB_AS_LIST_P (&devices), GRUB_AS_LIST (ret));
+
+ return ret;
+}
+
+static grub_err_t
+grub_xnu_devprop_add_property (struct grub_xnu_devprop_device_descriptor *dev,
+ grub_uint8_t *utf8, grub_uint16_t *utf16,
+ int utf16len, void *data, int datalen)
+{
+ struct property_descriptor *prop;
+
+ prop = grub_malloc (sizeof (*prop));
+ if (!prop)
+ return grub_errno;
+
+ prop->name = utf8;
+ prop->name16 = utf16;
+ prop->name16len = utf16len;
+
+ prop->length = datalen;
+ prop->data = grub_malloc (prop->length);
+ if (!prop->data)
+ {
+ grub_free (prop);
+ grub_free (prop->name);
+ grub_free (prop->name16);
+ return grub_errno;
+ }
+ grub_memcpy (prop->data, data, prop->length);
+ grub_list_push (GRUB_AS_LIST_P (&dev->properties),
+ GRUB_AS_LIST (prop));
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_devprop_add_property_utf8 (struct grub_xnu_devprop_device_descriptor *dev,
+ char *name, void *data, int datalen)
+{
+ grub_uint8_t *utf8;
+ grub_uint16_t *utf16;
+ int len, utf16len;
+ grub_err_t err;
+
+ utf8 = (grub_uint8_t *) grub_strdup (name);
+ if (!utf8)
+ return grub_errno;
+
+ len = grub_strlen (name);
+ utf16 = grub_malloc (sizeof (grub_uint16_t) * len);
+ if (!utf16)
+ {
+ grub_free (utf8);
+ return grub_errno;
+ }
+
+ utf16len = grub_utf8_to_utf16 (utf16, len, utf8, len, NULL);
+ if (utf16len < 0)
+ {
+ grub_free (utf8);
+ grub_free (utf16);
+ return grub_errno;
+ }
+
+ err = grub_xnu_devprop_add_property (dev, utf8, utf16,
+ utf16len, data, datalen);
+ if (err)
+ {
+ grub_free (utf8);
+ grub_free (utf16);
+ return err;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_devprop_add_property_utf16 (struct grub_xnu_devprop_device_descriptor *dev,
+ grub_uint16_t *name, int namelen,
+ void *data, int datalen)
+{
+ grub_uint8_t *utf8;
+ grub_uint16_t *utf16;
+ grub_err_t err;
+
+ utf16 = grub_malloc (sizeof (grub_uint16_t) * namelen);
+ if (!utf16)
+ return grub_errno;
+ grub_memcpy (utf16, name, sizeof (grub_uint16_t) * namelen);
+
+ utf8 = grub_malloc (namelen * 4 + 1);
+ if (!utf8)
+ {
+ grub_free (utf8);
+ return grub_errno;
+ }
+
+ *grub_utf16_to_utf8 ((grub_uint8_t *) utf8, name, namelen) = '\0';
+
+ err = grub_xnu_devprop_add_property (dev, utf8, utf16,
+ namelen, data, datalen);
+ if (err)
+ {
+ grub_free (utf8);
+ grub_free (utf16);
+ return err;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static inline int
+hextoval (char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
+ return 0;
+}
+
+void
+grub_cpu_xnu_unload (void)
+{
+ struct grub_xnu_devprop_device_descriptor *dev1, *dev2;
+
+ for (dev1 = devices; dev1; )
+ {
+ dev2 = dev1->next;
+ grub_xnu_devprop_remove_device (dev1);
+ dev1 = dev2;
+ }
+}
+
+static grub_err_t
+grub_cpu_xnu_fill_devprop (void)
+{
+ struct grub_xnu_devtree_key *efikey;
+ int total_length = sizeof (struct grub_xnu_devprop_header);
+ struct grub_xnu_devtree_key *devprop;
+ struct grub_xnu_devprop_device_descriptor *device;
+ void *ptr;
+ struct grub_xnu_devprop_header *head;
+ void *t;
+ int numdevs = 0;
+
+ /* The key "efi". */
+ efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
+ if (! efikey)
+ return grub_errno;
+
+ for (device = devices; device; device = device->next)
+ {
+ struct property_descriptor *propdesc;
+ total_length += sizeof (struct grub_xnu_devprop_device_header);
+ total_length += device->pathlen;
+
+ for (propdesc = device->properties; propdesc; propdesc = propdesc->next)
+ {
+ total_length += sizeof (grub_uint32_t);
+ total_length += sizeof (grub_uint16_t)
+ * (propdesc->name16len + 1);
+ total_length += sizeof (grub_uint32_t);
+ total_length += propdesc->length;
+ }
+ numdevs++;
+ }
+
+ devprop = grub_xnu_create_value (&(efikey->first_child), "device-properties");
+ if (devprop)
+ {
+ devprop->data = grub_malloc (total_length);
+ devprop->datasize = total_length;
+ }
+
+ ptr = devprop->data;
+ head = ptr;
+ ptr = head + 1;
+ head->length = total_length;
+ head->alwaysone = 1;
+ head->num_devices = numdevs;
+ for (device = devices; device; )
+ {
+ struct grub_xnu_devprop_device_header *devhead;
+ struct property_descriptor *propdesc;
+ devhead = ptr;
+ devhead->num_values = 0;
+ ptr = devhead + 1;
+
+ grub_memcpy (ptr, device->path, device->pathlen);
+ ptr = (char *) ptr + device->pathlen;
+
+ for (propdesc = device->properties; propdesc; )
+ {
+ grub_uint32_t *len;
+ grub_uint16_t *name;
+ void *data;
+
+ len = ptr;
+ *len = 2 * propdesc->name16len + sizeof (grub_uint16_t)
+ + sizeof (grub_uint32_t);
+ ptr = len + 1;
+
+ name = ptr;
+ grub_memcpy (name, propdesc->name16, 2 * propdesc->name16len);
+ name += propdesc->name16len;
+
+ /* NUL terminator. */
+ *name = 0;
+ ptr = name + 1;
+
+ len = ptr;
+ *len = propdesc->length + sizeof (grub_uint32_t);
+ data = len + 1;
+ ptr = data;
+ grub_memcpy (ptr, propdesc->data, propdesc->length);
+ ptr = (char *) ptr + propdesc->length;
+
+ grub_free (propdesc->name);
+ grub_free (propdesc->name16);
+ grub_free (propdesc->data);
+ t = propdesc;
+ propdesc = propdesc->next;
+ grub_free (t);
+ devhead->num_values++;
+ }
+
+ devhead->length = (char *) ptr - (char *) devhead;
+ t = device;
+ device = device->next;
+ grub_free (t);
+ }
+
+ devices = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_devprop_load (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *buf, *bufstart, *bufend;
+ struct grub_xnu_devprop_header *head;
+ grub_size_t size;
+ unsigned i, j;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ file = grub_file_open (args[0]);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "couldn't load device-propertie dump");
+ size = grub_file_size (file);
+ buf = grub_malloc (size);
+ if (!buf)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+ if (grub_file_read (file, buf, size) != (grub_ssize_t) size)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+ grub_file_close (file);
+
+ bufstart = buf;
+ bufend = (char *) buf + size;
+ head = buf;
+ buf = head + 1;
+ for (i = 0; i < grub_le_to_cpu32 (head->num_devices) && buf < bufend; i++)
+ {
+ struct grub_efi_device_path *dp, *dpstart;
+ struct grub_xnu_devprop_device_descriptor *dev;
+ struct grub_xnu_devprop_device_header *devhead;
+
+ devhead = buf;
+ buf = devhead + 1;
+ dpstart = buf;
+
+ do
+ {
+ dp = buf;
+ buf = (char *) buf + GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+ }
+ while (!GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp) && buf < bufend);
+
+ dev = grub_xnu_devprop_add_device (dpstart, (char *) buf
+ - (char *) dpstart);
+
+ for (j = 0; j < grub_le_to_cpu32 (devhead->num_values) && buf < bufend;
+ j++)
+ {
+ grub_uint32_t *namelen;
+ grub_uint32_t *datalen;
+ grub_uint16_t *utf16;
+ void *data;
+ grub_err_t err;
+
+ namelen = buf;
+ buf = namelen + 1;
+ if (buf >= bufend)
+ break;
+
+ utf16 = buf;
+ buf = (char *) buf + *namelen - sizeof (grub_uint32_t);
+ if (buf >= bufend)
+ break;
+
+ datalen = buf;
+ buf = datalen + 1;
+ if (buf >= bufend)
+ break;
+
+ data = buf;
+ buf = (char *) buf + *datalen - sizeof (grub_uint32_t);
+ if (buf >= bufend)
+ break;
+ err = grub_xnu_devprop_add_property_utf16
+ (dev, utf16, (*namelen - sizeof (grub_uint32_t)
+ - sizeof (grub_uint16_t)) / sizeof (grub_uint16_t),
+ data, *datalen - sizeof (grub_uint32_t));
+ if (err)
+ {
+ grub_free (bufstart);
+ return err;
+ }
+ }
+ }
+
+ grub_free (bufstart);
+ return GRUB_ERR_NONE;
+}
+
+/* Fill device tree. */
+/* FIXME: some entries may be platform-agnostic. Move them to loader/xnu.c. */
+grub_err_t
+grub_cpu_xnu_fill_devicetree (void)
+{
+ struct grub_xnu_devtree_key *efikey;
+ struct grub_xnu_devtree_key *cfgtablekey;
+ struct grub_xnu_devtree_key *curval;
+ struct grub_xnu_devtree_key *runtimesrvkey;
+ struct grub_xnu_devtree_key *platformkey;
+ unsigned i, j;
+
+ /* The value "model". */
+ /* FIXME: may this value be sometimes different? */
+ curval = grub_xnu_create_value (&grub_xnu_devtree_root, "model");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof ("ACPI");
+ curval->data = grub_strdup ("ACPI");
+ curval = grub_xnu_create_value (&grub_xnu_devtree_root, "compatible");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof ("ACPI");
+ curval->data = grub_strdup ("ACPI");
+
+ /* The key "efi". */
+ efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
+ if (! efikey)
+ return grub_errno;
+
+ /* Information about firmware. */
+ curval = grub_xnu_create_value (&(efikey->first_child), "firmware-revision");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = (SYSTEM_TABLE_SIZEOF (firmware_revision));
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ grub_memcpy (curval->data, (SYSTEM_TABLE_VAR(firmware_revision)),
+ curval->datasize);
+
+ curval = grub_xnu_create_value (&(efikey->first_child), "firmware-vendor");
+ if (! curval)
+ return grub_errno;
+ curval->datasize =
+ 2 * (utf16_strlen (SYSTEM_TABLE_PTR (firmware_vendor)) + 1);
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ grub_memcpy (curval->data, SYSTEM_TABLE_PTR (firmware_vendor),
+ curval->datasize);
+
+ curval = grub_xnu_create_value (&(efikey->first_child), "firmware-abi");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof ("EFI32");
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ if (SIZEOF_OF_UINTN == 4)
+ grub_memcpy (curval->data, "EFI32", curval->datasize);
+ else
+ grub_memcpy (curval->data, "EFI64", curval->datasize);
+
+ /* The key "platform". */
+ platformkey = grub_xnu_create_key (&(efikey->first_child),
+ "platform");
+ if (! platformkey)
+ return grub_errno;
+
+ /* Pass FSB frequency to the kernel. */
+ curval = grub_xnu_create_value (&(platformkey->first_child), "FSBFrequency");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof (grub_uint64_t);
+ curval->data = grub_malloc (curval->datasize);
+ if (!curval->data)
+ return grub_errno;
+
+ /* First see if user supplies the value. */
+ char *fsbvar = grub_env_get ("fsb");
+ if (! fsbvar)
+ *((grub_uint64_t *) curval->data) = 0;
+ else
+ *((grub_uint64_t *) curval->data) = readfrequency (fsbvar);
+ /* Try autodetect. */
+ if (! *((grub_uint64_t *) curval->data))
+ *((grub_uint64_t *) curval->data) = guessfsb ();
+ grub_dprintf ("xnu", "fsb autodetected as %llu\n",
+ (unsigned long long) *((grub_uint64_t *) curval->data));
+
+ cfgtablekey = grub_xnu_create_key (&(efikey->first_child),
+ "configuration-table");
+ if (!cfgtablekey)
+ return grub_errno;
+
+ /* Fill "configuration-table" key. */
+ for (i = 0; i < SYSTEM_TABLE (num_table_entries); i++)
+ {
+ void *ptr;
+ struct grub_xnu_devtree_key *curkey;
+ grub_efi_guid_t guid;
+ char guidbuf[64];
+
+ /* Retrieve current key. */
+#ifdef GRUB_MACHINE_EFI
+ {
+ ptr = (void *)
+ grub_efi_system_table->configuration_table[i].vendor_table;
+ guid = grub_efi_system_table->configuration_table[i].vendor_guid;
+ }
+#else
+ if (SIZEOF_OF_UINTN == 4)
+ {
+ ptr = UINT_TO_PTR (((grub_efiemu_configuration_table32_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i]
+ .vendor_table);
+ guid =
+ ((grub_efiemu_configuration_table32_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
+ }
+ else
+ {
+ ptr = UINT_TO_PTR (((grub_efiemu_configuration_table64_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i]
+ .vendor_table);
+ guid =
+ ((grub_efiemu_configuration_table64_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
+ }
+#endif
+
+ /* The name of key for new table. */
+ grub_snprintf (guidbuf, sizeof (guidbuf), "%08x-%04x-%04x-%02x%02x-",
+ guid.data1, guid.data2, guid.data3, guid.data4[0],
+ guid.data4[1]);
+ for (j = 2; j < 8; j++)
+ grub_snprintf (guidbuf + grub_strlen (guidbuf),
+ sizeof (guidbuf) - grub_strlen (guidbuf),
+ "%02x", guid.data4[j]);
+ /* For some reason GUID has to be in uppercase. */
+ for (j = 0; guidbuf[j] ; j++)
+ if (guidbuf[j] >= 'a' && guidbuf[j] <= 'f')
+ guidbuf[j] += 'A' - 'a';
+ curkey = grub_xnu_create_key (&(cfgtablekey->first_child), guidbuf);
+ if (! curkey)
+ return grub_errno;
+
+ curval = grub_xnu_create_value (&(curkey->first_child), "guid");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof (guid);
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ grub_memcpy (curval->data, &guid, curval->datasize);
+
+ /* The value "table". */
+ curval = grub_xnu_create_value (&(curkey->first_child), "table");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = SIZEOF_OF_UINTN;
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ if (SIZEOF_OF_UINTN == 4)
+ *((grub_uint32_t *)curval->data) = PTR_TO_UINT32 (ptr);
+ else
+ *((grub_uint64_t *)curval->data) = PTR_TO_UINT64 (ptr);
+
+ /* Create alias. */
+ for (j = 0; j < sizeof (table_aliases) / sizeof (table_aliases[0]); j++)
+ if (grub_memcmp (&table_aliases[j].guid, &guid, sizeof (guid)) == 0)
+ break;
+ if (j != sizeof (table_aliases) / sizeof (table_aliases[0]))
+ {
+ curval = grub_xnu_create_value (&(curkey->first_child), "alias");
+ if (!curval)
+ return grub_errno;
+ curval->datasize = grub_strlen (table_aliases[j].name) + 1;
+ curval->data = grub_malloc (curval->datasize);
+ if (!curval->data)
+ return grub_errno;
+ grub_memcpy (curval->data, table_aliases[j].name, curval->datasize);
+ }
+ }
+
+ /* Create and fill "runtime-services" key. */
+ runtimesrvkey = grub_xnu_create_key (&(efikey->first_child),
+ "runtime-services");
+ if (! runtimesrvkey)
+ return grub_errno;
+ curval = grub_xnu_create_value (&(runtimesrvkey->first_child), "table");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = SIZEOF_OF_UINTN;
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ if (SIZEOF_OF_UINTN == 4)
+ *((grub_uint32_t *) curval->data)
+ = PTR_TO_UINT32 (SYSTEM_TABLE_PTR (runtime_services));
+ else
+ *((grub_uint64_t *) curval->data)
+ = PTR_TO_UINT64 (SYSTEM_TABLE_PTR (runtime_services));
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_boot_resume (void)
+{
+ struct grub_relocator32_state state;
+
+ state.esp = grub_xnu_stack;
+ state.ebp = grub_xnu_stack;
+ state.eip = grub_xnu_entry_point;
+ state.eax = grub_xnu_arg1;
+
+ return grub_relocator32_boot (grub_xnu_relocator, state);
+}
+
+/* Setup video for xnu. */
+static grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *params)
+{
+ struct grub_video_mode_info mode_info;
+ int ret;
+ char *tmp;
+ const char *modevar;
+ void *framebuffer;
+ grub_err_t err;
+ struct grub_video_bitmap *bitmap = NULL;
+
+ modevar = grub_env_get ("gfxpayload");
+ /* Consider only graphical 32-bit deep modes. */
+ if (! modevar || *modevar == 0)
+ err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
+ GRUB_VIDEO_MODE_TYPE_PURE_TEXT
+ | GRUB_VIDEO_MODE_TYPE_DEPTH_MASK,
+ 32 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS);
+ else
+ {
+ tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
+ if (! tmp)
+ return grub_errno;
+ err = grub_video_set_mode (tmp,
+ GRUB_VIDEO_MODE_TYPE_PURE_TEXT
+ | GRUB_VIDEO_MODE_TYPE_DEPTH_MASK,
+ 32 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS);
+ grub_free (tmp);
+ }
+
+ if (err)
+ return err;
+
+ ret = grub_video_get_info (&mode_info);
+ if (ret)
+ return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters");
+
+ if (grub_xnu_bitmap)
+ {
+ if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH)
+ err = grub_video_bitmap_create_scaled (&bitmap,
+ mode_info.width,
+ mode_info.height,
+ grub_xnu_bitmap,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ else
+ bitmap = grub_xnu_bitmap;
+ }
+
+ if (bitmap)
+ {
+ if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH)
+ err = grub_video_bitmap_create_scaled (&bitmap,
+ mode_info.width,
+ mode_info.height,
+ grub_xnu_bitmap,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ else
+ bitmap = grub_xnu_bitmap;
+ }
+
+ if (bitmap)
+ {
+ int x, y;
+
+ x = mode_info.width - bitmap->mode_info.width;
+ x /= 2;
+ y = mode_info.height - bitmap->mode_info.height;
+ y /= 2;
+ err = grub_video_blit_bitmap (bitmap,
+ GRUB_VIDEO_BLIT_REPLACE,
+ x > 0 ? x : 0,
+ y > 0 ? y : 0,
+ x < 0 ? -x : 0,
+ y < 0 ? -y : 0,
+ min (bitmap->mode_info.width,
+ mode_info.width),
+ min (bitmap->mode_info.height,
+ mode_info.height));
+ }
+ if (err)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ bitmap = 0;
+ }
+
+ ret = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+ if (ret)
+ return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters");
+
+ params->lfb_width = mode_info.width;
+ params->lfb_height = mode_info.height;
+ params->lfb_depth = mode_info.bpp;
+ params->lfb_line_len = mode_info.pitch;
+
+ params->lfb_base = PTR_TO_UINT32 (framebuffer);
+ params->lfb_mode = bitmap ? GRUB_XNU_VIDEO_SPLASH
+ : GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
+
+ return GRUB_ERR_NONE;
+}
+
+/* Boot xnu. */
+grub_err_t
+grub_xnu_boot (void)
+{
+ struct grub_xnu_boot_params *bootparams;
+ void *bp_in;
+ grub_addr_t bootparams_target;
+ grub_err_t err;
+ grub_efi_uintn_t memory_map_size = 0;
+ void *memory_map;
+ grub_addr_t memory_map_target;
+ grub_efi_uintn_t map_key = 0;
+ grub_efi_uintn_t descriptor_size = 0;
+ grub_efi_uint32_t descriptor_version = 0;
+ grub_uint64_t firstruntimepage, lastruntimepage;
+ grub_uint64_t curruntimepage;
+ grub_addr_t devtree_target;
+ grub_size_t devtreelen;
+ int i;
+ struct grub_relocator32_state state;
+
+ err = grub_autoefi_prepare ();
+ if (err)
+ return err;
+
+ err = grub_cpu_xnu_fill_devprop ();
+ if (err)
+ return err;
+
+ err = grub_cpu_xnu_fill_devicetree ();
+ if (err)
+ return err;
+
+ err = grub_xnu_fill_devicetree ();
+ if (err)
+ return err;
+
+ /* Page-align to avoid following parts to be inadvertently freed. */
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+
+ /* Pass memory map to kernel. */
+ memory_map_size = 0;
+ memory_map = 0;
+ map_key = 0;
+ descriptor_size = 0;
+ descriptor_version = 0;
+
+ grub_dprintf ("xnu", "eip=%x\n", grub_xnu_entry_point);
+
+ const char *debug = grub_env_get ("debug");
+
+ if (debug && (grub_strword (debug, "all") || grub_strword (debug, "xnu")))
+ {
+ grub_printf ("Press any key to launch xnu\n");
+ grub_getkey ();
+ }
+
+ /* Relocate the boot parameters to heap. */
+ err = grub_xnu_heap_malloc (sizeof (*bootparams),
+ &bp_in, &bootparams_target);
+ if (err)
+ return err;
+ bootparams = bp_in;
+
+ /* Set video. */
+ err = grub_xnu_set_video (bootparams);
+ if (err != GRUB_ERR_NONE)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ grub_printf ("Booting in blind mode\n");
+
+ bootparams->lfb_mode = 0;
+ bootparams->lfb_width = 0;
+ bootparams->lfb_height = 0;
+ bootparams->lfb_depth = 0;
+ bootparams->lfb_line_len = 0;
+ bootparams->lfb_base = 0;
+ }
+
+ if (grub_autoefi_get_memory_map (&memory_map_size, memory_map,
+ &map_key, &descriptor_size,
+ &descriptor_version) < 0)
+ return grub_errno;
+
+ /* We will do few allocations later. Reserve some space for possible
+ memory map growth. */
+ memory_map_size += 20 * descriptor_size;
+ err = grub_xnu_heap_malloc (memory_map_size,
+ &memory_map, &memory_map_target);
+ if (err)
+ return err;
+
+ err = grub_xnu_writetree_toheap (&devtree_target, &devtreelen);
+ if (err)
+ return err;
+
+ grub_memcpy (bootparams->cmdline, grub_xnu_cmdline,
+ sizeof (bootparams->cmdline));
+
+ bootparams->devtree = devtree_target;
+ bootparams->devtreelen = devtreelen;
+
+ err = grub_autoefi_finish_boot_services (&memory_map_size, memory_map,
+ &map_key, &descriptor_size,
+ &descriptor_version);
+ if (err)
+ return err;
+
+ bootparams->efi_system_table = PTR_TO_UINT32 (grub_autoefi_system_table);
+
+ firstruntimepage = (((grub_addr_t) grub_xnu_heap_target_start
+ + grub_xnu_heap_size + GRUB_XNU_PAGESIZE - 1)
+ / GRUB_XNU_PAGESIZE) + 20;
+ curruntimepage = firstruntimepage;
+
+ for (i = 0; (unsigned) i < memory_map_size / descriptor_size; i++)
+ {
+ grub_efi_memory_descriptor_t *curdesc = (grub_efi_memory_descriptor_t *)
+ ((char *) memory_map + descriptor_size * i);
+
+ curdesc->virtual_start = curdesc->physical_start;
+
+ if (curdesc->type == GRUB_EFI_RUNTIME_SERVICES_DATA
+ || curdesc->type == GRUB_EFI_RUNTIME_SERVICES_CODE)
+ {
+ curdesc->virtual_start = curruntimepage << 12;
+ curruntimepage += curdesc->num_pages;
+ if (curdesc->physical_start
+ <= PTR_TO_UINT64 (grub_autoefi_system_table)
+ && curdesc->physical_start + (curdesc->num_pages << 12)
+ > PTR_TO_UINT64 (grub_autoefi_system_table))
+ bootparams->efi_system_table
+ = PTR_TO_UINT64 (grub_autoefi_system_table)
+ - curdesc->physical_start + curdesc->virtual_start;
+ if (SIZEOF_OF_UINTN == 8 && grub_xnu_is_64bit)
+ curdesc->virtual_start |= 0xffffff8000000000ULL;
+ }
+ }
+
+ lastruntimepage = curruntimepage;
+
+ bootparams->efi_mmap = memory_map_target;
+ bootparams->efi_mmap_size = memory_map_size;
+ bootparams->efi_mem_desc_size = descriptor_size;
+ bootparams->efi_mem_desc_version = descriptor_version;
+
+ bootparams->heap_start = grub_xnu_heap_target_start;
+ bootparams->heap_size = grub_xnu_heap_size;
+ bootparams->efi_runtime_first_page = firstruntimepage;
+
+ bootparams->efi_runtime_npages = lastruntimepage - firstruntimepage;
+ bootparams->efi_uintnbits = SIZEOF_OF_UINTN * 8;
+
+ bootparams->verminor = GRUB_XNU_BOOTARGS_VERMINOR;
+ bootparams->vermajor = GRUB_XNU_BOOTARGS_VERMAJOR;
+
+ /* Parameters for asm helper. */
+ grub_xnu_stack = bootparams->heap_start
+ + bootparams->heap_size + GRUB_XNU_PAGESIZE;
+ grub_xnu_arg1 = bootparams_target;
+
+ grub_autoefi_set_virtual_address_map (memory_map_size, descriptor_size,
+ descriptor_version, memory_map);
+
+ state.eip = grub_xnu_entry_point;
+ state.eax = grub_xnu_arg1;
+ state.esp = grub_xnu_stack;
+ state.ebp = grub_xnu_stack;
+ return grub_relocator32_boot (grub_xnu_relocator, state);
+}
+
+static grub_command_t cmd_devprop_load;
+
+void
+grub_cpu_xnu_init (void)
+{
+ cmd_devprop_load = grub_register_command ("xnu_devprop_load",
+ grub_cmd_devprop_load,
+ 0, N_("Load device-properties dump."));
+}
+
+void
+grub_cpu_xnu_fini (void)
+{
+ grub_unregister_command (cmd_devprop_load);
+}