summaryrefslogtreecommitdiffstats
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.c2191
-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.c679
-rw-r--r--grub-core/loader/i386/bsd_pagetable.c92
-rw-r--r--grub-core/loader/i386/coreboot/chainloader.c517
-rw-r--r--grub-core/loader/i386/linux.c1141
-rw-r--r--grub-core/loader/i386/multiboot_mbi.c756
-rw-r--r--grub-core/loader/i386/pc/chainloader.c310
-rw-r--r--grub-core/loader/i386/pc/freedos.c190
-rw-r--r--grub-core/loader/i386/pc/linux.c494
-rw-r--r--grub-core/loader/i386/pc/ntldr.c162
-rw-r--r--grub-core/loader/i386/pc/plan9.c607
-rw-r--r--grub-core/loader/i386/pc/pxechainloader.c168
-rw-r--r--grub-core/loader/i386/pc/truecrypt.c233
-rw-r--r--grub-core/loader/i386/xen.c986
-rw-r--r--grub-core/loader/i386/xen_file.c117
-rw-r--r--grub-core/loader/i386/xen_file32.c7
-rw-r--r--grub-core/loader/i386/xen_file64.c7
-rw-r--r--grub-core/loader/i386/xen_fileXX.c395
-rw-r--r--grub-core/loader/i386/xnu.c1166
21 files changed, 10230 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..5f3290c
--- /dev/null
+++ b/grub-core/loader/i386/bsd.c
@@ -0,0 +1,2191 @@
+/*
+ * 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>
+#include <grub/bsdlabel.h>
+#include <grub/crypto.h>
+#include <grub/safemath.h>
+#include <grub/verify.h>
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/machine/int.h>
+#endif
+
+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 CD-ROM 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 root device."), 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."),
+ /* TRANSLATORS: "com" is static and not to be translated. It refers to
+ serial ports e.g. com1.
+ */
+ 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_("Suppress 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."),
+ /* TRANSLATORS: "com" is static and not to be translated. It refers to
+ serial ports e.g. com1.
+ */
+ 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);
+}
+
+static grub_err_t
+grub_bsd_add_meta_ptr (grub_uint32_t type, void **ptr, 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;
+ *ptr = newtag->data;
+
+ if (kernel_type == KERNEL_TYPE_FREEBSD
+ && type == (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_SMAP))
+ {
+ struct bsd_tag *p;
+ for (p = tags;
+ p && 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;
+}
+
+grub_err_t
+grub_bsd_add_meta (grub_uint32_t type, const void *data, grub_uint32_t len)
+{
+ grub_err_t err;
+ void *ptr;
+
+ err = grub_bsd_add_meta_ptr (type, &ptr, len);
+ if (err)
+ return err;
+ if (len)
+ grub_memcpy (ptr, data, len);
+ return GRUB_ERR_NONE;
+}
+
+
+struct grub_e820_mmap
+{
+ grub_uint64_t addr;
+ grub_uint64_t size;
+ grub_uint32_t type;
+} GRUB_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
+#define GRUB_E820_COREBOOT_TABLES 0x10
+
+/* Context for generate_e820_mmap. */
+struct generate_e820_mmap_ctx
+{
+ int count;
+ struct grub_e820_mmap *mmap;
+ struct grub_e820_mmap prev, cur;
+};
+
+/* Helper for generate_e820_mmap. */
+static int
+generate_e820_mmap_iter (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type, void *data)
+{
+ struct generate_e820_mmap_ctx *ctx = data;
+
+ ctx->cur.addr = addr;
+ ctx->cur.size = size;
+
+ if (type == GRUB_MEMORY_COREBOOT_TABLES
+ && addr == 0)
+ /* Nowadays the tables at 0 don't contain anything important but
+ *BSD needs the memory at 0 for own needs.
+ */
+ type = GRUB_E820_RAM;
+
+ ctx->cur.type = type;
+
+ /* Merge regions if possible. */
+ if (ctx->count && ctx->cur.type == ctx->prev.type
+ && ctx->cur.addr == ctx->prev.addr + ctx->prev.size)
+ {
+ ctx->prev.size += ctx->cur.size;
+ if (ctx->mmap)
+ ctx->mmap[-1] = ctx->prev;
+ }
+ else
+ {
+ if (ctx->mmap)
+ *ctx->mmap++ = ctx->cur;
+ ctx->prev = ctx->cur;
+ ctx->count++;
+ }
+
+ if (kernel_type == KERNEL_TYPE_OPENBSD && ctx->prev.addr < 0x100000
+ && ctx->prev.addr + ctx->prev.size > 0x100000)
+ {
+ ctx->cur.addr = 0x100000;
+ ctx->cur.size = ctx->prev.addr + ctx->prev.size - 0x100000;
+ ctx->cur.type = ctx->prev.type;
+ ctx->prev.size = 0x100000 - ctx->prev.addr;
+ if (ctx->mmap)
+ {
+ ctx->mmap[-1] = ctx->prev;
+ ctx->mmap[0] = ctx->cur;
+ ctx->mmap++;
+ }
+ ctx->prev = ctx->cur;
+ ctx->count++;
+ }
+
+ return 0;
+}
+
+static void
+generate_e820_mmap (grub_size_t *len, grub_size_t *cnt, void *buf)
+{
+ struct generate_e820_mmap_ctx ctx = {
+ .count = 0,
+ .mmap = buf
+ };
+
+ grub_mmap_iterate (generate_e820_mmap_iter, &ctx);
+
+ if (len)
+ *len = ctx.count * sizeof (struct grub_e820_mmap);
+ *cnt = ctx.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 (const char *filename, const char *type,
+ int argc, char **argv,
+ grub_addr_t addr, grub_uint32_t size)
+{
+ const char *name;
+ grub_err_t err;
+
+ 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)
+ {
+ void *cmdline;
+ char *p;
+
+ if (grub_bsd_add_meta_ptr (FREEBSD_MODINFO_ARGS, &cmdline, n))
+ return grub_errno;
+
+ p = cmdline;
+ for (i = 0; i < argc; i++)
+ {
+ grub_strcpy (p, argv[i]);
+ p += grub_strlen (argv[i]);
+ *(p++) = ' ';
+ }
+ *p = 0;
+ err = grub_verify_string (cmdline, GRUB_VERIFY_MODULE_CMDLINE);
+ if (err)
+ return err;
+ }
+ }
+
+ 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_uint64_t freebsd_zfsguid;
+
+static void
+freebsd_get_zfs (void)
+{
+ grub_device_t dev;
+ grub_fs_t fs;
+ char *uuid;
+ grub_err_t err;
+
+ dev = grub_device_open (0);
+ if (!dev)
+ return;
+ fs = grub_fs_probe (dev);
+ if (!fs)
+ return;
+ if (!fs->fs_uuid || grub_strcmp (fs->name, "zfs") != 0)
+ return;
+ err = fs->fs_uuid (dev, &uuid);
+ if (err)
+ return;
+ if (!uuid)
+ return;
+ freebsd_zfsguid = grub_strtoull (uuid, 0, 16);
+ grub_free (uuid);
+}
+
+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;
+
+ struct grub_env_var *var;
+
+ grub_memset (&bi, 0, sizeof (bi));
+ bi.version = FREEBSD_BOOTINFO_VERSION;
+ bi.length = sizeof (bi);
+
+ bi.boot_device = freebsd_biosdev;
+
+ p_size = 0;
+ FOR_SORTED_ENV (var)
+ if ((grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1) == 0) && (var->name[sizeof("kFreeBSD.") - 1]))
+ {
+ p_size += grub_strlen (&var->name[sizeof("kFreeBSD.") - 1]);
+ p_size++;
+ p_size += grub_strlen (var->value) + 1;
+ }
+
+ 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;
+
+ FOR_SORTED_ENV (var)
+ if ((grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1) == 0) && (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;
+ }
+
+ 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,
+ 0);
+ 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,
+ 0);
+ 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] = freebsd_zfsguid ? 4 : 0;
+ stack[4] = freebsd_zfsguid;
+ stack[5] = freebsd_zfsguid >> 32;
+ stack[6] = stack_target + 9 * sizeof (grub_uint32_t);
+ stack[7] = bi.tags;
+ stack[8] = kern_end;
+ return grub_relocator32_boot (relocator, state, 0);
+ }
+
+ /* 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;
+
+#ifdef GRUB_MACHINE_PCBIOS
+ {
+ struct grub_bios_int_registers regs;
+
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+
+ regs.ebx = 0;
+ regs.ecx = 0;
+ regs.eax = 0xb101;
+ regs.es = 0;
+ regs.edi = 0;
+ regs.edx = 0;
+
+ grub_bios_interrupt (0x1a, &regs);
+ if (regs.edx == 0x20494350)
+ {
+ struct grub_openbsd_bootarg_pcibios pcibios;
+
+ pcibios.characteristics = regs.eax & 0xff;
+ pcibios.revision = regs.ebx & 0xffff;
+ pcibios.pm_entry = regs.edi;
+ pcibios.last_bus = regs.ecx & 0xff;
+
+ grub_bsd_add_meta (OPENBSD_BOOTARG_PCIBIOS, &pcibios,
+ sizeof (pcibios));
+ }
+ }
+#endif
+
+ {
+ 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, 0);
+}
+
+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;
+ grub_size_t sz;
+
+ for (mod = netbsd_mods; mod; mod = mod->next)
+ modcnt++;
+
+ if (grub_mul (modcnt, sizeof (mods->mods[0]), &sz) ||
+ grub_add (sz, sizeof (*mods), &sz))
+ return GRUB_ERR_OUT_OF_RANGE;
+
+ mods = grub_malloc (sz);
+ 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;
+}
+
+/*
+ * Adds NetBSD bootinfo bootdisk and bootwedge. The partition identified
+ * in these bootinfo fields is the root device.
+ */
+static void
+grub_netbsd_add_boot_disk_and_wedge (void)
+{
+ grub_device_t dev;
+ grub_disk_t disk;
+ grub_partition_t part;
+ grub_uint32_t biosdev;
+ grub_uint32_t partmapsector;
+ union {
+ grub_uint64_t raw[GRUB_DISK_SECTOR_SIZE / 8];
+ struct grub_partition_bsd_disk_label label;
+ } buf;
+
+ if (GRUB_MD_MD5->mdlen > GRUB_CRYPTO_MAX_MDLEN)
+ {
+ grub_error (GRUB_ERR_BUG, "mdlen too long");
+ return;
+ }
+
+ dev = grub_device_open (0);
+ if (! (dev && dev->disk && dev->disk->partition))
+ goto fail;
+
+ disk = dev->disk;
+ part = disk->partition;
+
+ if (disk->dev && disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID)
+ biosdev = (grub_uint32_t) disk->id & 0xff;
+ else
+ biosdev = 0xff;
+
+ /* Absolute sector of the partition map describing this partition. */
+ partmapsector = grub_partition_get_start (part->parent) + part->offset;
+
+ disk->partition = part->parent;
+ if (grub_disk_read (disk, part->offset, 0, GRUB_DISK_SECTOR_SIZE, buf.raw)
+ != GRUB_ERR_NONE)
+ goto fail;
+ disk->partition = part;
+
+ /* Fill bootwedge. */
+ {
+ struct grub_netbsd_btinfo_bootwedge biw;
+ grub_uint8_t hash[GRUB_CRYPTO_MAX_MDLEN];
+
+ grub_memset (&biw, 0, sizeof (biw));
+ biw.biosdev = biosdev;
+ biw.startblk = grub_partition_get_start (part);
+ biw.nblks = part->len;
+ biw.matchblk = partmapsector;
+ biw.matchnblks = 1;
+
+ grub_crypto_hash (GRUB_MD_MD5, hash,
+ buf.raw, GRUB_DISK_SECTOR_SIZE);
+ grub_memcpy (biw.matchhash, hash, 16);
+
+ grub_bsd_add_meta (NETBSD_BTINFO_BOOTWEDGE, &biw, sizeof (biw));
+ }
+
+ /* Fill bootdisk. */
+ {
+ struct grub_netbsd_btinfo_bootdisk bid;
+
+ grub_memset (&bid, 0, sizeof (bid));
+ /* Check for a NetBSD disk label. */
+ if (part->partmap != NULL &&
+ (grub_strcmp (part->partmap->name, "netbsd") == 0 ||
+ (part->parent == NULL && grub_strcmp (part->partmap->name, "bsd") == 0)))
+ {
+ bid.labelsector = partmapsector;
+ bid.label.type = buf.label.type;
+ bid.label.checksum = buf.label.checksum;
+ grub_memcpy (bid.label.packname, buf.label.packname, 16);
+ }
+ else
+ {
+ bid.labelsector = -1;
+ }
+ bid.biosdev = biosdev;
+ bid.partition = part->number;
+
+ grub_bsd_add_meta (NETBSD_BTINFO_BOOTDISK, &bid, sizeof (bid));
+ }
+
+fail:
+ if (dev)
+ grub_device_close (dev);
+}
+
+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_puts_ (N_("Booting in blind mode"));
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ err = grub_netbsd_add_modules ();
+ if (err)
+ return err;
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_bsd_add_meta (NETBSD_BTINFO_EFI,
+ &grub_efi_system_table,
+ sizeof (grub_efi_system_table));
+ if (err)
+ return err;
+#endif
+
+ {
+ 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,
+ 0);
+ 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, 0);
+}
+
+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, const char *filename)
+{
+ 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))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_READ_ERROR, N_("premature end of file %s"),
+ filename);
+ return grub_errno;
+ }
+
+ 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 grub_err_t
+grub_bsd_load_elf (grub_elf_t elf, const char *filename)
+{
+ grub_err_t err;
+
+ kern_end = 0;
+ kern_start = ~0;
+
+ if (grub_elf_is_elf32 (elf))
+ {
+ grub_relocator_chunk_t ch;
+ Elf32_Phdr *phdr;
+
+ entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFFF;
+
+ FOR_ELF32_PHDRS (elf, phdr)
+ {
+ Elf32_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ continue;
+
+ paddr = phdr->p_paddr & 0xFFFFFFF;
+
+ if (paddr < kern_start)
+ kern_start = paddr;
+
+ if (paddr + phdr->p_memsz > kern_end)
+ kern_end = paddr + phdr->p_memsz;
+ }
+
+ if (grub_errno)
+ return grub_errno;
+ 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, filename, (grub_uint8_t *) kern_chunk_src - kern_start, GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC | GRUB_ELF_LOAD_FLAGS_28BITS, 0, 0);
+ if (err)
+ return err;
+ if (kernel_type != KERNEL_TYPE_OPENBSD)
+ return GRUB_ERR_NONE;
+ return grub_openbsd_find_ramdisk32 (elf->file, filename, kern_start,
+ kern_chunk_src, &openbsd_ramdisk);
+ }
+ else if (grub_elf_is_elf64 (elf))
+ {
+ Elf64_Phdr *phdr;
+
+ 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;
+ }
+
+ FOR_ELF64_PHDRS (elf, phdr)
+ {
+ Elf64_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ continue;
+
+ paddr = phdr->p_paddr & 0xFFFFFFF;
+
+ if (paddr < kern_start)
+ kern_start = paddr;
+
+ if (paddr + phdr->p_memsz > kern_end)
+ kern_end = paddr + phdr->p_memsz;
+ }
+
+ if (grub_errno)
+ return grub_errno;
+
+ 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, filename,
+ (grub_uint8_t *) kern_chunk_src - kern_start, GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC | GRUB_ELF_LOAD_FLAGS_28BITS, 0, 0);
+ if (err)
+ return err;
+ if (kernel_type != KERNEL_TYPE_OPENBSD)
+ return GRUB_ERR_NONE;
+ return grub_openbsd_find_ramdisk64 (elf->file, filename, kern_start,
+ kern_chunk_src, &openbsd_ramdisk);
+ }
+ else
+ return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
+}
+
+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, N_("filename expected"));
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_BSD_KERNEL);
+ if (!file)
+ goto fail;
+
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ {
+ grub_file_close (file);
+ goto fail;
+ }
+
+ elf = grub_elf_file (file, argv[0]);
+ if (elf)
+ {
+ is_elf_kernel = 1;
+ grub_bsd_load_elf (elf, argv[0]);
+ grub_elf_close (elf);
+ }
+ else
+ {
+ is_elf_kernel = 0;
+ grub_errno = 0;
+ grub_bsd_load_aout (file, argv[0]);
+ 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], GRUB_FILE_TYPE_BSD_KERNEL);
+ if (! file)
+ return grub_errno;
+
+ if (is_64bit)
+ err = grub_freebsd_load_elf_meta64 (relocator, file, argv[0],
+ &kern_end);
+ else
+ err = grub_freebsd_load_elf_meta32 (relocator, file, argv[0],
+ &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_zfsguid = 0;
+ if (!is_64bit)
+ freebsd_get_zfs ();
+ grub_print_error ();
+ 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 const char *types[] = {
+ [0] = "wd",
+ [2] = "fd",
+ [4] = "sd",
+ [6] = "cd",
+ [14] = "vnd",
+ [17] = "rd"
+};
+
+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 && ctxt->state[OPENBSD_ROOT_ARG].arg != NULL)
+ {
+ const char *arg = ctxt->state[OPENBSD_ROOT_ARG].arg;
+ unsigned type, unit, part;
+ for (type = 0; type < ARRAY_SIZE (types); type++)
+ if (types[type]
+ && grub_strncmp (arg, types[type],
+ grub_strlen (types[type])) == 0)
+ {
+ arg += grub_strlen (types[type]);
+ break;
+ }
+ if (type == ARRAY_SIZE (types))
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "unknown disk type name");
+
+ unit = grub_strtoul (arg, &arg, 10);
+ if (! (*arg >= 'a' && *arg <= 'z'))
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only device specifications of form "
+ "<type><number><lowercase letter> are supported");
+
+ part = *arg - 'a';
+
+ bootdev = (OPENBSD_B_DEVMAGIC | (type << OPENBSD_B_TYPESHIFT)
+ | (unit << OPENBSD_B_UNITSHIFT)
+ | (part << OPENBSD_B_PARTSHIFT));
+ }
+ else
+ bootdev = 0;
+
+ if (ctxt->state[OPENBSD_SERIAL_ARG].set)
+ {
+ struct grub_openbsd_bootarg_console serial;
+ const 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;
+ serial.addr = grub_ns8250_hw_get_port (port);
+
+ 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);
+ serial.addr = 0xffffffff;
+ 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], GRUB_FILE_TYPE_BSD_KERNEL);
+ if (! file)
+ return grub_errno;
+
+ if (is_64bit)
+ err = grub_netbsd_load_elf_meta64 (relocator, file, argv[0], &kern_end);
+ else
+ err = grub_netbsd_load_elf_meta32 (relocator, file, argv[0], &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;
+ const 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_netbsd_add_boot_disk_and_wedge ();
+
+ 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,
+ N_("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, N_("filename expected"));
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_FREEBSD_ENV);
+ 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;
+ const char *type;
+ grub_err_t err;
+ void *src;
+
+ if (! grub_loader_is_loaded ())
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("you need to load the kernel first"));
+
+ 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], GRUB_FILE_TYPE_FREEBSD_MODULE);
+ 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, GRUB_FILE_TYPE_NETBSD_MODULE);
+ 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 (! grub_loader_is_loaded ())
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("you need to load the kernel first"));
+
+ 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,
+ N_("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], GRUB_FILE_TYPE_FREEBSD_MODULE_ELF);
+ 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, N_("filename expected"));
+
+ if (! grub_loader_is_loaded ())
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("you need to load the kernel first"));
+
+ 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], GRUB_FILE_TYPE_OPENBSD_RAMDISK);
+ if (! file)
+ return grub_errno;
+
+ 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 %" PRIuGRUB_SIZE " bytes, however you supplied"
+ " a %" PRIuGRUB_SIZE " bytes one",
+ openbsd_ramdisk.max_size, size);
+ }
+
+ if (grub_file_read (file, openbsd_ramdisk.target, size)
+ != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
+ return grub_errno;
+ }
+ 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,
+ /* TRANSLATORS: ramdisk isn't identifier,
+ it can be translated. */
+ N_("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..a8d8bf7
--- /dev/null
+++ b/grub-core/loader/i386/bsdXX.c
@@ -0,0 +1,679 @@
+#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>
+#include <grub/i18n.h>
+
+#define ALIGN_PAGE(a) ALIGN_UP (a, 4096)
+
+static inline grub_err_t
+load (grub_file_t file, const char *filename, 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;
+ return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ }
+ return GRUB_ERR_NONE;
+}
+
+static inline grub_err_t
+read_headers (grub_file_t file, const char *filename, 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;
+ return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ }
+
+ 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, N_("invalid arch-independent ELF magic"));
+
+ if (e->e_ident[EI_CLASS] != SUFFIX (ELFCLASS))
+ return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
+
+ *shdr = grub_calloc (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, (grub_uint32_t) e->e_shnum * e->e_shentsize)
+ != (grub_ssize_t) ((grub_uint32_t) e->e_shnum * e->e_shentsize))
+ {
+ if (grub_errno)
+ return grub_errno;
+ return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ }
+
+ 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;
+
+ curload = module = ALIGN_PAGE (*kern_end);
+
+ err = read_headers (file, argv[0], &e, &shdr);
+ if (err)
+ goto out;
+
+ 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)
+ goto out;
+ 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, argv[0], (grub_uint8_t *) chunk_src + curload - *kern_end,
+ s->sh_offset, s->sh_size);
+ if (err)
+ goto out;
+ 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);
+
+out:
+ grub_free (shdr);
+ 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;
+
+ curload = module = ALIGN_PAGE (*kern_end);
+
+ err = read_headers (file, argv[0], &e, &shdr);
+ if (err)
+ goto out;
+
+ 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 += (grub_uint32_t) e.e_phnum * e.e_phentsize;
+ chunk_size += (grub_uint32_t) e.e_shnum * e.e_shentsize;
+
+ {
+ grub_relocator_chunk_t ch;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ module, chunk_size);
+ if (err)
+ goto out;
+
+ 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, argv[0],
+ (grub_uint8_t *) chunk_src + module
+ + s->sh_addr - *kern_end,
+ s->sh_offset, s->sh_size);
+ if (err)
+ goto out;
+ 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, argv[0], (grub_uint8_t *) chunk_src + module - *kern_end, 0, sizeof (e));
+ if (curload < module + sizeof (e))
+ curload = module + sizeof (e);
+
+ load (file, argv[0], (grub_uint8_t *) chunk_src + curload - *kern_end, e.e_shoff,
+ (grub_uint32_t) e.e_shnum * e.e_shentsize);
+ e.e_shoff = curload - module;
+ curload += (grub_uint32_t) e.e_shnum * e.e_shentsize;
+
+ load (file, argv[0], (grub_uint8_t *) chunk_src + curload - *kern_end, e.e_phoff,
+ (grub_uint32_t) e.e_phnum * e.e_phentsize);
+ e.e_phoff = curload - module;
+ curload += (grub_uint32_t) 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);
+out:
+ grub_free (shdr);
+ if (err)
+ return err;
+ return SUFFIX (grub_freebsd_load_elf_meta) (relocator, file, argv[0], kern_end);
+}
+
+#endif
+
+grub_err_t
+SUFFIX (grub_freebsd_load_elf_meta) (struct grub_relocator *relocator,
+ grub_file_t file,
+ const char *filename,
+ 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, filename, &e, &shdr);
+ if (err)
+ goto out;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_ELFHDR, &e,
+ sizeof (e));
+ if (err)
+ goto out;
+
+ 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))
+ {
+ err = grub_error (GRUB_ERR_BAD_OS, N_("no symbol table"));
+ goto out;
+ }
+ 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)
+ goto out;
+ 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)
+ {
+ err = grub_errno;
+ goto out;
+ }
+ sym = (Elf_Sym *) curload;
+ if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize)
+ {
+ if (! grub_errno)
+ err = grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ else
+ err = grub_errno;
+ goto out;
+ }
+ curload += symsize;
+
+ *((grub_freebsd_addr_t *) curload) = strsize;
+ curload += sizeof (grub_freebsd_addr_t);
+ if (grub_file_seek (file, stroff) == (grub_off_t) -1)
+ {
+ err = grub_errno;
+ goto out;
+ }
+ str = (char *) curload;
+ if (grub_file_read (file, curload, strsize) != (grub_ssize_t) strsize)
+ {
+ if (! grub_errno)
+ err = grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ else
+ err = grub_errno;
+ goto out;
+ }
+
+ 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)
+ goto out;
+ }
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_SSYM, &symstart,
+ sizeof (symstart));
+ if (err)
+ goto out;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_ESYM, &symend,
+ sizeof (symend));
+out:
+ grub_free (shdr);
+ 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, const char *filename,
+ 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, filename, &e, &shdr);
+ if (err)
+ {
+ grub_free (shdr);
+ return grub_errno;
+ }
+
+ 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;
+ 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) + (grub_uint32_t) 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)
+ goto out;
+ 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) + (grub_uint32_t) e.e_shnum * e.e_shentsize;
+ else if (s == strsh)
+ s2->sh_offset = ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t))
+ + sizeof (e) + (grub_uint32_t) 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)
+ {
+ err = grub_errno;
+ goto out;
+ }
+ if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize)
+ {
+ if (! grub_errno)
+ err = grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ else
+ err = grub_errno;
+ goto out;
+ }
+ curload += ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t));
+
+ if (grub_file_seek (file, strsh->sh_offset) == (grub_off_t) -1)
+ {
+ err = grub_errno;
+ goto out;
+ }
+ if (grub_file_read (file, curload, strsize) != (grub_ssize_t) strsize)
+ {
+ if (! grub_errno)
+ err = grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ else
+ err = grub_errno;
+ goto out;
+ }
+
+ err = grub_bsd_add_meta (NETBSD_BTINFO_SYMTAB,
+ &symtab,
+ sizeof (symtab));
+out:
+ grub_free (shdr);
+ 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,
+ const char *filename,
+ 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, filename, &e, &shdr);
+ if (err)
+ {
+ grub_free (shdr);
+ 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, N_("premature end of file %s"),
+ filename);
+ 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)
+ {
+ grub_free (syms);
+ grub_free (strs);
+ 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, N_("premature end of file %s"),
+ filename);
+ return grub_errno;
+ }
+
+ for (i = 0, sym = syms; i * symentsize < symsize;
+ 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/coreboot/chainloader.c b/grub-core/loader/i386/coreboot/chainloader.c
new file mode 100644
index 0000000..0a19ebb
--- /dev/null
+++ b/grub-core/loader/i386/coreboot/chainloader.c
@@ -0,0 +1,517 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2011 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/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/video.h>
+#include <grub/relocator.h>
+#include <grub/i386/relocator.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/cbfs_core.h>
+#include <grub/lib/LzmaDec.h>
+#include <grub/efi/pe32.h>
+#include <grub/i386/cpuid.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_addr_t entry;
+static struct grub_relocator *relocator = NULL;
+
+static grub_err_t
+grub_chain_boot (void)
+{
+ struct grub_relocator32_state state;
+
+ grub_video_set_mode ("text", 0, 0);
+
+ state.eip = entry;
+ return grub_relocator32_boot (relocator, state, 0);
+}
+
+static grub_err_t
+grub_chain_unload (void)
+{
+ grub_relocator_unload (relocator);
+ relocator = NULL;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+load_elf (grub_file_t file, const char *filename)
+{
+ grub_elf_t elf;
+ Elf32_Phdr *phdr;
+ grub_err_t err;
+
+ elf = grub_elf_file (file, filename);
+ if (!elf)
+ return grub_errno;
+
+ if (!grub_elf_is_elf32 (elf))
+ return grub_error (GRUB_ERR_BAD_OS, "only ELF32 can be coreboot payload");
+
+ entry = elf->ehdr.ehdr32.e_entry;
+
+ FOR_ELF32_PHDRS(elf, phdr)
+ {
+ grub_uint8_t *load_addr;
+ grub_relocator_chunk_t ch;
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ phdr->p_paddr, phdr->p_memsz);
+ if (err)
+ {
+ elf->file = 0;
+ grub_elf_close (elf);
+ return err;
+ }
+
+ load_addr = get_virtual_current_address (ch);
+
+ if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1)
+ {
+ elf->file = 0;
+ grub_elf_close (elf);
+ return grub_errno;
+ }
+
+ if (phdr->p_filesz)
+ {
+ grub_ssize_t read;
+ read = grub_file_read (elf->file, load_addr, phdr->p_filesz);
+ if (read != (grub_ssize_t) phdr->p_filesz)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR,
+ N_("premature end of file %s"),
+ filename);
+ elf->file = 0;
+ grub_elf_close (elf);
+ return grub_errno;
+ }
+ }
+
+ if (phdr->p_filesz < phdr->p_memsz)
+ grub_memset ((load_addr + phdr->p_filesz),
+ 0, phdr->p_memsz - phdr->p_filesz);
+ }
+
+ elf->file = 0;
+ grub_elf_close (elf);
+ return GRUB_ERR_NONE;
+}
+
+static void *SzAlloc(void *p __attribute__ ((unused)), size_t size) { return grub_malloc (size); }
+static void SzFree(void *p __attribute__ ((unused)), void *address) { grub_free (address); }
+static ISzAlloc g_Alloc = { SzAlloc, SzFree };
+
+
+static grub_err_t
+load_segment (grub_file_t file, const char *filename,
+ void *load_addr, grub_uint32_t comp,
+ grub_size_t *size, grub_size_t max_size)
+{
+ switch (comp)
+ {
+ case grub_cpu_to_be32_compile_time (CBFS_COMPRESS_NONE):
+ if (grub_file_read (file, load_addr, *size)
+ != (grub_ssize_t) *size)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR,
+ N_("premature end of file %s"),
+ filename);
+ return grub_errno;
+ }
+ return GRUB_ERR_NONE;
+ case grub_cpu_to_be32_compile_time (CBFS_COMPRESS_LZMA):
+ {
+ grub_uint8_t *buf;
+ grub_size_t outsize, insize;
+ SRes res;
+ SizeT src_len, dst_len;
+ ELzmaStatus status;
+ if (*size < 13)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid compressed chunk");
+ buf = grub_malloc (*size);
+ if (!buf)
+ return grub_errno;
+ if (grub_file_read (file, buf, *size)
+ != (grub_ssize_t) *size)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR,
+ N_("premature end of file %s"),
+ filename);
+ grub_free (buf);
+ return grub_errno;
+ }
+ outsize = grub_get_unaligned64 (buf + 5);
+ if (outsize > max_size)
+ {
+ grub_free (buf);
+ return grub_error (GRUB_ERR_BAD_OS, "invalid compressed chunk");
+ }
+ insize = *size - 13;
+
+ src_len = insize;
+ dst_len = outsize;
+ res = LzmaDecode (load_addr, &dst_len, buf + 13, &src_len,
+ buf, 5, LZMA_FINISH_END, &status, &g_Alloc);
+ /* ELzmaFinishMode finishMode,
+ ELzmaStatus *status, ISzAlloc *alloc)*/
+ grub_free (buf);
+ grub_dprintf ("chain", "%x, %x, %x, %x\n",
+ insize, src_len, outsize, dst_len);
+ if (res != SZ_OK
+ || src_len != insize || dst_len != outsize)
+ return grub_error (GRUB_ERR_BAD_OS, "decompression failure %d", res);
+ *size = outsize;
+ }
+ return GRUB_ERR_NONE;
+ default:
+ return grub_error (GRUB_ERR_BAD_OS, "unsupported compression %d",
+ grub_be_to_cpu32 (comp));
+ }
+}
+
+static grub_err_t
+load_tianocore (grub_file_t file)
+{
+ grub_uint16_t header_length;
+ grub_uint32_t section_head;
+ grub_uint8_t mz[2], pe[4];
+ struct grub_pe32_coff_header coff_head;
+ struct file_header
+ {
+ grub_uint8_t unused[18];
+ grub_uint8_t type;
+ grub_uint8_t unused2;
+ grub_uint8_t size[3];
+ grub_uint8_t unused3;
+ } file_head;
+ grub_relocator_chunk_t ch;
+
+ if (grub_file_seek (file, 48) == (grub_off_t) -1
+ || grub_file_read (file, &header_length, sizeof (header_length))
+ != sizeof (header_length)
+ || grub_file_seek (file, header_length) == (grub_off_t) -1)
+ goto fail;
+
+ while (1)
+ {
+ grub_off_t off;
+ if (grub_file_read (file, &file_head, sizeof (file_head))
+ != sizeof (file_head))
+ goto fail;
+ if (file_head.type != 0xf0)
+ break;
+ off = grub_get_unaligned32 (file_head.size) & 0xffffff;
+ if (off < sizeof (file_head))
+ goto fail;
+ if (grub_file_seek (file, grub_file_tell (file) + off
+ - sizeof (file_head)) == (grub_off_t) -1)
+ goto fail;
+ }
+
+ if (file_head.type != 0x03)
+ goto fail;
+
+ while (1)
+ {
+ if (grub_file_read (file, &section_head, sizeof (section_head))
+ != sizeof (section_head))
+ goto fail;
+ if ((section_head >> 24) != 0x19)
+ break;
+
+ if ((section_head & 0xffffff) < sizeof (section_head))
+ goto fail;
+
+ if (grub_file_seek (file, grub_file_tell (file)
+ + (section_head & 0xffffff)
+ - sizeof (section_head)) == (grub_off_t) -1)
+ goto fail;
+ }
+
+ if ((section_head >> 24) != 0x10)
+ goto fail;
+
+ grub_off_t exe_start = grub_file_tell (file);
+
+ if (grub_file_read (file, &mz, sizeof (mz)) != sizeof (mz))
+ goto fail;
+ if (mz[0] != 'M' || mz[1] != 'Z')
+ goto fail;
+
+ if (grub_file_seek (file, grub_file_tell (file) + 0x3a) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (file, &section_head, sizeof (section_head))
+ != sizeof (section_head))
+ goto fail;
+ if (section_head < 0x40)
+ goto fail;
+
+ if (grub_file_seek (file, grub_file_tell (file)
+ + section_head - 0x40) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (file, &pe, sizeof (pe))
+ != sizeof (pe))
+ goto fail;
+
+ if (pe[0] != 'P' || pe[1] != 'E' || pe[2] != '\0' || pe[3] != '\0')
+ goto fail;
+
+ if (grub_file_read (file, &coff_head, sizeof (coff_head))
+ != sizeof (coff_head))
+ goto fail;
+
+ grub_uint32_t loadaddr;
+
+ switch (coff_head.machine)
+ {
+ case GRUB_PE32_MACHINE_I386:
+ {
+ struct grub_pe32_optional_header oh;
+ if (grub_file_read (file, &oh, sizeof (oh))
+ != sizeof (oh))
+ goto fail;
+ if (oh.magic != GRUB_PE32_PE32_MAGIC)
+ goto fail;
+ loadaddr = oh.image_base - exe_start;
+ entry = oh.image_base + oh.entry_addr;
+ break;
+ }
+ case GRUB_PE32_MACHINE_X86_64:
+ {
+ struct grub_pe64_optional_header oh;
+ if (! grub_cpuid_has_longmode)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "your CPU does not implement AMD64 architecture");
+ goto fail;
+ }
+
+ if (grub_file_read (file, &oh, sizeof (oh))
+ != sizeof (oh))
+ goto fail;
+ if (oh.magic != GRUB_PE32_PE64_MAGIC)
+ goto fail;
+ loadaddr = oh.image_base - exe_start;
+ entry = oh.image_base + oh.entry_addr;
+ break;
+ }
+ default:
+ goto fail;
+ }
+ if (grub_file_seek (file, 0) == (grub_off_t) -1)
+ goto fail;
+
+ grub_size_t fz = grub_file_size (file);
+
+ if (grub_relocator_alloc_chunk_addr (relocator, &ch,
+ loadaddr, fz))
+ goto fail;
+
+ if (grub_file_read (file, get_virtual_current_address (ch), fz)
+ != (grub_ssize_t) fz)
+ goto fail;
+
+ return GRUB_ERR_NONE;
+
+ fail:
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, "fv volume is invalid");
+ return grub_errno;
+}
+
+static grub_err_t
+load_chewed (grub_file_t file, const char *filename)
+{
+ grub_size_t i;
+ for (i = 0;; i++)
+ {
+ struct cbfs_payload_segment segment;
+ grub_err_t err;
+
+ if (grub_file_seek (file, sizeof (segment) * i) == (grub_off_t) -1
+ || grub_file_read (file, &segment, sizeof (segment))
+ != sizeof (segment))
+ {
+ if (!grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS,
+ "payload is too short");
+ return grub_errno;
+ }
+
+ switch (segment.type)
+ {
+ case PAYLOAD_SEGMENT_PARAMS:
+ break;
+
+ case PAYLOAD_SEGMENT_ENTRY:
+ entry = grub_be_to_cpu64 (segment.load_addr);
+ return GRUB_ERR_NONE;
+
+ case PAYLOAD_SEGMENT_BSS:
+ segment.len = 0;
+ segment.offset = 0;
+ segment.len = 0;
+ /* Fallthrough. */
+ case PAYLOAD_SEGMENT_CODE:
+ case PAYLOAD_SEGMENT_DATA:
+ {
+ grub_uint32_t target = grub_be_to_cpu64 (segment.load_addr);
+ grub_uint32_t memsize = grub_be_to_cpu32 (segment.mem_len);
+ grub_uint32_t filesize = grub_be_to_cpu32 (segment.len);
+ grub_uint8_t *load_addr;
+ grub_relocator_chunk_t ch;
+
+ if (memsize < filesize)
+ memsize = filesize;
+
+ grub_dprintf ("chain", "%x+%x\n", target, memsize);
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ target, memsize);
+ if (err)
+ return err;
+
+ load_addr = get_virtual_current_address (ch);
+
+ if (filesize)
+ {
+ if (grub_file_seek (file, grub_be_to_cpu32 (segment.offset))
+ == (grub_off_t) -1)
+ return grub_errno;
+
+ err = load_segment (file, filename, load_addr,
+ segment.compression, &filesize, memsize);
+ if (err)
+ return err;
+ }
+
+ if (filesize < memsize)
+ grub_memset ((load_addr + filesize),
+ 0, memsize - filesize);
+ }
+ }
+ }
+}
+
+static grub_err_t
+grub_cmd_chain (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_err_t err;
+ grub_file_t file;
+ grub_uint32_t head;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ grub_loader_unset ();
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_COREBOOT_CHAINLOADER);
+ if (!file)
+ return grub_errno;
+
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+
+ if (grub_file_read (file, &head, sizeof (head)) != sizeof (head)
+ || grub_file_seek (file, 0) == (grub_off_t) -1)
+ {
+ grub_file_close (file);
+ grub_relocator_unload (relocator);
+ relocator = 0;
+ if (!grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS,
+ "payload is too short");
+ return grub_errno;
+ }
+
+ switch (head)
+ {
+ case ELFMAG0 | (ELFMAG1 << 8) | (ELFMAG2 << 16) | (ELFMAG3 << 24):
+ err = load_elf (file, argv[0]);
+ break;
+ case PAYLOAD_SEGMENT_CODE:
+ case PAYLOAD_SEGMENT_DATA:
+ case PAYLOAD_SEGMENT_PARAMS:
+ case PAYLOAD_SEGMENT_BSS:
+ case PAYLOAD_SEGMENT_ENTRY:
+ err = load_chewed (file, argv[0]);
+ break;
+
+ default:
+ if (grub_file_seek (file, 40) == (grub_off_t) -1
+ || grub_file_read (file, &head, sizeof (head)) != sizeof (head)
+ || grub_file_seek (file, 0) == (grub_off_t) -1
+ || head != 0x4856465f)
+ err = grub_error (GRUB_ERR_BAD_OS, "unrecognised payload type");
+ else
+ err = load_tianocore (file);
+ break;
+ }
+ grub_file_close (file);
+ if (err)
+ {
+ grub_relocator_unload (relocator);
+ relocator = 0;
+ return err;
+ }
+
+ grub_loader_set (grub_chain_boot, grub_chain_unload, 0);
+ return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd_chain;
+
+GRUB_MOD_INIT (chain)
+{
+ cmd_chain = grub_register_command ("chainloader", grub_cmd_chain,
+ N_("FILE"),
+ /* TRANSLATORS: "payload" is a term used
+ by coreboot and must be translated in
+ sync with coreboot. If unsure,
+ let it untranslated. */
+ N_("Load another coreboot payload"));
+}
+
+GRUB_MOD_FINI (chain)
+{
+ grub_unregister_command (cmd_chain);
+ grub_chain_unload ();
+}
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
new file mode 100644
index 0000000..9f74a96
--- /dev/null
+++ b/grub-core/loader/i386/linux.c
@@ -0,0 +1,1141 @@
+/*
+ * 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>
+#include <grub/linux.h>
+#include <grub/machine/kernel.h>
+#include <grub/safemath.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>
+#include <grub/efi/sb.h>
+#define HAS_VGA_TEXT 0
+#define DEFAULT_VIDEO_MODE "auto"
+#define ACCEPTS_PURE_TEXT 0
+#elif defined (GRUB_MACHINE_IEEE1275)
+#include <grub/ieee1275/ieee1275.h>
+#define HAS_VGA_TEXT 0
+#define DEFAULT_VIDEO_MODE "text"
+#define ACCEPTS_PURE_TEXT 1
+#else
+#include <grub/i386/pc/vbe.h>
+#include <grub/i386/pc/console.h>
+#define HAS_VGA_TEXT 1
+#define DEFAULT_VIDEO_MODE "text"
+#define ACCEPTS_PURE_TEXT 1
+#endif
+
+static grub_dl_t my_mod;
+
+static grub_size_t linux_mem_size;
+static int loaded;
+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_size_t prot_init_space;
+static struct grub_relocator *relocator = NULL;
+static void *efi_mmap_buf;
+static grub_size_t maximal_cmdline_size;
+static struct linux_kernel_params linux_params;
+static char *linux_cmdline;
+#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;
+} GRUB_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));
+}
+
+/* Helper for find_mmap_size. */
+static int
+count_hook (grub_uint64_t addr __attribute__ ((unused)),
+ grub_uint64_t size __attribute__ ((unused)),
+ grub_memory_type_t type __attribute__ ((unused)), void *data)
+{
+ grub_size_t *count = data;
+
+ (*count)++;
+ return 0;
+}
+
+/* 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;
+
+ grub_mmap_iterate (count_hook, &count);
+
+ 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;
+ prot_mode_mem = initrd_mem = 0;
+ 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 *align,
+ grub_size_t min_align, int relocatable,
+ grub_uint64_t preferred_address)
+{
+ grub_err_t err;
+
+ if (prot_size == 0)
+ prot_size = 1;
+
+ prot_size = page_align (prot_size);
+
+ /* 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. */
+
+ {
+ grub_relocator_chunk_t ch;
+ if (relocatable)
+ {
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ preferred_address,
+ preferred_address,
+ prot_size, 1,
+ GRUB_RELOCATOR_PREFERENCE_LOW,
+ 1);
+ for (; err && *align + 1 > min_align; (*align)--)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000,
+ UP_TO_TOP32 (prot_size),
+ prot_size, 1 << *align,
+ GRUB_RELOCATOR_PREFERENCE_LOW,
+ 1);
+ }
+ if (err)
+ goto fail;
+ }
+ else
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ preferred_address,
+ prot_size);
+ if (err)
+ goto fail;
+ prot_mode_mem = get_virtual_current_address (ch);
+ prot_mode_target = get_physical_target_address (ch);
+ }
+
+ grub_dprintf ("linux", "prot_mode_mem = %p, prot_mode_target = %lx, prot_size = %x\n",
+ prot_mode_mem, (unsigned long) prot_mode_target,
+ (unsigned) prot_size);
+ return GRUB_ERR_NONE;
+
+ fail:
+ free_pages ();
+ return err;
+}
+
+static grub_err_t
+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 > 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)++;
+ }
+ return GRUB_ERR_NONE;
+}
+
+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;
+ const 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;
+
+#if defined (GRUB_MACHINE_EFI) && defined (__x86_64__)
+ params->ext_lfb_base = (grub_size_t) (((grub_uint64_t)(grub_size_t) framebuffer) >> 32);
+ params->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
+#endif
+
+ 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_SIS315PRO:
+ case GRUB_VIDEO_DRIVER_VGA:
+ case GRUB_VIDEO_DRIVER_CIRRUS:
+ case GRUB_VIDEO_DRIVER_BOCHS:
+ case GRUB_VIDEO_DRIVER_RADEON_FULOONG2E:
+ case GRUB_VIDEO_DRIVER_RADEON_YEELOONG3A:
+ case GRUB_VIDEO_DRIVER_IEEE1275:
+ case GRUB_VIDEO_DRIVER_COREBOOT:
+ /* Make gcc happy. */
+ case GRUB_VIDEO_DRIVER_XEN:
+ case GRUB_VIDEO_DRIVER_SDL:
+ case GRUB_VIDEO_DRIVER_NONE:
+ case GRUB_VIDEO_ADAPTER_CAPTURE:
+ 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;
+}
+
+/* Context for grub_linux_boot. */
+struct grub_linux_boot_ctx
+{
+ grub_addr_t real_mode_target;
+ grub_size_t real_size;
+ struct linux_kernel_params *params;
+ int e820_num;
+};
+
+/* Helper for grub_linux_boot. */
+static int
+grub_linux_boot_mmap_find (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type, void *data)
+{
+ struct grub_linux_boot_ctx *ctx = data;
+
+ /* We must put real mode code in the traditional space. */
+ if (type != GRUB_MEMORY_AVAILABLE || addr > 0x90000)
+ return 0;
+
+ if (addr + size < 0x10000)
+ return 0;
+
+ if (addr < 0x10000)
+ {
+ size += addr - 0x10000;
+ addr = 0x10000;
+ }
+
+ if (addr + size > 0x90000)
+ size = 0x90000 - addr;
+
+ if (ctx->real_size + efi_mmap_size > size)
+ return 0;
+
+ grub_dprintf ("linux", "addr = %lx, size = %x, need_size = %x\n",
+ (unsigned long) addr,
+ (unsigned) size,
+ (unsigned) (ctx->real_size + efi_mmap_size));
+ ctx->real_mode_target = ((addr + size) - (ctx->real_size + efi_mmap_size));
+ return 1;
+}
+
+/* GRUB types conveniently match E820 types. */
+static int
+grub_linux_boot_mmap_fill (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type, void *data)
+{
+ struct grub_linux_boot_ctx *ctx = data;
+
+ if (grub_e820_add_region (ctx->params->e820_map, &ctx->e820_num,
+ addr, size, type))
+ return 1;
+
+ return 0;
+}
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ grub_err_t err = 0;
+ const char *modevar;
+ char *tmp;
+ struct grub_relocator32_state state;
+ void *real_mode_mem;
+ struct grub_linux_boot_ctx ctx = {
+ .real_mode_target = 0
+ };
+ grub_size_t mmap_size;
+ grub_size_t cl_offset;
+
+#ifdef GRUB_MACHINE_IEEE1275
+ {
+ const 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);
+ linux_params.ofw_signature = GRUB_LINUX_OFW_SIGNATURE;
+ linux_params.ofw_num_items = 1;
+ linux_params.ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn;
+ linux_params.ofw_idt = 0;
+ }
+#endif
+
+ 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;
+#if ACCEPTS_PURE_TEXT
+ err = grub_video_set_mode (tmp, 0, 0);
+#else
+ err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
+#endif
+ grub_free (tmp);
+ }
+ else /* We can't go back to text mode from coreboot fb. */
+#ifdef GRUB_MACHINE_COREBOOT
+ if (grub_video_get_driver_id () == GRUB_VIDEO_DRIVER_COREBOOT)
+ err = GRUB_ERR_NONE;
+ else
+#endif
+ {
+#if ACCEPTS_PURE_TEXT
+ err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
+#else
+ err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
+ GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
+#endif
+ }
+
+ if (err)
+ {
+ grub_print_error ();
+ grub_puts_ (N_("Booting in blind mode"));
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ if (grub_linux_setup_video (&linux_params))
+ {
+#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
+ linux_params.have_vga = GRUB_VIDEO_LINUX_TYPE_TEXT;
+ linux_params.video_mode = 0x3;
+#else
+ linux_params.have_vga = 0;
+ linux_params.video_mode = 0;
+ linux_params.video_width = 0;
+ linux_params.video_height = 0;
+#endif
+ }
+
+
+#ifndef GRUB_MACHINE_IEEE1275
+ if (linux_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)
+ {
+ struct grub_term_coordinate pos = grub_term_getxy (term);
+ linux_params.video_cursor_x = pos.x;
+ linux_params.video_cursor_y = pos.y;
+ linux_params.video_width = grub_term_width (term);
+ linux_params.video_height = grub_term_height (term);
+ found = 1;
+ break;
+ }
+ if (!found)
+ {
+ linux_params.video_cursor_x = 0;
+ linux_params.video_cursor_y = 0;
+ linux_params.video_width = 80;
+ linux_params.video_height = 25;
+ }
+ }
+
+#ifdef GRUB_KERNEL_USE_RSDP_ADDR
+ linux_params.acpi_rsdp_addr = grub_le_to_cpu64 (grub_rsdp_addr);
+#endif
+
+ mmap_size = find_mmap_size ();
+ /* Make sure that each size is aligned to a page boundary. */
+ cl_offset = ALIGN_UP (mmap_size + sizeof (linux_params), 4096);
+ if (cl_offset < ((grub_size_t) linux_params.setup_sects << GRUB_DISK_SECTOR_BITS))
+ cl_offset = ALIGN_UP ((grub_size_t) (linux_params.setup_sects
+ << GRUB_DISK_SECTOR_BITS), 4096);
+ ctx.real_size = ALIGN_UP (cl_offset + maximal_cmdline_size, 4096);
+
+#ifdef GRUB_MACHINE_EFI
+ efi_mmap_size = grub_efi_find_mmap_size ();
+ if (efi_mmap_size == 0)
+ return grub_errno;
+#endif
+
+ grub_dprintf ("linux", "real_size = %x, mmap_size = %x\n",
+ (unsigned) ctx.real_size, (unsigned) mmap_size);
+
+#ifdef GRUB_MACHINE_EFI
+ grub_efi_mmap_iterate (grub_linux_boot_mmap_find, &ctx, 1);
+ if (! ctx.real_mode_target)
+ grub_efi_mmap_iterate (grub_linux_boot_mmap_find, &ctx, 0);
+#else
+ grub_mmap_iterate (grub_linux_boot_mmap_find, &ctx);
+#endif
+ grub_dprintf ("linux", "real_mode_target = %lx, real_size = %x, efi_mmap_size = %x\n",
+ (unsigned long) ctx.real_mode_target,
+ (unsigned) ctx.real_size,
+ (unsigned) efi_mmap_size);
+
+ if (! ctx.real_mode_target)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
+
+ {
+ grub_relocator_chunk_t ch;
+ grub_size_t sz;
+
+ if (grub_add (ctx.real_size, efi_mmap_size, &sz))
+ return GRUB_ERR_OUT_OF_RANGE;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ ctx.real_mode_target, sz);
+ if (err)
+ return err;
+ real_mode_mem = get_virtual_current_address (ch);
+ }
+ efi_mmap_buf = (grub_uint8_t *) real_mode_mem + ctx.real_size;
+
+ grub_dprintf ("linux", "real_mode_mem = %p\n",
+ real_mode_mem);
+
+ ctx.params = real_mode_mem;
+
+ *ctx.params = linux_params;
+ ctx.params->cmd_line_ptr = ctx.real_mode_target + cl_offset;
+ grub_memcpy ((char *) ctx.params + cl_offset, linux_cmdline,
+ maximal_cmdline_size);
+
+ grub_dprintf ("linux", "code32_start = %x\n",
+ (unsigned) ctx.params->code32_start);
+
+ ctx.e820_num = 0;
+ if (grub_mmap_iterate (grub_linux_boot_mmap_fill, &ctx))
+ return grub_errno;
+ ctx.params->mmap_size = ctx.e820_num;
+
+#ifdef GRUB_MACHINE_EFI
+ {
+ grub_efi_uintn_t efi_desc_size;
+ grub_size_t efi_mmap_target;
+ grub_efi_uint32_t efi_desc_version;
+
+ ctx.params->secure_boot = grub_efi_get_secureboot ();
+
+ 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 = ctx.real_mode_target
+ + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem);
+ /* Pass EFI parameters. */
+ if (grub_le_to_cpu16 (ctx.params->version) >= 0x0208)
+ {
+ ctx.params->v0208.efi_mem_desc_size = efi_desc_size;
+ ctx.params->v0208.efi_mem_desc_version = efi_desc_version;
+ ctx.params->v0208.efi_mmap = efi_mmap_target;
+ ctx.params->v0208.efi_mmap_size = efi_mmap_size;
+
+#ifdef __x86_64__
+ ctx.params->v0208.efi_mmap_hi = (efi_mmap_target >> 32);
+#endif
+ }
+ else if (grub_le_to_cpu16 (ctx.params->version) >= 0x0206)
+ {
+ ctx.params->v0206.efi_mem_desc_size = efi_desc_size;
+ ctx.params->v0206.efi_mem_desc_version = efi_desc_version;
+ ctx.params->v0206.efi_mmap = efi_mmap_target;
+ ctx.params->v0206.efi_mmap_size = efi_mmap_size;
+ }
+ else if (grub_le_to_cpu16 (ctx.params->version) >= 0x0204)
+ {
+ ctx.params->v0204.efi_mem_desc_size = efi_desc_size;
+ ctx.params->v0204.efi_mem_desc_version = efi_desc_version;
+ ctx.params->v0204.efi_mmap = efi_mmap_target;
+ ctx.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 = ctx.real_mode_target;
+ state.esp = ctx.real_mode_target;
+ state.eip = ctx.params->code32_start;
+ return grub_relocator32_boot (relocator, state, 0);
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ grub_free (linux_cmdline);
+ linux_cmdline = 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_i386_kernel_header lh;
+ grub_uint8_t setup_sects;
+ grub_size_t real_size, prot_size, prot_file_size;
+ grub_ssize_t len;
+ int i;
+ grub_size_t align, min_align;
+ int relocatable;
+ grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR;
+
+ grub_dl_ref (my_mod);
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
+ if (! file)
+ goto fail;
+
+ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+ goto fail;
+ }
+
+ if (lh.boot_flag != grub_cpu_to_le16_compile_time (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;
+ }
+
+ /* 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_compile_time (GRUB_LINUX_I386_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;
+ }
+
+ 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;
+ }
+
+ if (grub_le_to_cpu16 (lh.version) >= 0x0206)
+ maximal_cmdline_size = grub_le_to_cpu32 (lh.cmdline_size) + 1;
+ else
+ maximal_cmdline_size = 256;
+
+ if (maximal_cmdline_size < 128)
+ maximal_cmdline_size = 128;
+
+ 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_file_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
+
+ if (grub_le_to_cpu16 (lh.version) >= 0x205
+ && lh.kernel_alignment != 0
+ && ((lh.kernel_alignment - 1) & lh.kernel_alignment) == 0)
+ {
+ for (align = 0; align < 32; align++)
+ if (grub_le_to_cpu32 (lh.kernel_alignment) & (1 << align))
+ break;
+ relocatable = lh.relocatable;
+ }
+ else
+ {
+ align = 0;
+ relocatable = 0;
+ }
+
+ if (grub_le_to_cpu16 (lh.version) >= 0x020a)
+ {
+ min_align = lh.min_alignment;
+ prot_size = grub_le_to_cpu32 (lh.init_size);
+ prot_init_space = page_align (prot_size);
+ if (relocatable)
+ preferred_address = grub_le_to_cpu64 (lh.pref_address);
+ }
+ else
+ {
+ min_align = align;
+ prot_size = prot_file_size;
+ /* Usually, the compression ratio is about 50%. */
+ prot_init_space = page_align (prot_size) * 3;
+ }
+
+ if (allocate_pages (prot_size, &align,
+ min_align, relocatable,
+ preferred_address))
+ goto fail;
+
+ grub_memset (&linux_params, 0, sizeof (linux_params));
+
+ /*
+ * The Linux 32-bit boot protocol defines the setup header end
+ * to be at 0x202 + the byte value at 0x201.
+ */
+ len = 0x202 + *((char *) &lh.jump + 1);
+
+ /* Verify the struct is big enough so we do not write past the end. */
+ if (len > (char *) &linux_params.edd_mbr_sig_buffer - (char *) &linux_params) {
+ grub_error (GRUB_ERR_BAD_OS, "Linux setup header too big");
+ goto fail;
+ }
+
+ grub_memcpy (&linux_params.setup_sects, &lh.setup_sects, len - 0x1F1);
+
+ /* We've already read lh so there is no need to read it second time. */
+ len -= sizeof(lh);
+
+ if ((len > 0) &&
+ (grub_file_read (file, (char *) &linux_params + sizeof (lh), len) != len))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+ goto fail;
+ }
+
+ linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR;
+ linux_params.kernel_alignment = (1 << align);
+ linux_params.ps_mouse = linux_params.padding11 = 0;
+ linux_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. */
+ linux_params.cl_magic = GRUB_LINUX_CL_MAGIC;
+ linux_params.cl_offset = 0x1000;
+
+ linux_params.ramdisk_image = 0;
+ linux_params.ramdisk_size = 0;
+
+ linux_params.heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET;
+ linux_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. */
+ linux_params.ext_mem = ((32 * 0x100000) >> 10);
+ linux_params.alt_mem = ((32 * 0x100000) >> 10);
+
+ /* Ignored by Linux. */
+ linux_params.video_page = 0;
+
+ /* Only used when `video_mode == 0x7', otherwise ignored. */
+ linux_params.video_ega_bx = 0;
+
+ linux_params.font_size = 16; /* XXX */
+
+#ifdef GRUB_MACHINE_EFI
+#ifdef __x86_64__
+ if (grub_le_to_cpu16 (linux_params.version) < 0x0208 &&
+ ((grub_addr_t) grub_efi_system_table >> 32) != 0)
+ return grub_error(GRUB_ERR_BAD_OS,
+ "kernel does not support 64-bit addressing");
+#endif
+
+ if (grub_le_to_cpu16 (linux_params.version) >= 0x0208)
+ {
+ linux_params.v0208.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
+ linux_params.v0208.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table;
+#ifdef __x86_64__
+ linux_params.v0208.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32);
+#endif
+ }
+ else if (grub_le_to_cpu16 (linux_params.version) >= 0x0206)
+ {
+ linux_params.v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
+ linux_params.v0206.efi_system_table = (grub_uint32_t) (grub_addr_t) grub_efi_system_table;
+ }
+ else if (grub_le_to_cpu16 (linux_params.version) >= 0x0204)
+ {
+ linux_params.v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204;
+ linux_params.v0204.efi_system_table = (grub_uint32_t) (grub_addr_t) 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_puts_ (N_("Legacy `ask' parameter no longer supported."));
+
+ /* 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_ (N_("%s is deprecated. "
+ "Use set gfxpayload=%s before "
+ "linux command instead.\n"),
+ argv[i], "text");
+ break;
+
+ case 1:
+ case GRUB_LINUX_VID_MODE_EXTENDED:
+ /* FIXME: support 80x50 text. */
+ grub_env_set ("gfxpayload", "text");
+ grub_printf_ (N_("%s is deprecated. "
+ "Use set gfxpayload=%s before "
+ "linux command instead.\n"),
+ argv[i], "text");
+ 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");
+ /* TRANSLATORS: "x" has to be entered in, like an identifier,
+ so please don't use better Unicode codepoints. */
+ grub_printf_ (N_("%s is deprecated. VGA 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_ (N_("%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)
+ {
+ const 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;
+ /* FALLTHROUGH */
+ case 'm':
+ shift += 10;
+ /* FALLTHROUGH */
+ case 'k':
+ shift += 10;
+ /* FALLTHROUGH */
+ 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)
+ {
+ linux_params.loadflags |= GRUB_LINUX_FLAG_QUIET;
+ }
+
+ /* Create kernel command line. */
+ linux_cmdline = grub_zalloc (maximal_cmdline_size + 1);
+ if (!linux_cmdline)
+ goto fail;
+ grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+ {
+ grub_err_t err;
+ err = grub_create_loader_cmdline (argc, argv,
+ linux_cmdline
+ + sizeof (LINUX_IMAGE) - 1,
+ maximal_cmdline_size
+ - (sizeof (LINUX_IMAGE) - 1),
+ GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ goto fail;
+ }
+
+ len = prot_file_size;
+ if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+
+ 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_size_t size = 0, aligned_size = 0;
+ grub_addr_t addr_min, addr_max;
+ grub_addr_t addr;
+ grub_err_t err;
+ struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto fail;
+ }
+
+ if (! loaded)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
+ goto fail;
+ }
+
+ if (grub_initrd_init (argc, argv, &initrd_ctx))
+ goto fail;
+
+ size = grub_get_initrd_size (&initrd_ctx);
+ aligned_size = ALIGN_UP (size, 4096);
+
+ /* Get the highest address available for the initrd. */
+ if (grub_le_to_cpu16 (linux_params.version) >= 0x0203)
+ {
+ addr_max = grub_cpu_to_le32 (linux_params.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_addr_t) prot_mode_target + prot_init_space;
+
+ /* Put the initrd as high as possible, 4KiB aligned. */
+ addr = (addr_max - aligned_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, aligned_size,
+ 0x1000,
+ GRUB_RELOCATOR_PREFERENCE_HIGH,
+ 1);
+ if (err)
+ return err;
+ initrd_mem = get_virtual_current_address (ch);
+ initrd_mem_target = get_physical_target_address (ch);
+ }
+
+ if (grub_initrd_load (&initrd_ctx, argv, initrd_mem))
+ goto fail;
+
+ grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
+ (unsigned) addr, (unsigned) size);
+
+ linux_params.ramdisk_image = initrd_mem_target;
+ linux_params.ramdisk_size = size;
+ linux_params.root_dev = 0x0100; /* XXX */
+
+ fail:
+ grub_initrd_close (&initrd_ctx);
+
+ 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..a67d9d0
--- /dev/null
+++ b/grub-core/loader/i386/multiboot_mbi.c
@@ -0,0 +1,756 @@
+/*
+ * 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>
+#include <grub/net.h>
+#include <grub/i18n.h>
+#include <grub/lib/cmdline.h>
+
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#endif
+
+/* 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_multiboot_quirks_t grub_multiboot_quirks;
+
+static grub_err_t
+load_kernel (grub_file_t file, const char *filename,
+ char *buffer, struct multiboot_header *header)
+{
+ grub_err_t err;
+ mbi_load_data_t mld;
+
+ mld.file = file;
+ mld.filename = filename;
+ mld.buffer = buffer;
+ mld.mbi_ver = 1;
+ mld.relocatable = 0;
+ mld.avoid_efi_boot_services = 0;
+
+ if (grub_multiboot_quirks & GRUB_MULTIBOOT_QUIRK_BAD_KLUDGE)
+ {
+ err = grub_multiboot_load_elf (&mld);
+ if (err == GRUB_ERR_NONE) {
+ return GRUB_ERR_NONE;
+ }
+ if (err == GRUB_ERR_UNKNOWN_OS && (header->flags & MULTIBOOT_AOUT_KLUDGE))
+ grub_errno = err = GRUB_ERR_NONE;
+ }
+ 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");
+ return err;
+ }
+ source = get_virtual_current_address (ch);
+
+ if ((grub_file_seek (file, offset)) == (grub_off_t) -1)
+ {
+ return grub_errno;
+ }
+
+ grub_file_read (file, source, load_size);
+ if (grub_errno)
+ 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;
+ return GRUB_ERR_NONE;
+ }
+
+ return grub_multiboot_load_elf (&mld);
+}
+
+static struct multiboot_header *
+find_header (char *buffer, grub_ssize_t len)
+{
+ struct multiboot_header *header;
+
+ /* 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 = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN))
+ {
+ if (header->magic == MULTIBOOT_HEADER_MAGIC
+ && !(header->magic + header->flags + header->checksum))
+ return header;
+ }
+ return NULL;
+}
+
+grub_err_t
+grub_multiboot_load (grub_file_t file, const char *filename)
+{
+ 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);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ return grub_errno;
+ }
+
+ header = find_header (buffer, len);
+
+ 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);
+ }
+
+ err = load_kernel (file, filename, buffer, header);
+ 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)
+{
+ grub_size_t ret;
+ struct grub_net_network_level_interface *net;
+
+ ret = sizeof (struct multiboot_info) + ALIGN_UP (cmdline_size, 4)
+ + modcnt * sizeof (struct multiboot_mod_list) + total_modcmd
+ + ALIGN_UP (sizeof(PACKAGE_STRING), 4)
+ + grub_multiboot_get_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);
+
+ FOR_NET_NETWORK_LEVEL_INTERFACES(net)
+ if (net->dhcp_ack)
+ {
+ ret += net->dhcp_acklen;
+ break;
+ }
+
+ return ret;
+}
+
+/* Helper for grub_fill_multiboot_mmap. */
+static int
+grub_fill_multiboot_mmap_iter (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type, void *data)
+{
+ struct multiboot_mmap_entry **mmap_entry = data;
+
+ (*mmap_entry)->addr = addr;
+ (*mmap_entry)->len = size;
+ (*mmap_entry)->type = type;
+ (*mmap_entry)->size = sizeof (struct multiboot_mmap_entry) - sizeof ((*mmap_entry)->size);
+ (*mmap_entry)++;
+
+ return 0;
+}
+
+/* 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;
+
+ grub_mmap_iterate (grub_fill_multiboot_mmap_iter, &mmap_entry);
+}
+
+#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_safe (grub_multiboot_relocator, &ch,
+ 0x10000, 0xa0000, bufsize, 4,
+ GRUB_RELOCATOR_PREFERENCE_NONE, 0);
+ 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_multiboot_get_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;
+ }
+
+ {
+ struct grub_net_network_level_interface *net;
+ FOR_NET_NETWORK_LEVEL_INTERFACES(net)
+ if (net->dhcp_ack)
+ {
+ grub_memcpy (ptrorig, net->dhcp_ack, net->dhcp_acklen);
+ mbi->drives_addr = ptrdest;
+ mbi->drives_length = net->dhcp_acklen;
+ ptrorig += net->dhcp_acklen;
+ ptrdest += net->dhcp_acklen;
+ break;
+ }
+ }
+
+ 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
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#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;
+
+ grub_multiboot_free_mbi ();
+
+ len = grub_loader_cmdline_size (argc, argv);
+
+ cmdline = grub_malloc (len);
+ if (! cmdline)
+ return grub_errno;
+ cmdline_size = len;
+
+ return grub_create_loader_cmdline (argc, argv, cmdline,
+ cmdline_size, GRUB_VERIFY_KERNEL_CMDLINE);
+}
+
+grub_err_t
+grub_multiboot_add_module (grub_addr_t start, grub_size_t size,
+ int argc, char *argv[])
+{
+ struct module *newmod;
+ grub_size_t len = 0;
+ grub_err_t err;
+
+ newmod = grub_malloc (sizeof (*newmod));
+ if (!newmod)
+ return grub_errno;
+ newmod->start = start;
+ newmod->size = size;
+ newmod->next = 0;
+
+ len = grub_loader_cmdline_size (argc, argv);
+
+ newmod->cmdline = grub_malloc (len);
+ if (! newmod->cmdline)
+ {
+ grub_free (newmod);
+ return grub_errno;
+ }
+ newmod->cmdline_size = len;
+ total_modcmd += ALIGN_UP (len, 4);
+
+ err = grub_create_loader_cmdline (argc, argv, newmod->cmdline,
+ newmod->cmdline_size, GRUB_VERIFY_MODULE_CMDLINE);
+ if (err)
+ {
+ grub_free (newmod);
+ return grub_errno;
+ }
+
+ if (modules_last)
+ modules_last->next = newmod;
+ else
+ modules = newmod;
+ 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..976fea7
--- /dev/null
+++ b/grub-core/loader/i386/pc/chainloader.c
@@ -0,0 +1,310 @@
+/* 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/chainloader.h>
+#include <grub/machine/biosdisk.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>
+#include <grub/fat.h>
+#include <grub/ntfs.h>
+#include <grub/i386/relocator.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+static int boot_drive;
+static grub_addr_t boot_part_addr;
+static struct grub_relocator *rel;
+
+typedef enum
+ {
+ GRUB_CHAINLOADER_FORCE = 0x1,
+ GRUB_CHAINLOADER_BPB = 0x2,
+ } grub_chainloader_flags_t;
+
+static grub_err_t
+grub_chainloader_boot (void)
+{
+ struct grub_relocator16_state state = {
+ .edx = boot_drive,
+ .esi = boot_part_addr,
+ .ds = 0,
+ .es = 0,
+ .fs = 0,
+ .gs = 0,
+ .ss = 0,
+ .cs = 0,
+ .sp = GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR,
+ .ip = GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR,
+ .a20 = 0
+ };
+ grub_video_set_mode ("text", 0, 0);
+
+ return grub_relocator16_boot (rel, state);
+}
+
+static grub_err_t
+grub_chainloader_unload (void)
+{
+ grub_relocator_unload (rel);
+ rel = NULL;
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_chainloader_patch_bpb (void *bs, grub_device_t dev, grub_uint8_t dl)
+{
+ grub_uint32_t part_start = 0, heads = 0, sectors = 0;
+ if (dev && dev->disk)
+ {
+ part_start = grub_partition_get_start (dev->disk->partition);
+ if (dev->disk->data)
+ {
+ heads = ((struct grub_biosdisk_data *)(dev->disk->data))->heads;
+ sectors = ((struct grub_biosdisk_data *)(dev->disk->data))->sectors;
+ }
+ }
+ if (grub_memcmp ((char *) &((struct grub_ntfs_bpb *) bs)->oem_name,
+ "NTFS", 4) == 0)
+ {
+ struct grub_ntfs_bpb *bpb = (struct grub_ntfs_bpb *) bs;
+ bpb->num_hidden_sectors = grub_cpu_to_le32 (part_start);
+ bpb->bios_drive = dl;
+ return;
+ }
+
+ do
+ {
+ struct grub_fat_bpb *bpb = (struct grub_fat_bpb *) bs;
+ if (grub_strncmp((const char *) bpb->version_specific.fat12_or_fat16.fstype, "FAT12", 5)
+ && grub_strncmp((const char *) bpb->version_specific.fat12_or_fat16.fstype, "FAT16", 5)
+ && grub_strncmp((const char *) bpb->version_specific.fat32.fstype, "FAT32", 5))
+ break;
+
+ if (grub_le_to_cpu16 (bpb->bytes_per_sector) < 512
+ || (grub_le_to_cpu16 (bpb->bytes_per_sector)
+ & (grub_le_to_cpu16 (bpb->bytes_per_sector) - 1)))
+ break;
+
+ if (bpb->sectors_per_cluster == 0
+ || (bpb->sectors_per_cluster & (bpb->sectors_per_cluster - 1)))
+ break;
+
+ if (bpb->num_reserved_sectors == 0)
+ break;
+ if (bpb->num_total_sectors_16 == 0 && bpb->num_total_sectors_32 == 0)
+ break;
+
+ if (bpb->num_fats == 0)
+ break;
+
+ if (bpb->sectors_per_fat_16)
+ {
+ bpb->num_hidden_sectors = grub_cpu_to_le32 (part_start);
+ bpb->version_specific.fat12_or_fat16.num_ph_drive = dl;
+ if (sectors)
+ bpb->sectors_per_track = grub_cpu_to_le16 (sectors);
+ if (heads)
+ bpb->num_heads = grub_cpu_to_le16 (heads);
+ return;
+ }
+ if (bpb->version_specific.fat32.sectors_per_fat_32)
+ {
+ bpb->num_hidden_sectors = grub_cpu_to_le32 (part_start);
+ bpb->version_specific.fat32.num_ph_drive = dl;
+ if (sectors)
+ bpb->sectors_per_track = grub_cpu_to_le16 (sectors);
+ if (heads)
+ bpb->num_heads = grub_cpu_to_le16 (heads);
+ return;
+ }
+ break;
+ }
+ while (0);
+}
+
+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;
+ grub_addr_t part_addr = 0;
+ grub_uint8_t *bs, *ptable;
+
+ rel = grub_relocator_new ();
+ if (!rel)
+ goto fail;
+
+ grub_dl_ref (my_mod);
+
+ file = grub_file_open (filename, GRUB_FILE_TYPE_PCCHAINLOADER
+ | GRUB_FILE_TYPE_NO_DECOMPRESS);
+ if (! file)
+ goto fail;
+
+ {
+ grub_relocator_chunk_t ch;
+ grub_err_t err;
+
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, 0x7C00,
+ GRUB_DISK_SECTOR_SIZE);
+ if (err)
+ goto fail;
+ bs = get_virtual_current_address (ch);
+ err = grub_relocator_alloc_chunk_addr (rel, &ch,
+ GRUB_MEMORY_MACHINE_PART_TABLE_ADDR,
+ 64);
+ if (err)
+ goto fail;
+ ptable = get_virtual_current_address (ch);
+ }
+
+ /* Read the first block. */
+ if (grub_file_read (file, bs, GRUB_DISK_SECTOR_SIZE)
+ != GRUB_DISK_SECTOR_SIZE)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+
+ goto fail;
+ }
+
+ /* Check the signature. */
+ signature = *((grub_uint16_t *) (bs + 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, ptable);
+ part_addr = (GRUB_MEMORY_MACHINE_PART_TABLE_ADDR
+ + (p->index << 4));
+ disk->partition = p;
+ }
+ }
+ }
+
+ if (flags & GRUB_CHAINLOADER_BPB)
+ grub_chainloader_patch_bpb ((void *) 0x7C00, dev, drive);
+
+ 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;
+
+ while (argc > 0)
+ {
+ if (grub_strcmp (argv[0], "--force") == 0)
+ {
+ flags |= GRUB_CHAINLOADER_FORCE;
+ argc--;
+ argv++;
+ continue;
+ }
+ if (grub_strcmp (argv[0], "--bpb") == 0)
+ {
+ flags |= GRUB_CHAINLOADER_BPB;
+ argc--;
+ argv++;
+ continue;
+ }
+ break;
+ }
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ 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,
+ N_("[--force|--bpb] FILE"),
+ N_("Load another boot loader."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(chainloader)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/loader/i386/pc/freedos.c b/grub-core/loader/i386/pc/freedos.c
new file mode 100644
index 0000000..aac6c97
--- /dev/null
+++ b/grub-core/loader/i386/pc/freedos.c
@@ -0,0 +1,190 @@
+/* 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>
+#include <grub/machine/chainloader.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+static struct grub_relocator *rel;
+static grub_uint32_t ebx = 0xffffffff;
+
+#define GRUB_FREEDOS_SEGMENT 0x60
+#define GRUB_FREEDOS_ADDR (GRUB_FREEDOS_SEGMENT << 4)
+#define GRUB_FREEDOS_STACK_SEGMENT 0x1fe0
+#define GRUB_FREEDOS_STACK_BPB_POINTER 0x7c00
+#define GRUB_FREEDOS_BPB_ADDR ((GRUB_FREEDOS_STACK_SEGMENT << 4) \
+ + GRUB_FREEDOS_STACK_BPB_POINTER)
+
+/* FreeDOS boot.asm passes register sp as exactly this. Importantly,
+ it must point below the BPB (to avoid overwriting any of it). */
+#define GRUB_FREEDOS_STACK_POINTER (GRUB_FREEDOS_STACK_BPB_POINTER \
+ - 0x60)
+
+/* In this, the additional 8192 bytes are the stack reservation; the
+ remaining parts trivially give the maximum allowed size. */
+#define GRUB_FREEDOS_MAX_SIZE ((GRUB_FREEDOS_STACK_SEGMENT << 4) \
+ + GRUB_FREEDOS_STACK_POINTER \
+ - GRUB_FREEDOS_ADDR \
+ - 8192)
+
+static grub_err_t
+grub_freedos_boot (void)
+{
+ struct grub_relocator16_state state = {
+ .cs = GRUB_FREEDOS_SEGMENT,
+ .ip = 0,
+
+ .ds = GRUB_FREEDOS_STACK_SEGMENT,
+ .es = 0,
+ .fs = 0,
+ .gs = 0,
+ .ss = GRUB_FREEDOS_STACK_SEGMENT,
+ .sp = GRUB_FREEDOS_STACK_POINTER,
+ .ebp = GRUB_FREEDOS_STACK_BPB_POINTER,
+ .ebx = ebx,
+ .edx = ebx,
+ .a20 = 1
+ };
+ grub_video_set_mode ("text", 0, 0);
+
+ return grub_relocator16_boot (rel, state);
+}
+
+static grub_err_t
+grub_freedos_unload (void)
+{
+ grub_relocator_unload (rel);
+ rel = NULL;
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_freedos (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_err_t err;
+ void *bs, *kernelsys;
+ grub_size_t kernelsyssize;
+ grub_device_t dev;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ grub_dl_ref (my_mod);
+
+ rel = grub_relocator_new ();
+ if (!rel)
+ goto fail;
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_FREEDOS);
+ if (! file)
+ goto fail;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_FREEDOS_BPB_ADDR,
+ GRUB_DISK_SECTOR_SIZE);
+ if (err)
+ goto fail;
+ bs = get_virtual_current_address (ch);
+ }
+
+ ebx = 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;
+ }
+ grub_chainloader_patch_bpb (bs, dev, ebx);
+ }
+
+ if (dev)
+ grub_device_close (dev);
+
+ kernelsyssize = grub_file_size (file);
+
+ if (kernelsyssize > GRUB_FREEDOS_MAX_SIZE)
+ {
+ grub_error (GRUB_ERR_BAD_OS,
+ N_("the size of `%s' is too large"), argv[0]);
+ goto fail;
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_FREEDOS_ADDR,
+ kernelsyssize);
+ if (err)
+ goto fail;
+ kernelsys = get_virtual_current_address (ch);
+ }
+
+ if (grub_file_read (file, kernelsys, kernelsyssize)
+ != (grub_ssize_t) kernelsyssize)
+ goto fail;
+
+ grub_loader_set (grub_freedos_boot, grub_freedos_unload, 1);
+ return GRUB_ERR_NONE;
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ grub_freedos_unload ();
+
+ return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(freedos)
+{
+ cmd = grub_register_command ("freedos", grub_cmd_freedos,
+ 0, N_("Load FreeDOS kernel.sys."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(freedos)
+{
+ 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..2a29952
--- /dev/null
+++ b/grub-core/loader/i386/pc/linux.c
@@ -0,0 +1,494 @@
+/* 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/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>
+#include <grub/linux.h>
+#include <grub/safemath.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define GRUB_LINUX_CL_OFFSET 0x9000
+
+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_size_t maximal_cmdline_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;
+ state.a20 = 1;
+
+ 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 int
+target_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type,
+ void *data)
+{
+ grub_uint64_t *result = data;
+ grub_uint64_t candidate;
+
+ if (type != GRUB_MEMORY_AVAILABLE)
+ return 0;
+ if (addr >= 0xa0000)
+ return 0;
+ if (addr + size >= 0xa0000)
+ size = 0xa0000 - addr;
+
+ /* Put the real mode part at as a high location as possible. */
+ candidate = addr + size - (GRUB_LINUX_CL_OFFSET + maximal_cmdline_size);
+ /* But it must not exceed the traditional area. */
+ if (candidate > GRUB_LINUX_OLD_REAL_MODE_ADDR)
+ candidate = GRUB_LINUX_OLD_REAL_MODE_ADDR;
+ if (candidate < addr)
+ return 0;
+
+ if (candidate > *result || *result == (grub_uint64_t) -1)
+ *result = candidate;
+ return 0;
+}
+
+static grub_addr_t
+grub_find_real_target (void)
+{
+ grub_uint64_t result = (grub_uint64_t) -1;
+
+ grub_mmap_iterate (target_hook, &result);
+ return result;
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ struct linux_i386_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, N_("filename expected"));
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
+ if (! file)
+ goto fail;
+
+ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+ goto fail;
+ }
+
+ if (lh.boot_flag != grub_cpu_to_le16_compile_time (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;
+
+ maximal_cmdline_size = 256;
+
+ if (lh.header == grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_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;
+
+ if (grub_le_to_cpu16 (lh.version) >= 0x0206)
+ maximal_cmdline_size = grub_le_to_cpu32 (lh.cmdline_size) + 1;
+
+ grub_linux_real_target = grub_find_real_target ();
+ if (grub_linux_real_target == (grub_addr_t)-1)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "no appropriate low memory found");
+ goto fail;
+ }
+
+ if (grub_le_to_cpu16 (lh.version) >= 0x0201)
+ {
+ lh.heap_end_ptr = grub_cpu_to_le16_compile_time (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_compile_time (GRUB_LINUX_CL_MAGIC);
+ lh.cl_offset = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_OFFSET);
+ lh.setup_move_size = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_OFFSET
+ + maximal_cmdline_size);
+ }
+ }
+ else
+ {
+ /* Your kernel is quite old... */
+ lh.cl_magic = grub_cpu_to_le16_compile_time (GRUB_LINUX_CL_MAGIC);
+ lh.cl_offset = grub_cpu_to_le16_compile_time (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;
+ if (grub_sub (grub_file_size (file), real_size, &grub_linux16_prot_size) ||
+ grub_sub (grub_linux16_prot_size, GRUB_DISK_SECTOR_SIZE, &grub_linux16_prot_size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+ goto fail;
+ }
+
+ 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%" PRIxGRUB_SIZE
+ " > 0x%" PRIxGRUB_ADDR "), use bzImage instead",
+ GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size,
+ grub_linux_real_target);
+ goto fail;
+ }
+
+ grub_dprintf ("linux", "[Linux-%s, setup=0x%x, size=0x%x]\n",
+ grub_linux_is_bzimage ? "bzImage" : "zImage",
+ (unsigned) real_size,
+ (unsigned) grub_linux16_prot_size);
+
+ 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)
+ {
+ const 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;
+ /* Fallthrough. */
+ case 'm':
+ shift += 10;
+ /* Fallthrough. */
+ case 'k':
+ shift += 10;
+ /* Fallthrough. */
+ default:
+ break;
+ }
+
+ /* Check an overflow. */
+ if (linux_mem_size > (~0UL >> shift))
+ linux_mem_size = 0;
+ else
+ linux_mem_size <<= shift;
+ }
+ }
+
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ goto fail;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ grub_linux_real_target,
+ GRUB_LINUX_CL_OFFSET
+ + maximal_cmdline_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)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+ goto fail;
+ }
+
+ if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_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));
+ err = grub_create_loader_cmdline (argc, argv,
+ (char *)grub_linux_real_chunk
+ + GRUB_LINUX_CL_OFFSET + sizeof (LINUX_IMAGE) - 1,
+ maximal_cmdline_size
+ - (sizeof (LINUX_IMAGE) - 1),
+ GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ goto fail;
+
+ 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, len) != len && !grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+
+ 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_size_t size = 0;
+ grub_addr_t addr_max, addr_min;
+ struct linux_i386_kernel_header *lh;
+ grub_uint8_t *initrd_chunk;
+ grub_addr_t initrd_addr;
+ grub_err_t err;
+ struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto fail;
+ }
+
+ if (!loaded)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
+ goto fail;
+ }
+
+ lh = (struct linux_i386_kernel_header *) grub_linux_real_chunk;
+
+ if (!(lh->header == grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_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;
+
+ if (grub_initrd_init (argc, argv, &initrd_ctx))
+ goto fail;
+
+ size = grub_get_initrd_size (&initrd_ctx);
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, addr_min, addr_max, size,
+ 0x1000, GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
+ if (err)
+ return err;
+ initrd_chunk = get_virtual_current_address (ch);
+ initrd_addr = get_physical_target_address (ch);
+ }
+
+ if (grub_initrd_load (&initrd_ctx, argv, initrd_chunk))
+ goto fail;
+
+ lh->ramdisk_image = initrd_addr;
+ lh->ramdisk_size = size;
+
+ fail:
+ grub_initrd_close (&initrd_ctx);
+
+ 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..f0d7414
--- /dev/null
+++ b/grub-core/loader/i386/pc/ntldr.c
@@ -0,0 +1,162 @@
+/* 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>
+#include <grub/machine/chainloader.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,
+ .a20 = 1
+ };
+ 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, N_("filename expected"));
+
+ grub_dl_ref (my_mod);
+
+ rel = grub_relocator_new ();
+ if (!rel)
+ goto fail;
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_NTLDR);
+ 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;
+ }
+ grub_chainloader_patch_bpb (bs, dev, edx);
+ }
+
+ 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/pc/plan9.c b/grub-core/loader/i386/pc/plan9.c
new file mode 100644
index 0000000..3755015
--- /dev/null
+++ b/grub-core/loader/i386/pc/plan9.c
@@ -0,0 +1,607 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/msdos_partition.h>
+#include <grub/scsi.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/video.h>
+#include <grub/mm.h>
+#include <grub/cpu/relocator.h>
+#include <grub/extcmd.h>
+#include <grub/verify.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+static struct grub_relocator *rel;
+static grub_uint32_t eip = 0xffffffff;
+
+#define GRUB_PLAN9_TARGET 0x100000
+#define GRUB_PLAN9_ALIGN 4096
+#define GRUB_PLAN9_CONFIG_ADDR 0x001200
+#define GRUB_PLAN9_CONFIG_PATH_SIZE 0x000040
+#define GRUB_PLAN9_CONFIG_MAGIC "ZORT 0\r\n"
+
+static const struct grub_arg_option options[] =
+ {
+ {"map", 'm', GRUB_ARG_OPTION_REPEATABLE,
+ /* TRANSLATORS: it's about guessing which GRUB disk
+ is which Plan9 disk. If your language has no
+ word "mapping" you can use another word which
+ means that the GRUBDEVICE and PLAN9DEVICE are
+ actually the same device, just named differently
+ in OS and GRUB. */
+ N_("Override guessed mapping of Plan9 devices."),
+ N_("GRUBDEVICE=PLAN9DEVICE"),
+ ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+struct grub_plan9_header
+{
+ grub_uint32_t magic;
+#define GRUB_PLAN9_MAGIC 0x1eb
+ grub_uint32_t text_size;
+ grub_uint32_t data_size;
+ grub_uint32_t bss_size;
+ grub_uint32_t sectiona;
+ grub_uint32_t entry_addr;
+ grub_uint32_t zero;
+ grub_uint32_t sectionb;
+};
+
+static grub_err_t
+grub_plan9_boot (void)
+{
+ struct grub_relocator32_state state = {
+ .eax = 0,
+ .eip = eip,
+ .ebx = 0,
+ .ecx = 0,
+ .edx = 0,
+ .edi = 0,
+ .esp = 0,
+ .ebp = 0,
+ .esi = 0
+ };
+ grub_video_set_mode ("text", 0, 0);
+
+ return grub_relocator32_boot (rel, state, 0);
+}
+
+static grub_err_t
+grub_plan9_unload (void)
+{
+ grub_relocator_unload (rel);
+ rel = NULL;
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+/* Context for grub_cmd_plan9. */
+struct grub_cmd_plan9_ctx
+{
+ grub_extcmd_context_t ctxt;
+ grub_file_t file;
+ char *pmap;
+ grub_size_t pmapalloc;
+ grub_size_t pmapptr;
+ int noslash;
+ int prefixescnt[5];
+ char *bootdisk, *bootpart;
+};
+
+static const char prefixes[5][10] = {
+ "dos", "plan9", "ntfs", "linux", "linuxswap"
+};
+
+#include <grub/err.h>
+
+static inline grub_err_t
+grub_extend_alloc (grub_size_t sz, grub_size_t *allocated, char **ptr)
+{
+ void *n;
+ if (sz < *allocated)
+ return GRUB_ERR_NONE;
+
+ *allocated = 2 * sz;
+ n = grub_realloc (*ptr, *allocated);
+ if (!n)
+ return grub_errno;
+ *ptr = n;
+ return GRUB_ERR_NONE;
+}
+
+/* Helper for grub_cmd_plan9. */
+static int
+fill_partition (grub_disk_t disk, const grub_partition_t partition, void *data)
+{
+ struct grub_cmd_plan9_ctx *fill_ctx = data;
+ int file_disk = 0;
+ int pstart, pend;
+
+ if (!fill_ctx->noslash)
+ {
+ if (grub_extend_alloc (fill_ctx->pmapptr + 1, &fill_ctx->pmapalloc,
+ &fill_ctx->pmap))
+ return 1;
+ fill_ctx->pmap[fill_ctx->pmapptr++] = '/';
+ }
+ fill_ctx->noslash = 0;
+
+ file_disk = fill_ctx->file->device->disk
+ && disk->id == fill_ctx->file->device->disk->id
+ && disk->dev->id == fill_ctx->file->device->disk->dev->id;
+
+ pstart = fill_ctx->pmapptr;
+ if (grub_strcmp (partition->partmap->name, "plan") == 0)
+ {
+ unsigned ptr = partition->index + sizeof ("part ") - 1;
+ grub_err_t err;
+ disk->partition = partition->parent;
+ do
+ {
+ if (grub_extend_alloc (fill_ctx->pmapptr + 1, &fill_ctx->pmapalloc,
+ &fill_ctx->pmap))
+ return 1;
+ err = grub_disk_read (disk, 1, ptr, 1,
+ fill_ctx->pmap + fill_ctx->pmapptr);
+ if (err)
+ {
+ disk->partition = 0;
+ return err;
+ }
+ ptr++;
+ fill_ctx->pmapptr++;
+ }
+ while (grub_isalpha (fill_ctx->pmap[fill_ctx->pmapptr - 1])
+ || grub_isdigit (fill_ctx->pmap[fill_ctx->pmapptr - 1]));
+ fill_ctx->pmapptr--;
+ }
+ else
+ {
+ char name[50];
+ int c = 0;
+ if (grub_strcmp (partition->partmap->name, "msdos") == 0)
+ {
+ switch (partition->msdostype)
+ {
+ case GRUB_PC_PARTITION_TYPE_PLAN9:
+ c = 1;
+ break;
+ case GRUB_PC_PARTITION_TYPE_NTFS:
+ c = 2;
+ break;
+ case GRUB_PC_PARTITION_TYPE_MINIX:
+ case GRUB_PC_PARTITION_TYPE_LINUX_MINIX:
+ case GRUB_PC_PARTITION_TYPE_EXT2FS:
+ c = 3;
+ break;
+ case GRUB_PC_PARTITION_TYPE_LINUX_SWAP:
+ c = 4;
+ break;
+ }
+ }
+
+ if (fill_ctx->prefixescnt[c] == 0)
+ grub_strcpy (name, prefixes[c]);
+ else
+ grub_snprintf (name, sizeof (name), "%s.%d", prefixes[c],
+ fill_ctx->prefixescnt[c]);
+ fill_ctx->prefixescnt[c]++;
+ if (grub_extend_alloc (fill_ctx->pmapptr + grub_strlen (name) + 1,
+ &fill_ctx->pmapalloc, &fill_ctx->pmap))
+ return 1;
+ grub_strcpy (fill_ctx->pmap + fill_ctx->pmapptr, name);
+ fill_ctx->pmapptr += grub_strlen (name);
+ }
+ pend = fill_ctx->pmapptr;
+ if (grub_extend_alloc (fill_ctx->pmapptr + 2 + 25 + 5 + 25,
+ &fill_ctx->pmapalloc, &fill_ctx->pmap))
+ return 1;
+ fill_ctx->pmap[fill_ctx->pmapptr++] = ' ';
+ grub_snprintf (fill_ctx->pmap + fill_ctx->pmapptr, 25 + 5 + 25,
+ "%" PRIuGRUB_UINT64_T " %" PRIuGRUB_UINT64_T,
+ grub_partition_get_start (partition),
+ grub_partition_get_start (partition)
+ + grub_partition_get_len (partition));
+ if (file_disk && grub_partition_get_start (partition)
+ == grub_partition_get_start (fill_ctx->file->device->disk->partition)
+ && grub_partition_get_len (partition)
+ == grub_partition_get_len (fill_ctx->file->device->disk->partition))
+ {
+ grub_free (fill_ctx->bootpart);
+ fill_ctx->bootpart = grub_strndup (fill_ctx->pmap + pstart,
+ pend - pstart);
+ }
+
+ fill_ctx->pmapptr += grub_strlen (fill_ctx->pmap + fill_ctx->pmapptr);
+ return 0;
+}
+
+/* Helper for grub_cmd_plan9. */
+static int
+fill_disk (const char *name, void *data)
+{
+ struct grub_cmd_plan9_ctx *fill_ctx = data;
+ grub_device_t dev;
+ char *plan9name = NULL;
+ unsigned i;
+ int file_disk = 0;
+
+ dev = grub_device_open (name);
+ if (!dev)
+ {
+ grub_print_error ();
+ return 0;
+ }
+ if (!dev->disk)
+ {
+ grub_device_close (dev);
+ return 0;
+ }
+ file_disk = fill_ctx->file->device->disk
+ && dev->disk->id == fill_ctx->file->device->disk->id
+ && dev->disk->dev->id == fill_ctx->file->device->disk->dev->id;
+ for (i = 0;
+ fill_ctx->ctxt->state[0].args && fill_ctx->ctxt->state[0].args[i]; i++)
+ if (grub_strncmp (name, fill_ctx->ctxt->state[0].args[i],
+ grub_strlen (name)) == 0
+ && fill_ctx->ctxt->state[0].args[i][grub_strlen (name)] == '=')
+ break;
+ if (fill_ctx->ctxt->state[0].args && fill_ctx->ctxt->state[0].args[i])
+ plan9name = grub_strdup (fill_ctx->ctxt->state[0].args[i]
+ + grub_strlen (name) + 1);
+ else
+ switch (dev->disk->dev->id)
+ {
+ case GRUB_DISK_DEVICE_BIOSDISK_ID:
+ if (dev->disk->id & 0x80)
+ plan9name = grub_xasprintf ("sdB%u",
+ (unsigned) (dev->disk->id & 0x7f));
+ else
+ plan9name = grub_xasprintf ("fd%u",
+ (unsigned) (dev->disk->id & 0x7f));
+ break;
+ /* Shouldn't happen as Plan9 doesn't work on these platforms. */
+ case GRUB_DISK_DEVICE_OFDISK_ID:
+ case GRUB_DISK_DEVICE_EFIDISK_ID:
+
+ /* Plan9 doesn't see those. */
+ default:
+
+ /* Not sure how to handle those. */
+ case GRUB_DISK_DEVICE_NAND_ID:
+ if (!file_disk)
+ {
+ grub_device_close (dev);
+ return 0;
+ }
+
+ /* if it's the disk the kernel is loaded from we need to name
+ it nevertheless. */
+ plan9name = grub_strdup ("sdZ0");
+ break;
+
+ case GRUB_DISK_DEVICE_ATA_ID:
+ {
+ unsigned unit;
+ if (grub_strlen (dev->disk->name) < sizeof ("ata0") - 1)
+ unit = 0;
+ else
+ unit = grub_strtoul (dev->disk->name + sizeof ("ata0") - 1, 0, 0);
+ plan9name = grub_xasprintf ("sd%c%d", 'C' + unit / 2, unit % 2);
+ }
+ break;
+ case GRUB_DISK_DEVICE_SCSI_ID:
+ if (((dev->disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xff)
+ == GRUB_SCSI_SUBSYSTEM_PATA)
+ {
+ unsigned unit;
+ if (grub_strlen (dev->disk->name) < sizeof ("ata0") - 1)
+ unit = 0;
+ else
+ unit = grub_strtoul (dev->disk->name + sizeof ("ata0") - 1,
+ 0, 0);
+ plan9name = grub_xasprintf ("sd%c%d", 'C' + unit / 2, unit % 2);
+ break;
+ }
+
+ /* FIXME: how does Plan9 number controllers?
+ We probably need save the SCSI devices and sort them */
+ plan9name
+ = grub_xasprintf ("sd0%u", (unsigned)
+ ((dev->disk->id >> GRUB_SCSI_ID_BUS_SHIFT)
+ & 0xf));
+ break;
+ }
+ if (!plan9name)
+ {
+ grub_print_error ();
+ grub_device_close (dev);
+ return 0;
+ }
+ if (grub_extend_alloc (fill_ctx->pmapptr + grub_strlen (plan9name)
+ + sizeof ("part="), &fill_ctx->pmapalloc,
+ &fill_ctx->pmap))
+ {
+ grub_free (plan9name);
+ grub_device_close (dev);
+ return 1;
+ }
+ grub_strcpy (fill_ctx->pmap + fill_ctx->pmapptr, plan9name);
+ fill_ctx->pmapptr += grub_strlen (plan9name);
+ if (!file_disk)
+ grub_free (plan9name);
+ else
+ {
+ grub_free (fill_ctx->bootdisk);
+ fill_ctx->bootdisk = plan9name;
+ }
+ grub_strcpy (fill_ctx->pmap + fill_ctx->pmapptr, "part=");
+ fill_ctx->pmapptr += sizeof ("part=") - 1;
+
+ fill_ctx->noslash = 1;
+ grub_memset (fill_ctx->prefixescnt, 0, sizeof (fill_ctx->prefixescnt));
+ if (grub_partition_iterate (dev->disk, fill_partition, fill_ctx))
+ {
+ grub_device_close (dev);
+ return 1;
+ }
+ if (grub_extend_alloc (fill_ctx->pmapptr + 1, &fill_ctx->pmapalloc,
+ &fill_ctx->pmap))
+ {
+ grub_device_close (dev);
+ return 1;
+ }
+ fill_ctx->pmap[fill_ctx->pmapptr++] = '\n';
+
+ grub_device_close (dev);
+ return 0;
+}
+
+static grub_err_t
+grub_cmd_plan9 (grub_extcmd_context_t ctxt, int argc, char *argv[])
+{
+ struct grub_cmd_plan9_ctx fill_ctx = {
+ .ctxt = ctxt,
+ .file = 0,
+ .pmap = NULL,
+ .pmapalloc = 256,
+ .pmapptr = 0,
+ .noslash = 1,
+ .bootdisk = NULL,
+ .bootpart = NULL
+ };
+ void *mem;
+ grub_size_t memsize, padsize;
+ struct grub_plan9_header hdr;
+ char *config, *configptr;
+ grub_size_t configsize;
+ char *bootpath = NULL;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ grub_dl_ref (my_mod);
+
+ rel = grub_relocator_new ();
+ if (!rel)
+ goto fail;
+
+ fill_ctx.file = grub_file_open (argv[0], GRUB_FILE_TYPE_PLAN9_KERNEL);
+ if (! fill_ctx.file)
+ goto fail;
+
+ fill_ctx.pmap = grub_malloc (fill_ctx.pmapalloc);
+ if (!fill_ctx.pmap)
+ goto fail;
+
+ if (grub_disk_dev_iterate (fill_disk, &fill_ctx))
+ goto fail;
+
+ if (grub_extend_alloc (fill_ctx.pmapptr + 1, &fill_ctx.pmapalloc,
+ &fill_ctx.pmap))
+ goto fail;
+ fill_ctx.pmap[fill_ctx.pmapptr] = 0;
+
+ {
+ char *file_name = grub_strchr (argv[0], ')');
+ if (file_name)
+ file_name++;
+ else
+ file_name = argv[0];
+ if (*file_name)
+ file_name++;
+
+ if (fill_ctx.bootpart)
+ bootpath = grub_xasprintf ("%s!%s!%s", fill_ctx.bootdisk,
+ fill_ctx.bootpart, file_name);
+ else
+ bootpath = grub_xasprintf ("%s!%s", fill_ctx.bootdisk, file_name);
+ grub_free (fill_ctx.bootdisk);
+ grub_free (fill_ctx.bootpart);
+ }
+ if (!bootpath)
+ goto fail;
+
+ if (grub_file_read (fill_ctx.file, &hdr,
+ sizeof (hdr)) != (grub_ssize_t) sizeof (hdr))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+ goto fail;
+ }
+
+ if (grub_be_to_cpu32 (hdr.magic) != GRUB_PLAN9_MAGIC
+ || hdr.zero)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "unsupported Plan9");
+ goto fail;
+ }
+
+ memsize = ALIGN_UP (grub_be_to_cpu32 (hdr.text_size) + sizeof (hdr),
+ GRUB_PLAN9_ALIGN);
+ memsize += ALIGN_UP (grub_be_to_cpu32 (hdr.data_size), GRUB_PLAN9_ALIGN);
+ memsize += ALIGN_UP(grub_be_to_cpu32 (hdr.bss_size), GRUB_PLAN9_ALIGN);
+ eip = grub_be_to_cpu32 (hdr.entry_addr) & 0xfffffff;
+
+ /* path */
+ configsize = GRUB_PLAN9_CONFIG_PATH_SIZE;
+ /* magic */
+ configsize += sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1;
+ {
+ int i;
+ for (i = 1; i < argc; i++)
+ configsize += grub_strlen (argv[i]) + 1;
+ }
+ configsize += (sizeof ("bootfile=") - 1) + grub_strlen (bootpath) + 1;
+ configsize += fill_ctx.pmapptr;
+ /* Terminating \0. */
+ configsize++;
+
+ {
+ grub_relocator_chunk_t ch;
+ grub_err_t err;
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_PLAN9_CONFIG_ADDR,
+ configsize);
+ if (err)
+ goto fail;
+ config = get_virtual_current_address (ch);
+ }
+
+ grub_memset (config, 0, GRUB_PLAN9_CONFIG_PATH_SIZE);
+ grub_strncpy (config, bootpath, GRUB_PLAN9_CONFIG_PATH_SIZE - 1);
+
+ configptr = config + GRUB_PLAN9_CONFIG_PATH_SIZE;
+ grub_memcpy (configptr, GRUB_PLAN9_CONFIG_MAGIC,
+ sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1);
+ configptr += sizeof (GRUB_PLAN9_CONFIG_MAGIC) - 1;
+ configptr = grub_stpcpy (configptr, "bootfile=");
+ configptr = grub_stpcpy (configptr, bootpath);
+ *configptr++ = '\n';
+ char *cmdline = configptr;
+ {
+ int i;
+ for (i = 1; i < argc; i++)
+ {
+ configptr = grub_stpcpy (configptr, argv[i]);
+ *configptr++ = '\n';
+ }
+ }
+
+ {
+ grub_err_t err;
+ *configptr = '\0';
+ err = grub_verify_string (cmdline, GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ goto fail;
+ }
+
+ configptr = grub_stpcpy (configptr, fill_ctx.pmap);
+
+ {
+ grub_relocator_chunk_t ch;
+ grub_err_t err;
+
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_PLAN9_TARGET,
+ memsize);
+ if (err)
+ goto fail;
+ mem = get_virtual_current_address (ch);
+ }
+
+ {
+ grub_uint8_t *ptr;
+ ptr = mem;
+ grub_memcpy (ptr, &hdr, sizeof (hdr));
+ ptr += sizeof (hdr);
+
+ if (grub_file_read (fill_ctx.file, ptr, grub_be_to_cpu32 (hdr.text_size))
+ != (grub_ssize_t) grub_be_to_cpu32 (hdr.text_size))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+ goto fail;
+ }
+ ptr += grub_be_to_cpu32 (hdr.text_size);
+ padsize = ALIGN_UP (grub_be_to_cpu32 (hdr.text_size) + sizeof (hdr),
+ GRUB_PLAN9_ALIGN) - grub_be_to_cpu32 (hdr.text_size)
+ - sizeof (hdr);
+
+ grub_memset (ptr, 0, padsize);
+ ptr += padsize;
+
+ if (grub_file_read (fill_ctx.file, ptr, grub_be_to_cpu32 (hdr.data_size))
+ != (grub_ssize_t) grub_be_to_cpu32 (hdr.data_size))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+ goto fail;
+ }
+ ptr += grub_be_to_cpu32 (hdr.data_size);
+ padsize = ALIGN_UP (grub_be_to_cpu32 (hdr.data_size), GRUB_PLAN9_ALIGN)
+ - grub_be_to_cpu32 (hdr.data_size);
+
+ grub_memset (ptr, 0, padsize);
+ ptr += padsize;
+ grub_memset (ptr, 0, ALIGN_UP(grub_be_to_cpu32 (hdr.bss_size),
+ GRUB_PLAN9_ALIGN));
+ }
+ grub_loader_set (grub_plan9_boot, grub_plan9_unload, 1);
+ return GRUB_ERR_NONE;
+
+ fail:
+ grub_free (fill_ctx.pmap);
+
+ if (fill_ctx.file)
+ grub_file_close (fill_ctx.file);
+
+ grub_plan9_unload ();
+
+ return grub_errno;
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT(plan9)
+{
+ cmd = grub_register_extcmd ("plan9", grub_cmd_plan9,
+ GRUB_COMMAND_OPTIONS_AT_START,
+ N_("KERNEL ARGS"), N_("Load Plan9 kernel."),
+ options);
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(plan9)
+{
+ grub_unregister_extcmd (cmd);
+}
diff --git a/grub-core/loader/i386/pc/pxechainloader.c b/grub-core/loader/i386/pc/pxechainloader.c
new file mode 100644
index 0000000..acb0611
--- /dev/null
+++ b/grub-core/loader/i386/pc/pxechainloader.c
@@ -0,0 +1,168 @@
+/* chainloader.c - boot another boot loader */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2004,2007,2009,2010,2012 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>
+#include <grub/machine/pxe.h>
+#include <grub/net.h>
+
+static grub_dl_t my_mod;
+static struct grub_relocator *rel;
+static grub_uint32_t edx = 0xffffffff;
+static char boot_file[128];
+static char server_name[64];
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_err_t
+grub_pxechain_boot (void)
+{
+ struct grub_relocator16_state state = {
+ .cs = 0,
+ .ip = 0x7c00,
+ .ds = 0,
+ .es = 0,
+ .fs = 0,
+ .gs = 0,
+ .ss = 0,
+ .sp = 0x7c00,
+ .edx = edx
+ };
+ struct grub_net_bootp_packet *bp;
+
+ bp = grub_pxe_get_cached (GRUB_PXENV_PACKET_TYPE_DHCP_ACK);
+
+ grub_video_set_mode ("text", 0, 0);
+
+ if (bp && boot_file[0])
+ grub_memcpy (bp->boot_file, boot_file, sizeof (bp->boot_file));
+ if (bp && server_name[0])
+ grub_memcpy (bp->server_name, server_name, sizeof (bp->server_name));
+
+ return grub_relocator16_boot (rel, state);
+}
+
+static grub_err_t
+grub_pxechain_unload (void)
+{
+ grub_relocator_unload (rel);
+ rel = NULL;
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_pxechain (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_err_t err;
+ void *image;
+ grub_size_t imagesize;
+ char *fname;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ grub_dl_ref (my_mod);
+
+ rel = grub_relocator_new ();
+ if (!rel)
+ goto fail;
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_PXECHAINLOADER);
+ if (! file)
+ goto fail;
+
+ if (file->device->net && file->device->net->name)
+ fname = file->device->net->name;
+ else
+ {
+ fname = argv[0];
+ if (fname[0] == '(')
+ {
+ fname = grub_strchr (fname, ')');
+ if (fname)
+ fname++;
+ else
+ fname = argv[0];
+ }
+ }
+
+ grub_memset (boot_file, 0, sizeof (boot_file));
+ grub_strncpy (boot_file, fname, sizeof (boot_file));
+
+ grub_memset (server_name, 0, sizeof (server_name));
+ if (file->device->net && file->device->net->server)
+ grub_strncpy (server_name, file->device->net->server, sizeof (server_name));
+
+ edx = grub_get_root_biosnumber ();
+
+ imagesize = grub_file_size (file);
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, 0x7c00, imagesize);
+ if (err)
+ goto fail;
+ image = get_virtual_current_address (ch);
+ }
+
+ if (grub_file_read (file, image, imagesize) != (grub_ssize_t) imagesize)
+ goto fail;
+
+ grub_loader_set (grub_pxechain_boot, grub_pxechain_unload,
+ GRUB_LOADER_FLAG_NORETURN | GRUB_LOADER_FLAG_PXE_NOT_UNLOAD);
+ return GRUB_ERR_NONE;
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ grub_pxechain_unload ();
+
+ return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(pxechainloader)
+{
+ cmd = grub_register_command ("pxechainloader", grub_cmd_pxechain,
+ 0, N_("Load a PXE image."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(pxechainloader)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/loader/i386/pc/truecrypt.c b/grub-core/loader/i386/pc/truecrypt.c
new file mode 100644
index 0000000..cbeeec7
--- /dev/null
+++ b/grub-core/loader/i386/pc/truecrypt.c
@@ -0,0 +1,233 @@
+/* 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>
+#include <grub/deflate.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+static struct grub_relocator *rel;
+static grub_uint32_t edx = 0xffffffff;
+static grub_uint16_t sp;
+static grub_uint32_t destaddr;
+
+#define GRUB_TRUECRYPT_SEGMENT 0x2000
+
+static grub_err_t
+grub_truecrypt_boot (void)
+{
+ grub_uint16_t segment = destaddr >> 4;
+ struct grub_relocator16_state state = {
+ .cs = segment,
+ .ds = segment,
+ .es = segment,
+ .fs = segment,
+ .gs = segment,
+ .ss = segment,
+ .ip = 0x100,
+ .sp = sp,
+ .edx = edx,
+ .a20 = 1
+ };
+ grub_video_set_mode ("text", 0, 0);
+
+ return grub_relocator16_boot (rel, state);
+}
+
+static grub_err_t
+grub_truecrypt_unload (void)
+{
+ grub_relocator_unload (rel);
+ rel = NULL;
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+/* Information on protocol supplied by Attila Lendvai. */
+#define MAGIC "\0CD001\1EL TORITO SPECIFICATION"
+
+static grub_err_t
+grub_cmd_truecrypt (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_err_t err;
+ void *truecrypt;
+ grub_size_t truecryptsize;
+ const grub_size_t truecryptmemsize = 42 * 1024;
+ grub_uint8_t dh;
+ grub_uint32_t catalog, rba;
+ grub_uint8_t buf[128];
+ char *compressed = NULL;
+ char *uncompressed = NULL;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ rel = NULL;
+
+ grub_dl_ref (my_mod);
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_TRUECRYPT);
+ if (! file)
+ goto fail;
+
+ if (grub_file_seek (file, 17 * 2048) == (grub_size_t) -1)
+ goto fail;
+
+ if (grub_file_read (file, buf, sizeof (buf))
+ != sizeof (buf))
+ goto fail;
+
+ if (grub_memcmp (buf, MAGIC, sizeof (MAGIC)) != 0)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid eltorito signature");
+ goto fail;
+ }
+
+ catalog = grub_get_unaligned32 (buf + 0x47);
+
+ if (grub_file_seek (file, catalog * 2048) == (grub_size_t)-1)
+ goto fail;
+
+ if (grub_file_read (file, buf, sizeof (buf))
+ != sizeof (buf))
+ goto fail;
+
+ if (buf[0] != 1 || buf[1] != 0 || buf[0x1e] != 0x55
+ || buf[0x1f] != 0xaa || buf[0x20] != 0x88
+ || buf[0x26] != 1 || buf[0x27] != 0)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid eltorito catalog");
+ goto fail;
+ }
+
+ rba = grub_get_unaligned32 (buf + 0x28);
+
+ if (grub_file_seek (file, rba * 2048 + 0x1b7) == (grub_size_t) -1)
+ goto fail;
+
+ if (grub_file_read (file, &dh, 1)
+ != 1)
+ goto fail;
+
+ if (grub_file_seek (file, rba * 2048 + 512 + 2048) == (grub_size_t) -1)
+ goto fail;
+
+ compressed = grub_malloc (57 * 512);
+ if (!compressed)
+ goto fail;
+
+ if (grub_file_read (file, compressed, 57 * 512)
+ != 57 * 512)
+ goto fail;
+
+ uncompressed = grub_malloc (truecryptmemsize);
+ if (!uncompressed)
+ goto fail;
+
+ /* It's actually gzip but our gzip decompressor isn't able to handle
+ trailing garbage, hence let's use raw decompressor. */
+ truecryptsize = grub_deflate_decompress (compressed + 10, 57 * 512 - 10,
+ 0, uncompressed, truecryptmemsize);
+ if ((grub_ssize_t) truecryptsize < 0)
+ goto fail;
+
+ if (truecryptmemsize <= truecryptsize + 0x100)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "file is too big");
+ goto fail;
+ }
+
+ rel = grub_relocator_new ();
+ if (!rel)
+ goto fail;
+
+ edx = (dh << 8) | grub_get_root_biosnumber ();
+
+ destaddr = ALIGN_DOWN (grub_min (0x90000, grub_mmap_get_lower ())
+ - truecryptmemsize, 64 * 1024);
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, destaddr,
+ truecryptmemsize);
+ if (err)
+ goto fail;
+ truecrypt = get_virtual_current_address (ch);
+ }
+
+ grub_memset (truecrypt, 0, 0x100);
+ grub_memcpy ((char *) truecrypt + 0x100, uncompressed, truecryptsize);
+
+ grub_memset ((char *) truecrypt + truecryptsize + 0x100,
+ 0, truecryptmemsize - truecryptsize - 0x100);
+ sp = truecryptmemsize - 4;
+
+ grub_loader_set (grub_truecrypt_boot, grub_truecrypt_unload, 1);
+
+ grub_free (uncompressed);
+ grub_free (compressed);
+
+ return GRUB_ERR_NONE;
+
+ fail:
+
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, "bad truecrypt ISO");
+
+ if (file)
+ grub_file_close (file);
+
+ grub_truecrypt_unload ();
+
+ grub_free (uncompressed);
+ grub_free (compressed);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(truecrypt)
+{
+ cmd = grub_register_command ("truecrypt", grub_cmd_truecrypt,
+ 0, N_("Load Truecrypt ISO."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(truecrypt)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/loader/i386/xen.c b/grub-core/loader/i386/xen.c
new file mode 100644
index 0000000..cd24874
--- /dev/null
+++ b/grub-core/loader/i386/xen.c
@@ -0,0 +1,986 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/xen/relocator.h>
+#include <grub/i18n.h>
+#include <grub/elf.h>
+#include <grub/elfload.h>
+#include <grub/lib/cmdline.h>
+#include <grub/xen.h>
+#include <grub/xen_file.h>
+#include <grub/linux.h>
+#include <grub/i386/memory.h>
+#include <grub/verify.h>
+#include <grub/safemath.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#ifdef __x86_64__
+#define NUMBER_OF_LEVELS 4
+#define INTERMEDIATE_OR (GRUB_PAGE_PRESENT | GRUB_PAGE_RW | GRUB_PAGE_USER)
+#define VIRT_MASK 0x0000ffffffffffffULL
+#else
+#define NUMBER_OF_LEVELS 3
+#define INTERMEDIATE_OR (GRUB_PAGE_PRESENT | GRUB_PAGE_RW)
+#define VIRT_MASK 0x00000000ffffffffULL
+#define HYPERVISOR_PUD_ADDRESS 0xc0000000ULL
+#endif
+
+struct grub_xen_mapping_lvl {
+ grub_uint64_t virt_start;
+ grub_uint64_t virt_end;
+ grub_uint64_t pfn_start;
+ grub_uint64_t n_pt_pages;
+};
+
+struct grub_xen_mapping {
+ grub_uint64_t *where;
+ struct grub_xen_mapping_lvl area;
+ struct grub_xen_mapping_lvl lvls[NUMBER_OF_LEVELS];
+};
+
+struct xen_loader_state {
+ struct grub_relocator *relocator;
+ struct grub_relocator_xen_state state;
+ struct start_info next_start;
+ struct grub_xen_file_info xen_inf;
+ grub_xen_mfn_t *virt_mfn_list;
+ struct start_info *virt_start_info;
+ grub_xen_mfn_t console_pfn;
+ grub_uint64_t max_addr;
+ grub_uint64_t pgtbl_end;
+ struct xen_multiboot_mod_list *module_info_page;
+ grub_uint64_t modules_target_start;
+ grub_size_t n_modules;
+ struct grub_xen_mapping *map_reloc;
+ struct grub_xen_mapping mappings[XEN_MAX_MAPPINGS];
+ int n_mappings;
+ int loaded;
+};
+
+static struct xen_loader_state xen_state;
+
+static grub_dl_t my_mod;
+
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list))
+#define STACK_SIZE 1048576
+#define ADDITIONAL_SIZE (1 << 19)
+#define ALIGN_SIZE (1 << 22)
+#define LOG_POINTERS_PER_PAGE 9
+#define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE)
+
+static grub_uint64_t
+page2offset (grub_uint64_t page)
+{
+ return page << PAGE_SHIFT;
+}
+
+static grub_err_t
+get_pgtable_size (grub_uint64_t from, grub_uint64_t to, grub_uint64_t pfn)
+{
+ struct grub_xen_mapping *map, *map_cmp;
+ grub_uint64_t mask, bits;
+ int i, m;
+
+ if (xen_state.n_mappings == XEN_MAX_MAPPINGS)
+ return grub_error (GRUB_ERR_BUG, "too many mapped areas");
+
+ grub_dprintf ("xen", "get_pgtable_size %d from=%llx, to=%llx, pfn=%llx\n",
+ xen_state.n_mappings, (unsigned long long) from,
+ (unsigned long long) to, (unsigned long long) pfn);
+
+ map = xen_state.mappings + xen_state.n_mappings;
+ grub_memset (map, 0, sizeof (*map));
+
+ map->area.virt_start = from & VIRT_MASK;
+ map->area.virt_end = (to - 1) & VIRT_MASK;
+ map->area.n_pt_pages = 0;
+
+ for (i = NUMBER_OF_LEVELS - 1; i >= 0; i--)
+ {
+ map->lvls[i].pfn_start = pfn + map->area.n_pt_pages;
+ if (i == NUMBER_OF_LEVELS - 1)
+ {
+ if (xen_state.n_mappings == 0)
+ {
+ map->lvls[i].virt_start = 0;
+ map->lvls[i].virt_end = VIRT_MASK;
+ map->lvls[i].n_pt_pages = 1;
+ map->area.n_pt_pages++;
+ }
+ continue;
+ }
+
+ bits = PAGE_SHIFT + (i + 1) * LOG_POINTERS_PER_PAGE;
+ mask = (1ULL << bits) - 1;
+ map->lvls[i].virt_start = map->area.virt_start & ~mask;
+ map->lvls[i].virt_end = map->area.virt_end | mask;
+#ifdef __i386__
+ /* PAE wants last root directory present. */
+ if (i == 1 && to <= HYPERVISOR_PUD_ADDRESS && xen_state.n_mappings == 0)
+ map->lvls[i].virt_end = VIRT_MASK;
+#endif
+ for (m = 0; m < xen_state.n_mappings; m++)
+ {
+ map_cmp = xen_state.mappings + m;
+ if (map_cmp->lvls[i].virt_start == map_cmp->lvls[i].virt_end)
+ continue;
+ if (map->lvls[i].virt_start >= map_cmp->lvls[i].virt_start &&
+ map->lvls[i].virt_end <= map_cmp->lvls[i].virt_end)
+ {
+ map->lvls[i].virt_start = 0;
+ map->lvls[i].virt_end = 0;
+ break;
+ }
+ if (map->lvls[i].virt_start >= map_cmp->lvls[i].virt_start &&
+ map->lvls[i].virt_start <= map_cmp->lvls[i].virt_end)
+ map->lvls[i].virt_start = map_cmp->lvls[i].virt_end + 1;
+ if (map->lvls[i].virt_end >= map_cmp->lvls[i].virt_start &&
+ map->lvls[i].virt_end <= map_cmp->lvls[i].virt_end)
+ map->lvls[i].virt_end = map_cmp->lvls[i].virt_start - 1;
+ }
+ if (map->lvls[i].virt_start < map->lvls[i].virt_end)
+ map->lvls[i].n_pt_pages =
+ ((map->lvls[i].virt_end - map->lvls[i].virt_start) >> bits) + 1;
+ map->area.n_pt_pages += map->lvls[i].n_pt_pages;
+ grub_dprintf ("xen", "get_pgtable_size level %d: virt %llx-%llx %d pts\n",
+ i, (unsigned long long) map->lvls[i].virt_start,
+ (unsigned long long) map->lvls[i].virt_end,
+ (int) map->lvls[i].n_pt_pages);
+ }
+
+ grub_dprintf ("xen", "get_pgtable_size return: %d page tables\n",
+ (int) map->area.n_pt_pages);
+
+ xen_state.state.paging_start[xen_state.n_mappings] = pfn;
+ xen_state.state.paging_size[xen_state.n_mappings] = map->area.n_pt_pages;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_uint64_t *
+get_pg_table_virt (int mapping, int level)
+{
+ grub_uint64_t pfn;
+ struct grub_xen_mapping *map;
+
+ map = xen_state.mappings + mapping;
+ pfn = map->lvls[level].pfn_start - map->lvls[NUMBER_OF_LEVELS - 1].pfn_start;
+ return map->where + pfn * POINTERS_PER_PAGE;
+}
+
+static grub_uint64_t
+get_pg_table_prot (int level, grub_uint64_t pfn)
+{
+ int m;
+ grub_uint64_t pfn_s, pfn_e;
+
+ if (level > 0)
+ return INTERMEDIATE_OR;
+ for (m = 0; m < xen_state.n_mappings; m++)
+ {
+ pfn_s = xen_state.mappings[m].lvls[NUMBER_OF_LEVELS - 1].pfn_start;
+ pfn_e = xen_state.mappings[m].area.n_pt_pages + pfn_s;
+ if (pfn >= pfn_s && pfn < pfn_e)
+ return GRUB_PAGE_PRESENT | GRUB_PAGE_USER;
+ }
+ return GRUB_PAGE_PRESENT | GRUB_PAGE_RW | GRUB_PAGE_USER;
+}
+
+static void
+generate_page_table (grub_xen_mfn_t *mfn_list)
+{
+ int l, m1, m2;
+ long p, p_s, p_e;
+ grub_uint64_t start, end, pfn;
+ grub_uint64_t *pg;
+ struct grub_xen_mapping_lvl *lvl;
+
+ for (m1 = 0; m1 < xen_state.n_mappings; m1++)
+ grub_memset (xen_state.mappings[m1].where, 0,
+ xen_state.mappings[m1].area.n_pt_pages * PAGE_SIZE);
+
+ for (l = NUMBER_OF_LEVELS - 1; l >= 0; l--)
+ {
+ for (m1 = 0; m1 < xen_state.n_mappings; m1++)
+ {
+ start = xen_state.mappings[m1].lvls[l].virt_start;
+ end = xen_state.mappings[m1].lvls[l].virt_end;
+ pg = get_pg_table_virt(m1, l);
+ for (m2 = 0; m2 < xen_state.n_mappings; m2++)
+ {
+ lvl = (l > 0) ? xen_state.mappings[m2].lvls + l - 1
+ : &xen_state.mappings[m2].area;
+ if (l > 0 && lvl->n_pt_pages == 0)
+ continue;
+ if (lvl->virt_start >= end || lvl->virt_end <= start)
+ continue;
+ p_s = (grub_max (start, lvl->virt_start) - start) >>
+ (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE);
+ p_e = (grub_min (end, lvl->virt_end) - start) >>
+ (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE);
+ pfn = ((grub_max (start, lvl->virt_start) - lvl->virt_start) >>
+ (PAGE_SHIFT + l * LOG_POINTERS_PER_PAGE)) + lvl->pfn_start;
+ grub_dprintf ("xen", "write page table entries level %d pg %p "
+ "mapping %d/%d index %lx-%lx pfn %llx\n",
+ l, pg, m1, m2, p_s, p_e, (unsigned long long) pfn);
+ for (p = p_s; p <= p_e; p++)
+ {
+ pg[p] = page2offset (mfn_list[pfn]) |
+ get_pg_table_prot (l, pfn);
+ pfn++;
+ }
+ }
+ }
+ }
+}
+
+static grub_err_t
+set_mfns (grub_xen_mfn_t pfn)
+{
+ grub_xen_mfn_t i, t;
+ grub_xen_mfn_t cn_pfn = -1, st_pfn = -1;
+ struct mmu_update m2p_updates[4];
+
+
+ for (i = 0; i < grub_xen_start_page_addr->nr_pages; i++)
+ {
+ if (xen_state.virt_mfn_list[i] ==
+ grub_xen_start_page_addr->console.domU.mfn)
+ cn_pfn = i;
+ if (xen_state.virt_mfn_list[i] == grub_xen_start_page_addr->store_mfn)
+ st_pfn = i;
+ }
+ if (cn_pfn == (grub_xen_mfn_t)-1)
+ return grub_error (GRUB_ERR_BUG, "no console");
+ if (st_pfn == (grub_xen_mfn_t)-1)
+ return grub_error (GRUB_ERR_BUG, "no store");
+ t = xen_state.virt_mfn_list[pfn];
+ xen_state.virt_mfn_list[pfn] = xen_state.virt_mfn_list[cn_pfn];
+ xen_state.virt_mfn_list[cn_pfn] = t;
+ t = xen_state.virt_mfn_list[pfn + 1];
+ xen_state.virt_mfn_list[pfn + 1] = xen_state.virt_mfn_list[st_pfn];
+ xen_state.virt_mfn_list[st_pfn] = t;
+
+ m2p_updates[0].ptr =
+ page2offset (xen_state.virt_mfn_list[pfn]) | MMU_MACHPHYS_UPDATE;
+ m2p_updates[0].val = pfn;
+ m2p_updates[1].ptr =
+ page2offset (xen_state.virt_mfn_list[pfn + 1]) | MMU_MACHPHYS_UPDATE;
+ m2p_updates[1].val = pfn + 1;
+ m2p_updates[2].ptr =
+ page2offset (xen_state.virt_mfn_list[cn_pfn]) | MMU_MACHPHYS_UPDATE;
+ m2p_updates[2].val = cn_pfn;
+ m2p_updates[3].ptr =
+ page2offset (xen_state.virt_mfn_list[st_pfn]) | MMU_MACHPHYS_UPDATE;
+ m2p_updates[3].val = st_pfn;
+
+ grub_xen_mmu_update (m2p_updates, 4, NULL, DOMID_SELF);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_xen_p2m_alloc (void)
+{
+ grub_relocator_chunk_t ch;
+ grub_size_t p2msize, p2malloc;
+ grub_err_t err;
+ struct grub_xen_mapping *map;
+
+ if (xen_state.virt_mfn_list)
+ return GRUB_ERR_NONE;
+
+ map = xen_state.mappings + xen_state.n_mappings;
+ p2msize = ALIGN_UP (sizeof (grub_xen_mfn_t) *
+ grub_xen_start_page_addr->nr_pages, PAGE_SIZE);
+ if (xen_state.xen_inf.has_p2m_base)
+ {
+ err = get_pgtable_size (xen_state.xen_inf.p2m_base,
+ xen_state.xen_inf.p2m_base + p2msize,
+ (xen_state.max_addr + p2msize) >> PAGE_SHIFT);
+ if (err)
+ return err;
+
+ map->area.pfn_start = xen_state.max_addr >> PAGE_SHIFT;
+ p2malloc = p2msize + page2offset (map->area.n_pt_pages);
+ xen_state.n_mappings++;
+ xen_state.next_start.mfn_list = xen_state.xen_inf.p2m_base;
+ xen_state.next_start.first_p2m_pfn = map->area.pfn_start;
+ xen_state.next_start.nr_p2m_frames = p2malloc >> PAGE_SHIFT;
+ }
+ else
+ {
+ xen_state.next_start.mfn_list =
+ xen_state.max_addr + xen_state.xen_inf.virt_base;
+ p2malloc = p2msize;
+ }
+
+ xen_state.state.mfn_list = xen_state.max_addr;
+ err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
+ xen_state.max_addr, p2malloc);
+ if (err)
+ return err;
+ xen_state.virt_mfn_list = get_virtual_current_address (ch);
+ if (xen_state.xen_inf.has_p2m_base)
+ map->where = (grub_uint64_t *) xen_state.virt_mfn_list +
+ p2msize / sizeof (grub_uint64_t);
+ grub_memcpy (xen_state.virt_mfn_list,
+ (void *) grub_xen_start_page_addr->mfn_list, p2msize);
+ xen_state.max_addr += p2malloc;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_xen_special_alloc (void)
+{
+ grub_relocator_chunk_t ch;
+ grub_err_t err;
+
+ if (xen_state.virt_start_info)
+ return GRUB_ERR_NONE;
+
+ err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
+ xen_state.max_addr,
+ sizeof (xen_state.next_start));
+ if (err)
+ return err;
+ xen_state.state.start_info = xen_state.max_addr + xen_state.xen_inf.virt_base;
+ xen_state.virt_start_info = get_virtual_current_address (ch);
+ xen_state.max_addr =
+ ALIGN_UP (xen_state.max_addr + sizeof (xen_state.next_start), PAGE_SIZE);
+ xen_state.console_pfn = xen_state.max_addr >> PAGE_SHIFT;
+ xen_state.max_addr += 2 * PAGE_SIZE;
+
+ xen_state.next_start.nr_pages = grub_xen_start_page_addr->nr_pages;
+ grub_memcpy (xen_state.next_start.magic, grub_xen_start_page_addr->magic,
+ sizeof (xen_state.next_start.magic));
+ xen_state.next_start.store_mfn = grub_xen_start_page_addr->store_mfn;
+ xen_state.next_start.store_evtchn = grub_xen_start_page_addr->store_evtchn;
+ xen_state.next_start.console.domU = grub_xen_start_page_addr->console.domU;
+ xen_state.next_start.shared_info = grub_xen_start_page_addr->shared_info;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_xen_pt_alloc (void)
+{
+ grub_relocator_chunk_t ch;
+ grub_err_t err;
+ grub_uint64_t nr_info_pages;
+ grub_uint64_t nr_need_pages;
+ grub_uint64_t try_virt_end;
+ struct grub_xen_mapping *map;
+
+ if (xen_state.pgtbl_end)
+ return GRUB_ERR_NONE;
+
+ map = xen_state.mappings + xen_state.n_mappings;
+ xen_state.map_reloc = map + 1;
+
+ xen_state.next_start.pt_base =
+ xen_state.max_addr + xen_state.xen_inf.virt_base;
+ nr_info_pages = xen_state.max_addr >> PAGE_SHIFT;
+ nr_need_pages = nr_info_pages;
+
+ while (1)
+ {
+ try_virt_end = ALIGN_UP (xen_state.xen_inf.virt_base +
+ page2offset (nr_need_pages) +
+ ADDITIONAL_SIZE + STACK_SIZE, ALIGN_SIZE);
+
+ err = get_pgtable_size (xen_state.xen_inf.virt_base, try_virt_end,
+ nr_info_pages);
+ if (err)
+ return err;
+ xen_state.n_mappings++;
+
+ /* Map the relocator page either at virtual 0 or after end of area. */
+ nr_need_pages = nr_info_pages + map->area.n_pt_pages;
+ if (xen_state.xen_inf.virt_base)
+ err = get_pgtable_size (0, PAGE_SIZE, nr_need_pages);
+ else
+ err = get_pgtable_size (try_virt_end, try_virt_end + PAGE_SIZE,
+ nr_need_pages);
+ if (err)
+ return err;
+ nr_need_pages += xen_state.map_reloc->area.n_pt_pages;
+
+ if (xen_state.xen_inf.virt_base + page2offset (nr_need_pages) <=
+ try_virt_end)
+ break;
+
+ xen_state.n_mappings--;
+ }
+
+ xen_state.n_mappings++;
+ nr_need_pages = map->area.n_pt_pages + xen_state.map_reloc->area.n_pt_pages;
+ err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
+ xen_state.max_addr,
+ page2offset (nr_need_pages));
+ if (err)
+ return err;
+
+ map->where = get_virtual_current_address (ch);
+ map->area.pfn_start = 0;
+ xen_state.max_addr += page2offset (nr_need_pages);
+ xen_state.state.stack =
+ xen_state.max_addr + STACK_SIZE + xen_state.xen_inf.virt_base;
+ xen_state.next_start.nr_pt_frames = nr_need_pages;
+ xen_state.max_addr = try_virt_end - xen_state.xen_inf.virt_base;
+ xen_state.pgtbl_end = xen_state.max_addr >> PAGE_SHIFT;
+ xen_state.map_reloc->where = (grub_uint64_t *) ((char *) map->where +
+ page2offset (map->area.n_pt_pages));
+
+ return GRUB_ERR_NONE;
+}
+
+/* Allocate all not yet allocated areas mapped by initial page tables. */
+static grub_err_t
+grub_xen_alloc_boot_data (void)
+{
+ grub_err_t err;
+
+ if (!xen_state.xen_inf.has_p2m_base)
+ {
+ err = grub_xen_p2m_alloc ();
+ if (err)
+ return err;
+ }
+ err = grub_xen_special_alloc ();
+ if (err)
+ return err;
+ err = grub_xen_pt_alloc ();
+ if (err)
+ return err;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_xen_boot (void)
+{
+ grub_err_t err;
+ grub_uint64_t nr_pages;
+ struct gnttab_set_version gnttab_setver;
+ grub_size_t i;
+
+ if (grub_xen_n_allocated_shared_pages)
+ return grub_error (GRUB_ERR_BUG, "active grants");
+
+ err = grub_xen_alloc_boot_data ();
+ if (err)
+ return err;
+ if (xen_state.xen_inf.has_p2m_base)
+ {
+ err = grub_xen_p2m_alloc ();
+ if (err)
+ return err;
+ }
+
+ err = set_mfns (xen_state.console_pfn);
+ if (err)
+ return err;
+
+ nr_pages = xen_state.max_addr >> PAGE_SHIFT;
+
+ grub_dprintf ("xen", "bootstrap domain %llx+%llx\n",
+ (unsigned long long) xen_state.xen_inf.virt_base,
+ (unsigned long long) page2offset (nr_pages));
+
+ xen_state.map_reloc->area.pfn_start = nr_pages;
+ generate_page_table (xen_state.virt_mfn_list);
+
+ xen_state.state.entry_point = xen_state.xen_inf.entry_point;
+
+ *xen_state.virt_start_info = xen_state.next_start;
+
+ grub_memset (&gnttab_setver, 0, sizeof (gnttab_setver));
+
+ gnttab_setver.version = 1;
+ grub_xen_grant_table_op (GNTTABOP_set_version, &gnttab_setver, 1);
+
+ for (i = 0; i < ARRAY_SIZE (grub_xen_shared_info->evtchn_pending); i++)
+ grub_xen_shared_info->evtchn_pending[i] = 0;
+
+ return grub_relocator_xen_boot (xen_state.relocator, xen_state.state, nr_pages,
+ xen_state.xen_inf.virt_base <
+ PAGE_SIZE ? page2offset (nr_pages) : 0,
+ xen_state.pgtbl_end - 1,
+ page2offset (xen_state.pgtbl_end - 1) +
+ xen_state.xen_inf.virt_base);
+}
+
+static void
+grub_xen_reset (void)
+{
+ grub_relocator_unload (xen_state.relocator);
+
+ grub_memset (&xen_state, 0, sizeof (xen_state));
+}
+
+static grub_err_t
+grub_xen_unload (void)
+{
+ grub_xen_reset ();
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+#define HYPERCALL_INTERFACE_SIZE 32
+
+#ifdef __x86_64__
+static grub_uint8_t template[] =
+ {
+ 0x51, /* push %rcx */
+ 0x41, 0x53, /* push %r11 */
+ 0x48, 0xc7, 0xc0, 0xbb, 0xaa, 0x00, 0x00, /* mov $0xaabb,%rax */
+ 0x0f, 0x05, /* syscall */
+ 0x41, 0x5b, /* pop %r11 */
+ 0x59, /* pop %rcx */
+ 0xc3 /* ret */
+ };
+
+static grub_uint8_t template_iret[] =
+ {
+ 0x51, /* push %rcx */
+ 0x41, 0x53, /* push %r11 */
+ 0x50, /* push %rax */
+ 0x48, 0xc7, 0xc0, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%rax */
+ 0x0f, 0x05 /* syscall */
+ };
+#define CALLNO_OFFSET 6
+#else
+
+static grub_uint8_t template[] =
+ {
+ 0xb8, 0xbb, 0xaa, 0x00, 0x00, /* mov imm32, %eax */
+ 0xcd, 0x82, /* int $0x82 */
+ 0xc3 /* ret */
+ };
+
+static grub_uint8_t template_iret[] =
+ {
+ 0x50, /* push %eax */
+ 0xb8, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%eax */
+ 0xcd, 0x82, /* int $0x82 */
+ };
+#define CALLNO_OFFSET 1
+
+#endif
+
+
+static void
+set_hypercall_interface (grub_uint8_t *tgt, unsigned callno)
+{
+ if (callno == 0x17)
+ {
+ grub_memcpy (tgt, template_iret, ARRAY_SIZE (template_iret));
+ grub_memset (tgt + ARRAY_SIZE (template_iret), 0xcc,
+ HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template_iret));
+ return;
+ }
+ grub_memcpy (tgt, template, ARRAY_SIZE (template));
+ grub_memset (tgt + ARRAY_SIZE (template), 0xcc,
+ HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template));
+ tgt[CALLNO_OFFSET] = callno & 0xff;
+ tgt[CALLNO_OFFSET + 1] = callno >> 8;
+}
+
+#ifdef __x86_64__
+#define grub_elfXX_load grub_elf64_load
+#else
+#define grub_elfXX_load grub_elf32_load
+#endif
+
+static grub_err_t
+grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file;
+ grub_elf_t elf;
+ grub_err_t err;
+ void *kern_chunk_src;
+ grub_relocator_chunk_t ch;
+ grub_addr_t kern_start;
+ grub_addr_t kern_end;
+ grub_size_t sz;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ /* Call grub_loader_unset early to avoid it being called by grub_loader_set */
+ grub_loader_unset ();
+
+ grub_xen_reset ();
+
+ err = grub_create_loader_cmdline (argc - 1, argv + 1,
+ (char *) xen_state.next_start.cmd_line,
+ sizeof (xen_state.next_start.cmd_line) - 1,
+ GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ return err;
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
+ if (!file)
+ return grub_errno;
+
+ elf = grub_xen_file (file);
+ if (!elf)
+ goto fail;
+
+ err = grub_xen_get_info (elf, &xen_state.xen_inf);
+ if (err)
+ goto fail;
+#ifdef __x86_64__
+ if (xen_state.xen_inf.arch != GRUB_XEN_FILE_X86_64)
+#else
+ if (xen_state.xen_inf.arch != GRUB_XEN_FILE_I386_PAE
+ && xen_state.xen_inf.arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
+#endif
+ {
+ grub_error (GRUB_ERR_BAD_OS, "incompatible architecture: %d",
+ xen_state.xen_inf.arch);
+ goto fail;
+ }
+
+ if (xen_state.xen_inf.virt_base & (PAGE_SIZE - 1))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "unaligned virt_base");
+ goto fail;
+ }
+ grub_dprintf ("xen", "virt_base = %llx, entry = %llx\n",
+ (unsigned long long) xen_state.xen_inf.virt_base,
+ (unsigned long long) xen_state.xen_inf.entry_point);
+
+ xen_state.relocator = grub_relocator_new ();
+ if (!xen_state.relocator)
+ goto fail;
+
+ kern_start = xen_state.xen_inf.kern_start - xen_state.xen_inf.paddr_offset;
+ kern_end = xen_state.xen_inf.kern_end - xen_state.xen_inf.paddr_offset;
+
+ if (xen_state.xen_inf.has_hypercall_page)
+ {
+ grub_dprintf ("xen", "hypercall page at 0x%llx\n",
+ (unsigned long long) xen_state.xen_inf.hypercall_page);
+ kern_start = grub_min (kern_start, xen_state.xen_inf.hypercall_page -
+ xen_state.xen_inf.virt_base);
+ kern_end = grub_max (kern_end, xen_state.xen_inf.hypercall_page -
+ xen_state.xen_inf.virt_base + PAGE_SIZE);
+ }
+
+ xen_state.max_addr = ALIGN_UP (kern_end, PAGE_SIZE);
+
+
+ if (grub_sub (kern_end, kern_start, &sz))
+ {
+ err = GRUB_ERR_OUT_OF_RANGE;
+ goto fail;
+ }
+
+ err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, kern_start, sz);
+ if (err)
+ goto fail;
+ kern_chunk_src = get_virtual_current_address (ch);
+
+ grub_dprintf ("xen", "paddr_offset = 0x%llx\n",
+ (unsigned long long) xen_state.xen_inf.paddr_offset);
+ grub_dprintf ("xen", "kern_start = 0x%llx, kern_end = 0x%llx\n",
+ (unsigned long long) xen_state.xen_inf.kern_start,
+ (unsigned long long) xen_state.xen_inf.kern_end);
+
+ err = grub_elfXX_load (elf, argv[0],
+ (grub_uint8_t *) kern_chunk_src - kern_start
+ - xen_state.xen_inf.paddr_offset, 0, 0, 0);
+
+ if (xen_state.xen_inf.has_hypercall_page)
+ {
+ unsigned i;
+ for (i = 0; i < PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++)
+ set_hypercall_interface ((grub_uint8_t *) kern_chunk_src +
+ i * HYPERCALL_INTERFACE_SIZE +
+ xen_state.xen_inf.hypercall_page -
+ xen_state.xen_inf.virt_base - kern_start, i);
+ }
+
+ if (err)
+ goto fail;
+
+ grub_dl_ref (my_mod);
+ xen_state.loaded = 1;
+
+ grub_loader_set (grub_xen_boot, grub_xen_unload, 0);
+
+ goto fail;
+
+fail:
+ /* grub_errno might be clobbered by further calls, save the error reason. */
+ err = grub_errno;
+
+ if (elf)
+ grub_elf_close (elf);
+ else if (file)
+ grub_file_close (file);
+
+ if (err != GRUB_ERR_NONE)
+ grub_xen_reset ();
+
+ return err;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_size_t size = 0;
+ grub_err_t err;
+ struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
+ grub_relocator_chunk_t ch;
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto fail;
+ }
+
+ if (!xen_state.loaded)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("you need to load the kernel first"));
+ goto fail;
+ }
+
+ if (xen_state.next_start.mod_start || xen_state.next_start.mod_len)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded"));
+ goto fail;
+ }
+
+ if (xen_state.xen_inf.unmapped_initrd)
+ {
+ err = grub_xen_alloc_boot_data ();
+ if (err)
+ goto fail;
+ }
+
+ if (grub_initrd_init (argc, argv, &initrd_ctx))
+ goto fail;
+
+ size = grub_get_initrd_size (&initrd_ctx);
+
+ if (size)
+ {
+ err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
+ xen_state.max_addr, size);
+ if (err)
+ goto fail;
+
+ if (grub_initrd_load (&initrd_ctx, argv,
+ get_virtual_current_address (ch)))
+ goto fail;
+ }
+
+ xen_state.next_start.mod_len = size;
+
+ if (xen_state.xen_inf.unmapped_initrd)
+ {
+ xen_state.next_start.flags |= SIF_MOD_START_PFN;
+ xen_state.next_start.mod_start = xen_state.max_addr >> PAGE_SHIFT;
+ }
+ else
+ xen_state.next_start.mod_start =
+ xen_state.max_addr + xen_state.xen_inf.virt_base;
+
+ grub_dprintf ("xen", "Initrd, addr=0x%x, size=0x%x\n",
+ (unsigned) (xen_state.max_addr + xen_state.xen_inf.virt_base),
+ (unsigned) size);
+
+ xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE);
+
+fail:
+ grub_initrd_close (&initrd_ctx);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_size_t size = 0;
+ grub_err_t err;
+ grub_relocator_chunk_t ch;
+ grub_size_t cmdline_len;
+ int nounzip = 0;
+ grub_file_t file;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ if (grub_strcmp (argv[0], "--nounzip") == 0)
+ {
+ argv++;
+ argc--;
+ nounzip = 1;
+ }
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ if (!xen_state.loaded)
+ {
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("you need to load the kernel first"));
+ }
+
+ if ((xen_state.next_start.mod_start || xen_state.next_start.mod_len) &&
+ !xen_state.module_info_page)
+ {
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded"));
+ }
+
+ /* Leave one space for terminator. */
+ if (xen_state.n_modules >= MAX_MODULES - 1)
+ {
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many modules");
+ }
+
+ if (!xen_state.module_info_page)
+ {
+ xen_state.xen_inf.unmapped_initrd = 0;
+ xen_state.n_modules = 0;
+ xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE);
+ xen_state.modules_target_start = xen_state.max_addr;
+ xen_state.next_start.mod_start =
+ xen_state.max_addr + xen_state.xen_inf.virt_base;
+ xen_state.next_start.flags |= SIF_MULTIBOOT_MOD;
+
+ err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
+ xen_state.max_addr, MAX_MODULES
+ *
+ sizeof (xen_state.module_info_page
+ [0]));
+ if (err)
+ return err;
+ xen_state.module_info_page = get_virtual_current_address (ch);
+ grub_memset (xen_state.module_info_page, 0, MAX_MODULES
+ * sizeof (xen_state.module_info_page[0]));
+ xen_state.max_addr +=
+ MAX_MODULES * sizeof (xen_state.module_info_page[0]);
+ }
+
+ xen_state.max_addr = ALIGN_UP (xen_state.max_addr, PAGE_SIZE);
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD |
+ (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE));
+ if (!file)
+ return grub_errno;
+ size = grub_file_size (file);
+
+ cmdline_len = grub_loader_cmdline_size (argc - 1, argv + 1);
+
+ err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
+ xen_state.max_addr, cmdline_len);
+ if (err)
+ goto fail;
+
+ err = grub_create_loader_cmdline (argc - 1, argv + 1,
+ get_virtual_current_address (ch), cmdline_len,
+ GRUB_VERIFY_MODULE_CMDLINE);
+ if (err)
+ goto fail;
+
+ xen_state.module_info_page[xen_state.n_modules].cmdline =
+ xen_state.max_addr - xen_state.modules_target_start;
+ xen_state.max_addr = ALIGN_UP (xen_state.max_addr + cmdline_len, PAGE_SIZE);
+
+ if (size)
+ {
+ err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch,
+ xen_state.max_addr, size);
+ if (err)
+ goto fail;
+ if (grub_file_read (file, get_virtual_current_address (ch), size)
+ != (grub_ssize_t) size)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR,
+ N_("premature end of file %s"), argv[0]);
+ goto fail;
+ }
+ }
+ xen_state.next_start.mod_len =
+ xen_state.max_addr + size - xen_state.modules_target_start;
+ xen_state.module_info_page[xen_state.n_modules].mod_start =
+ xen_state.max_addr - xen_state.modules_target_start;
+ xen_state.module_info_page[xen_state.n_modules].mod_end =
+ xen_state.max_addr + size - xen_state.modules_target_start;
+
+ xen_state.n_modules++;
+ grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n",
+ (unsigned) xen_state.max_addr, (unsigned) size);
+ xen_state.max_addr = ALIGN_UP (xen_state.max_addr + size, PAGE_SIZE);
+
+
+fail:
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_xen, cmd_initrd, cmd_module, cmd_multiboot;
+
+GRUB_MOD_INIT (xen)
+{
+ cmd_xen = grub_register_command ("linux", grub_cmd_xen,
+ 0, N_("Load Linux."));
+ cmd_multiboot = grub_register_command ("multiboot", grub_cmd_xen,
+ 0, N_("Load Linux."));
+ cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
+ 0, N_("Load initrd."));
+ cmd_module = grub_register_command ("module", grub_cmd_module,
+ 0, N_("Load module."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI (xen)
+{
+ grub_unregister_command (cmd_xen);
+ grub_unregister_command (cmd_initrd);
+ grub_unregister_command (cmd_multiboot);
+ grub_unregister_command (cmd_module);
+}
diff --git a/grub-core/loader/i386/xen_file.c b/grub-core/loader/i386/xen_file.c
new file mode 100644
index 0000000..9af5d66
--- /dev/null
+++ b/grub-core/loader/i386/xen_file.c
@@ -0,0 +1,117 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/xen_file.h>
+#include <grub/i386/linux.h>
+#include <grub/misc.h>
+
+#define XZ_MAGIC "\3757zXZ\0"
+
+grub_elf_t
+grub_xen_file (grub_file_t file)
+{
+ grub_elf_t elf;
+ struct linux_i386_kernel_header lh;
+ grub_file_t off_file;
+ grub_uint32_t payload_offset, payload_length;
+ grub_uint8_t magic[6];
+
+ elf = grub_elf_file (file, file->name);
+ if (elf)
+ return elf;
+ grub_errno = GRUB_ERR_NONE;
+
+ if (grub_file_seek (file, 0) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
+ goto fail;
+
+ if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)
+ || lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE)
+ || grub_le_to_cpu16 (lh.version) < 0x0208)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "version too old for xen boot");
+ return NULL;
+ }
+
+ payload_length = lh.payload_length;
+ payload_offset = (lh.setup_sects + 1) * 512
+ + lh.payload_offset;
+
+ if (payload_length < sizeof (magic))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "payload too short");
+ return NULL;
+ }
+
+ grub_dprintf ("xen", "found bzimage payload 0x%llx-0x%llx\n",
+ (unsigned long long) payload_offset,
+ (unsigned long long) lh.payload_length);
+
+ grub_file_seek (file, payload_offset);
+
+ if (grub_file_read (file, &magic, sizeof (magic)) != sizeof (magic))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ file->name);
+ goto fail;
+ }
+
+ /* Kernel suffixes xz payload with their uncompressed size.
+ Trim it. */
+ if (grub_memcmp (magic, XZ_MAGIC, sizeof (XZ_MAGIC) - 1) == 0)
+ payload_length -= 4;
+ off_file = grub_file_offset_open (file, GRUB_FILE_TYPE_LINUX_KERNEL, payload_offset,
+ payload_length);
+ if (!off_file)
+ goto fail;
+
+ elf = grub_elf_file (off_file, file->name);
+ if (elf)
+ return elf;
+ grub_file_offset_close (off_file);
+
+fail:
+ grub_error (GRUB_ERR_BAD_OS, "not xen image");
+ return NULL;
+}
+
+grub_err_t
+grub_xen_get_info (grub_elf_t elf, struct grub_xen_file_info * xi)
+{
+ grub_memset (xi, 0, sizeof (*xi));
+
+ if (grub_elf_is_elf64 (elf)
+ && elf->ehdr.ehdr64.e_machine
+ == grub_cpu_to_le16_compile_time (EM_X86_64)
+ && elf->ehdr.ehdr64.e_ident[EI_DATA] == ELFDATA2LSB)
+ {
+ xi->arch = GRUB_XEN_FILE_X86_64;
+ return grub_xen_get_info64 (elf, xi);
+ }
+ if (grub_elf_is_elf32 (elf)
+ && elf->ehdr.ehdr32.e_machine == grub_cpu_to_le16_compile_time (EM_386)
+ && elf->ehdr.ehdr32.e_ident[EI_DATA] == ELFDATA2LSB)
+ {
+ xi->arch = GRUB_XEN_FILE_I386;
+ return grub_xen_get_info32 (elf, xi);
+ }
+ return grub_error (GRUB_ERR_BAD_OS, "unknown ELF type");
+}
diff --git a/grub-core/loader/i386/xen_file32.c b/grub-core/loader/i386/xen_file32.c
new file mode 100644
index 0000000..340d445
--- /dev/null
+++ b/grub-core/loader/i386/xen_file32.c
@@ -0,0 +1,7 @@
+#define GRUB_TARGET_WORDSIZE 32
+#define XX 32
+#define grub_le_to_cpu_addr grub_le_to_cpu32
+#define ehdrXX ehdr32
+#define grub_xen_get_infoXX grub_xen_get_info32
+#define FOR_ELF_PHDRS FOR_ELF32_PHDRS
+#include "xen_fileXX.c"
diff --git a/grub-core/loader/i386/xen_file64.c b/grub-core/loader/i386/xen_file64.c
new file mode 100644
index 0000000..c410493
--- /dev/null
+++ b/grub-core/loader/i386/xen_file64.c
@@ -0,0 +1,7 @@
+#define GRUB_TARGET_WORDSIZE 64
+#define XX 64
+#define grub_le_to_cpu_addr grub_le_to_cpu64
+#define ehdrXX ehdr64
+#define grub_xen_get_infoXX grub_xen_get_info64
+#define FOR_ELF_PHDRS FOR_ELF64_PHDRS
+#include "xen_fileXX.c"
diff --git a/grub-core/loader/i386/xen_fileXX.c b/grub-core/loader/i386/xen_fileXX.c
new file mode 100644
index 0000000..27afcaa
--- /dev/null
+++ b/grub-core/loader/i386/xen_fileXX.c
@@ -0,0 +1,395 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013 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/xen_file.h>
+#include <grub/misc.h>
+#include <xen/elfnote.h>
+
+static grub_err_t
+parse_xen_guest (grub_elf_t elf, struct grub_xen_file_info *xi,
+ grub_off_t off, grub_size_t sz)
+{
+ char *buf;
+ const char *ptr;
+ int has_paddr = 0;
+
+ grub_errno = GRUB_ERR_NONE;
+ if (grub_file_seek (elf->file, off) == (grub_off_t) -1)
+ return grub_errno;
+ buf = grub_malloc (sz);
+ if (!buf)
+ return grub_errno;
+
+ if (grub_file_read (elf->file, buf, sz) != (grub_ssize_t) sz)
+ {
+ if (grub_errno)
+ goto out;
+ grub_free (buf);
+ return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ elf->file->name);
+ }
+ xi->has_xen_guest = 1;
+ for (ptr = buf; ptr && ptr - buf < (grub_ssize_t) sz;
+ ptr = grub_strchr (ptr, ','), (ptr ? ptr++ : 0))
+ {
+ if (grub_strncmp (ptr, "PAE=no,", sizeof ("PAE=no,") - 1) == 0)
+ {
+ if (xi->arch != GRUB_XEN_FILE_I386
+ && xi->arch != GRUB_XEN_FILE_I386_PAE
+ && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
+ continue;
+ xi->arch = GRUB_XEN_FILE_I386;
+ continue;
+ }
+
+ if (grub_strncmp (ptr, "PAE=yes,", sizeof ("PAE=yes,") - 1) == 0)
+ {
+ if (xi->arch != GRUB_XEN_FILE_I386
+ && xi->arch != GRUB_XEN_FILE_I386_PAE
+ && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
+ continue;
+ xi->arch = GRUB_XEN_FILE_I386_PAE;
+ continue;
+ }
+
+ if (grub_strncmp (ptr, "PAE=yes[extended-cr3],",
+ sizeof ("PAE=yes[extended-cr3],") - 1) == 0)
+ {
+ if (xi->arch != GRUB_XEN_FILE_I386
+ && xi->arch != GRUB_XEN_FILE_I386_PAE
+ && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
+ continue;
+ xi->arch = GRUB_XEN_FILE_I386_PAE;
+ xi->extended_cr3 = 1;
+ continue;
+ }
+
+ if (grub_strncmp (ptr, "PAE=bimodal,", sizeof ("PAE=bimodal,") - 1) == 0)
+ {
+ if (xi->arch != GRUB_XEN_FILE_I386
+ && xi->arch != GRUB_XEN_FILE_I386_PAE
+ && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
+ continue;
+ xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE;
+ continue;
+ }
+
+ if (grub_strncmp (ptr, "PAE=bimodal[extended-cr3],",
+ sizeof ("PAE=bimodal[extended-cr3],") - 1) == 0)
+ {
+ if (xi->arch != GRUB_XEN_FILE_I386
+ && xi->arch != GRUB_XEN_FILE_I386_PAE
+ && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
+ continue;
+ xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE;
+ xi->extended_cr3 = 1;
+ continue;
+ }
+
+ if (grub_strncmp (ptr, "PAE=yes,bimodal,", sizeof ("PAE=yes,bimodal,") - 1) == 0)
+ {
+ if (xi->arch != GRUB_XEN_FILE_I386
+ && xi->arch != GRUB_XEN_FILE_I386_PAE
+ && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
+ continue;
+ xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE;
+ continue;
+ }
+
+ if (grub_strncmp (ptr, "PAE=yes[extended-cr3],bimodal,",
+ sizeof ("PAE=yes[extended-cr3],bimodal,") - 1) == 0)
+ {
+ if (xi->arch != GRUB_XEN_FILE_I386
+ && xi->arch != GRUB_XEN_FILE_I386_PAE
+ && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
+ continue;
+ xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE;
+ xi->extended_cr3 = 1;
+ continue;
+ }
+
+ if (grub_strncmp (ptr, "VIRT_BASE=", sizeof ("VIRT_BASE=") - 1) == 0)
+ {
+ xi->virt_base = grub_strtoull (ptr + sizeof ("VIRT_BASE=") - 1, &ptr, 16);
+ if (grub_errno)
+ goto out;
+ continue;
+ }
+ if (grub_strncmp (ptr, "VIRT_ENTRY=", sizeof ("VIRT_ENTRY=") - 1) == 0)
+ {
+ xi->entry_point = grub_strtoull (ptr + sizeof ("VIRT_ENTRY=") - 1, &ptr, 16);
+ if (grub_errno)
+ goto out;
+ continue;
+ }
+ if (grub_strncmp (ptr, "HYPERCALL_PAGE=", sizeof ("HYPERCALL_PAGE=") - 1) == 0)
+ {
+ xi->hypercall_page = grub_strtoull (ptr + sizeof ("HYPERCALL_PAGE=") - 1, &ptr, 16);
+ xi->has_hypercall_page = 1;
+ if (grub_errno)
+ goto out;
+ continue;
+ }
+ if (grub_strncmp (ptr, "ELF_PADDR_OFFSET=", sizeof ("ELF_PADDR_OFFSET=") - 1) == 0)
+ {
+ xi->paddr_offset = grub_strtoull (ptr + sizeof ("ELF_PADDR_OFFSET=") - 1, &ptr, 16);
+ has_paddr = 1;
+ if (grub_errno)
+ goto out;
+ continue;
+ }
+ }
+ if (xi->has_hypercall_page)
+ xi->hypercall_page = (xi->hypercall_page << 12) + xi->virt_base;
+ if (!has_paddr)
+ xi->paddr_offset = xi->virt_base;
+
+out:
+ grub_free (buf);
+
+ return grub_errno;
+}
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+static grub_err_t
+parse_note (grub_elf_t elf, struct grub_xen_file_info *xi,
+ grub_off_t off, grub_size_t sz)
+{
+ grub_uint32_t *buf;
+ grub_uint32_t *ptr;
+ if (grub_file_seek (elf->file, off) == (grub_off_t) -1)
+ return grub_errno;
+ buf = grub_malloc (sz);
+ if (!buf)
+ return grub_errno;
+
+ if (grub_file_read (elf->file, buf, sz) != (grub_ssize_t) sz)
+ {
+ if (grub_errno)
+ return grub_errno;
+ return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ elf->file->name);
+ }
+ for (ptr = buf; ptr - buf < (grub_ssize_t) (sz / sizeof (grub_uint32_t));)
+ {
+ Elf_Nhdr *nh = (Elf_Nhdr *) ptr;
+ char *name;
+ grub_uint32_t *desc;
+ grub_uint32_t namesz, descsz;
+ ptr += sizeof (*nh) / sizeof (grub_uint32_t);
+ name = (char *) ptr;
+ namesz = grub_le_to_cpu32 (nh->n_namesz);
+ descsz = grub_le_to_cpu32 (nh->n_descsz);
+ ptr += (namesz + 3) / 4;
+ desc = ptr;
+ ptr += (grub_le_to_cpu32 (nh->n_descsz) + 3) / 4;
+ if ((namesz < 3) || grub_memcmp (name, "Xen", namesz == 3 ? 3 : 4) != 0)
+ continue;
+ xi->has_note = 1;
+ switch (nh->n_type)
+ {
+ case XEN_ELFNOTE_ENTRY:
+ xi->entry_point = grub_le_to_cpu_addr (*(Elf_Addr *) desc);
+ break;
+ case XEN_ELFNOTE_HYPERCALL_PAGE:
+ xi->hypercall_page = grub_le_to_cpu_addr (*(Elf_Addr *) desc);
+ xi->has_hypercall_page = 1;
+ break;
+ case XEN_ELFNOTE_VIRT_BASE:
+ xi->virt_base = grub_le_to_cpu_addr (*(Elf_Addr *) desc);
+ break;
+ case XEN_ELFNOTE_PADDR_OFFSET:
+ xi->paddr_offset = grub_le_to_cpu_addr (*(Elf_Addr *) desc);
+ break;
+ case XEN_ELFNOTE_XEN_VERSION:
+ grub_dprintf ("xen", "xenversion = `%s'\n", (char *) desc);
+ break;
+ case XEN_ELFNOTE_GUEST_OS:
+ grub_dprintf ("xen", "name = `%s'\n", (char *) desc);
+ break;
+ case XEN_ELFNOTE_GUEST_VERSION:
+ grub_dprintf ("xen", "version = `%s'\n", (char *) desc);
+ break;
+ case XEN_ELFNOTE_LOADER:
+ if (descsz < 7
+ || grub_memcmp (desc, "generic", descsz == 7 ? 7 : 8) != 0)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid loader");
+ break;
+ /* PAE */
+ case XEN_ELFNOTE_PAE_MODE:
+ grub_dprintf ("xen", "pae = `%s', %d, %d\n", (char *) desc,
+ xi->arch, descsz);
+ if (xi->arch != GRUB_XEN_FILE_I386
+ && xi->arch != GRUB_XEN_FILE_I386_PAE
+ && xi->arch != GRUB_XEN_FILE_I386_PAE_BIMODE)
+ break;
+ if (descsz >= 3 && grub_memcmp (desc, "yes",
+ descsz == 3 ? 3 : 4) == 0)
+ {
+ xi->extended_cr3 = 1;
+ xi->arch = GRUB_XEN_FILE_I386_PAE;
+ }
+ if (descsz >= 7 && grub_memcmp (desc, "bimodal",
+ descsz == 7 ? 7 : 8) == 0)
+ {
+ xi->extended_cr3 = 1;
+ xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE;
+ }
+ if (descsz >= 11 && grub_memcmp (desc, "yes,bimodal",
+ descsz == 11 ? 11 : 12) == 0)
+ {
+ xi->extended_cr3 = 1;
+ xi->arch = GRUB_XEN_FILE_I386_PAE_BIMODE;
+ }
+ if (descsz >= 2 && grub_memcmp (desc, "no",
+ descsz == 2 ? 2 : 3) == 0)
+ xi->arch = GRUB_XEN_FILE_I386;
+ break;
+ case XEN_ELFNOTE_INIT_P2M:
+ xi->p2m_base = grub_le_to_cpu_addr (*(Elf_Addr *) desc);
+ xi->has_p2m_base = 1;
+ break;
+ case XEN_ELFNOTE_MOD_START_PFN:
+ xi->unmapped_initrd = !!grub_le_to_cpu32(*(grub_uint32_t *) desc);
+ break;
+ default:
+ grub_dprintf ("xen", "unknown note type %d\n", nh->n_type);
+ break;
+ }
+ }
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xen_get_infoXX (grub_elf_t elf, struct grub_xen_file_info *xi)
+{
+ Elf_Shdr *s, *s0;
+ grub_size_t shnum = elf->ehdr.ehdrXX.e_shnum;
+ grub_size_t shentsize = elf->ehdr.ehdrXX.e_shentsize;
+ grub_size_t shsize = shnum * shentsize;
+ grub_off_t stroff;
+ grub_err_t err;
+ Elf_Phdr *phdr;
+
+ xi->kern_end = 0;
+ xi->kern_start = ~0;
+ xi->entry_point = elf->ehdr.ehdrXX.e_entry;
+
+ /* FIXME: check note. */
+ FOR_ELF_PHDRS (elf, phdr)
+ {
+ Elf_Addr paddr;
+
+ if (phdr->p_type == PT_NOTE)
+ {
+ err = parse_note (elf, xi, phdr->p_offset, phdr->p_filesz);
+ if (err)
+ return err;
+ }
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ paddr = phdr->p_paddr;
+
+ if (paddr < xi->kern_start)
+ xi->kern_start = paddr;
+
+ if (paddr + phdr->p_memsz > xi->kern_end)
+ xi->kern_end = paddr + phdr->p_memsz;
+ }
+
+ if (xi->has_note)
+ return GRUB_ERR_NONE;
+
+ if (!shnum || !shentsize)
+ return grub_error (GRUB_ERR_BAD_OS, "no XEN note");
+
+ s0 = grub_malloc (shsize);
+ if (!s0)
+ return grub_errno;
+
+ if (grub_file_seek (elf->file, elf->ehdr.ehdrXX.e_shoff) == (grub_off_t) -1)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+
+ if (grub_file_read (elf->file, s0, shsize) != (grub_ssize_t) shsize)
+ {
+ if (grub_errno)
+ err = grub_errno;
+ else
+ err = grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ elf->file->name);
+ goto cleanup;
+ }
+
+ s = (Elf_Shdr *) ((char *) s0 + elf->ehdr.ehdrXX.e_shstrndx * shentsize);
+ stroff = s->sh_offset;
+
+ for (s = s0; s < (Elf_Shdr *) ((char *) s0 + shnum * shentsize);
+ s = (Elf_Shdr *) ((char *) s + shentsize))
+ {
+ if (s->sh_type == SHT_NOTE)
+ {
+ err = parse_note (elf, xi, s->sh_offset, s->sh_size);
+ if (err)
+ goto cleanup;
+ }
+ }
+
+ if (xi->has_note)
+ {
+ err = GRUB_ERR_NONE;
+ goto cleanup;
+ }
+
+ for (s = s0; s < (Elf_Shdr *) ((char *) s0 + shnum * shentsize);
+ s = (Elf_Shdr *) ((char *) s + shentsize))
+ {
+ char name[sizeof("__xen_guest")];
+ grub_memset (name, 0, sizeof (name));
+ if (grub_file_seek (elf->file, stroff + s->sh_name) == (grub_off_t) -1)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+
+ if (grub_file_read (elf->file, name, sizeof (name)) != (grub_ssize_t) sizeof (name))
+ {
+ if (grub_errno)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+ continue;
+ }
+ if (grub_memcmp (name, "__xen_guest",
+ sizeof("__xen_guest")) != 0)
+ continue;
+ err = parse_xen_guest (elf, xi, s->sh_offset, s->sh_size);
+ goto cleanup;
+ }
+ err = grub_error (GRUB_ERR_BAD_OS, "no XEN note found");
+
+cleanup:
+ grub_free (s0);
+ return err;
+}
diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c
new file mode 100644
index 0000000..a700936
--- /dev/null
+++ b/grub-core/loader/i386/xnu.c
@@ -0,0 +1,1166 @@
+/*
+ * 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/i386/cpuid.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>
+#include <grub/cpu/io.h>
+#include <grub/random.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;
+ const 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 grub_xnu_devprop_device_descriptor **prev;
+ 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_uint32_t a, b, d, divisor;
+
+ if (! grub_cpu_is_cpuid_supported ())
+ return sane_value;
+
+ grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]);
+
+ /* Only Intel for now is done. */
+ if (grub_memcmp (manufacturer, "GenuineIntel", 12) != 0)
+ return sane_value;
+
+ /* Check Speedstep. */
+ if (max_cpuid < 1)
+ return sane_value;
+
+ grub_cpuid (1, a, b, capabilities, d);
+
+ if (! (capabilities & (1 << 7)))
+ return sane_value;
+
+ /* Read the multiplier. */
+ asm volatile ("movl $0x198, %%ecx\n"
+ "rdmsr"
+ : "=d" (msrlow)
+ :
+ : "%ecx", "%eax");
+
+ grub_uint64_t v;
+ grub_uint32_t r;
+
+ /* (2000ULL << 32) / grub_tsc_rate */
+ /* Assumption: TSC frequency is over 2 MHz. */
+ v = 0xffffffff / grub_tsc_rate;
+ v *= 2000;
+ /* v is at most 2000 off from (2000ULL << 32) / grub_tsc_rate.
+ Since grub_tsc_rate < 2^32/2^11=2^21, so no overflow.
+ */
+ r = (2000ULL << 32) - v * grub_tsc_rate;
+ v += r / grub_tsc_rate;
+
+ divisor = ((msrlow >> 7) & 0x3e) | ((msrlow >> 14) & 1);
+ if (divisor == 0)
+ return sane_value;
+ return grub_divmod64 (v, divisor, 0);
+}
+
+struct property_descriptor
+{
+ struct property_descriptor *next;
+ struct property_descriptor **prev;
+ 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 (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 (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 (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->data = grub_malloc (datalen);
+ if (!prop->data)
+ {
+ grub_free (prop);
+ return grub_errno;
+ }
+ grub_memcpy (prop->data, data, datalen);
+
+ prop->name = utf8;
+ prop->name16 = utf16;
+ prop->name16len = utf16len;
+ prop->length = datalen;
+
+ 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_calloc (len, sizeof (grub_uint16_t));
+ 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_calloc (namelen, sizeof (grub_uint16_t));
+ if (!utf16)
+ return grub_errno;
+ grub_memcpy (utf16, name, sizeof (grub_uint16_t) * namelen);
+
+ utf8 = grub_malloc (namelen * 4 + 1);
+ if (!utf8)
+ {
+ grub_free (utf16);
+ 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;
+}
+
+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)
+ return grub_errno;
+
+ 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, N_("filename expected"));
+
+ file = grub_file_open (args[0], GRUB_FILE_XNU_DEVPROP);
+ if (! file)
+ return grub_errno;
+ 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;
+ dp = dpstart = buf;
+
+ while (GRUB_EFI_DEVICE_PATH_VALID (dp) && buf < bufend)
+ {
+ buf = (char *) buf + GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
+ break;
+ dp = buf;
+ }
+
+ 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. */
+static grub_err_t
+grub_cpu_xnu_fill_devicetree (grub_uint64_t *fsbfreq_out)
+{
+ struct grub_xnu_devtree_key *efikey;
+ struct grub_xnu_devtree_key *chosenkey;
+ 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;
+ grub_err_t err;
+
+ chosenkey = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
+ if (! chosenkey)
+ return grub_errno;
+
+ /* Random seed. */
+ curval = grub_xnu_create_value (&(chosenkey->first_child), "random-seed");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = 64;
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ /* Our random is not peer-reviewed but xnu uses this seed only for
+ ASLR in kernel. */
+ err = grub_crypto_get_random (curval->data, curval->datasize);
+ if (err)
+ return err;
+
+ /* 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. */
+ const char *fsbvar = grub_env_get ("fsb");
+ grub_uint64_t fsbfreq = 0;
+ if (fsbvar)
+ fsbfreq = readfrequency (fsbvar);
+ /* Try autodetect. */
+ if (! fsbfreq)
+ fsbfreq = guessfsb ();
+ *((grub_uint64_t *) curval->data) = fsbfreq;
+ *fsbfreq_out = fsbfreq;
+ 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_packed_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 = (void *) (grub_addr_t) ((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 = (void *) (grub_addr_t) ((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) = (grub_addr_t) ptr;
+ else
+ *((grub_uint64_t *) curval->data) = (grub_addr_t) ptr;
+
+ /* Create alias. */
+ for (j = 0; j < ARRAY_SIZE(table_aliases); j++)
+ if (grub_memcmp (&table_aliases[j].guid, &guid, sizeof (guid)) == 0)
+ break;
+ if (j != ARRAY_SIZE(table_aliases))
+ {
+ 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)
+ = (grub_addr_t) SYSTEM_TABLE_PTR (runtime_services);
+ else
+ *((grub_uint64_t *) curval->data)
+ = (grub_addr_t) 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, 0);
+}
+
+/* Setup video for xnu. */
+static grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params_common *params)
+{
+ struct grub_video_mode_info mode_info;
+ 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;
+
+ err = grub_video_get_info (&mode_info);
+ if (err)
+ return err;
+
+ 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;
+ }
+
+ err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+ if (err)
+ return err;
+
+ 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_addr_t) framebuffer;
+ params->lfb_mode = bitmap ? GRUB_XNU_VIDEO_SPLASH
+ : GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
+
+ return GRUB_ERR_NONE;
+}
+
+static int
+total_ram_hook (grub_uint64_t addr __attribute__ ((unused)), grub_uint64_t size,
+ grub_memory_type_t type,
+ void *data)
+{
+ grub_uint64_t *result = data;
+
+ if (type != GRUB_MEMORY_AVAILABLE)
+ return 0;
+ *result += size;
+ return 0;
+}
+
+static grub_uint64_t
+get_total_ram (void)
+{
+ grub_uint64_t result = 0;
+
+ grub_mmap_iterate (total_ram_hook, &result);
+ return result;
+}
+
+/* Boot xnu. */
+grub_err_t
+grub_xnu_boot (void)
+{
+ union grub_xnu_boot_params_any *bootparams;
+ struct grub_xnu_boot_params_common *bootparams_common;
+ 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;
+ grub_uint64_t fsbfreq = 100000000;
+ int v2 = (grub_xnu_darwin_version >= 11);
+ grub_uint32_t efi_system_table = 0;
+
+ err = grub_autoefi_prepare ();
+ if (err)
+ return err;
+
+ err = grub_cpu_xnu_fill_devprop ();
+ if (err)
+ return err;
+
+ err = grub_cpu_xnu_fill_devicetree (&fsbfreq);
+ 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, efi=%p\n", grub_xnu_entry_point,
+ grub_autoefi_system_table);
+
+ const char *debug = grub_env_get ("debug");
+
+ if (debug && (grub_strword (debug, "all") || grub_strword (debug, "xnu")))
+ {
+ grub_puts_ (N_("Press any key to launch xnu"));
+ 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;
+
+ grub_memset (bootparams, 0, sizeof (*bootparams));
+ if (v2)
+ {
+ bootparams_common = &bootparams->v2.common;
+ bootparams->v2.fsbfreq = fsbfreq;
+ bootparams->v2.ram_size = get_total_ram();
+ }
+ else
+ bootparams_common = &bootparams->v1.common;
+
+ /* Set video. */
+ err = grub_xnu_set_video (bootparams_common);
+ if (err != GRUB_ERR_NONE)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ grub_puts_ (N_("Booting in blind mode"));
+
+ bootparams_common->lfb_mode = 0;
+ bootparams_common->lfb_width = 0;
+ bootparams_common->lfb_height = 0;
+ bootparams_common->lfb_depth = 0;
+ bootparams_common->lfb_line_len = 0;
+ bootparams_common->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_common->cmdline, grub_xnu_cmdline,
+ sizeof (bootparams_common->cmdline));
+
+ bootparams_common->devtree = devtree_target;
+ bootparams_common->devtreelen = devtreelen;
+
+ err = grub_autoefi_finish_boot_services (&memory_map_size, memory_map,
+ &map_key, &descriptor_size,
+ &descriptor_version);
+ if (err)
+ return err;
+
+ if (v2)
+ bootparams->v2.efi_system_table = (grub_addr_t) grub_autoefi_system_table;
+ else
+ bootparams->v1.efi_system_table = (grub_addr_t) 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
+ <= (grub_addr_t) grub_autoefi_system_table
+ && curdesc->physical_start + (curdesc->num_pages << 12)
+ > (grub_addr_t) grub_autoefi_system_table)
+ efi_system_table
+ = (grub_addr_t) 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;
+
+ if (v2)
+ {
+ bootparams->v2.efi_uintnbits = SIZEOF_OF_UINTN * 8;
+ bootparams->v2.verminor = GRUB_XNU_BOOTARGSV2_VERMINOR;
+ bootparams->v2.vermajor = GRUB_XNU_BOOTARGSV2_VERMAJOR;
+ bootparams->v2.efi_system_table = efi_system_table;
+ }
+ else
+ {
+ bootparams->v1.efi_uintnbits = SIZEOF_OF_UINTN * 8;
+ bootparams->v1.verminor = GRUB_XNU_BOOTARGSV1_VERMINOR;
+ bootparams->v1.vermajor = GRUB_XNU_BOOTARGSV1_VERMAJOR;
+ bootparams->v1.efi_system_table = efi_system_table;
+ }
+
+ bootparams_common->efi_runtime_first_page = firstruntimepage;
+ bootparams_common->efi_runtime_npages = lastruntimepage - firstruntimepage;
+ bootparams_common->efi_mem_desc_size = descriptor_size;
+ bootparams_common->efi_mem_desc_version = descriptor_version;
+ bootparams_common->efi_mmap = memory_map_target;
+ bootparams_common->efi_mmap_size = memory_map_size;
+ bootparams_common->heap_start = grub_xnu_heap_target_start;
+ bootparams_common->heap_size = curruntimepage * GRUB_XNU_PAGESIZE - grub_xnu_heap_target_start;
+
+ /* Parameters for asm helper. */
+ grub_xnu_stack = bootparams_common->heap_start
+ + bootparams_common->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;
+
+ /* XNU uses only APIC. Disable PIC. */
+ grub_outb (0xff, 0x21);
+ grub_outb (0xff, 0xa1);
+
+ return grub_relocator32_boot (grub_xnu_relocator, state, 0);
+}
+
+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,
+ /* TRANSLATORS: `device-properties'
+ is a variable name,
+ not a program. */
+ 0, N_("Load `device-properties' dump."));
+}
+
+void
+grub_cpu_xnu_fini (void)
+{
+ grub_unregister_command (cmd_devprop_load);
+}