summaryrefslogtreecommitdiffstats
path: root/grub-core/loader
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:54:16 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:54:16 +0000
commit485f6ecd453d8a2fd8b9b9fadea03159d8b50797 (patch)
tree32451fa3cdd9321fb2591fada9891b2cb70a9cd1 /grub-core/loader
parentInitial commit. (diff)
downloadgrub2-upstream.tar.xz
grub2-upstream.zip
Adding upstream version 2.06.upstream/2.06upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--grub-core/loader/aout.c62
-rw-r--r--grub-core/loader/arm/linux.c509
-rw-r--r--grub-core/loader/arm64/linux.c393
-rw-r--r--grub-core/loader/arm64/xen_boot.c527
-rw-r--r--grub-core/loader/efi/appleloader.c242
-rw-r--r--grub-core/loader/efi/chainloader.c444
-rw-r--r--grub-core/loader/efi/fdt.c179
-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
-rw-r--r--grub-core/loader/ia64/efi/linux.c607
-rw-r--r--grub-core/loader/linux.c335
-rw-r--r--grub-core/loader/lzss.c56
-rw-r--r--grub-core/loader/macho.c205
-rw-r--r--grub-core/loader/macho32.c22
-rw-r--r--grub-core/loader/macho64.c22
-rw-r--r--grub-core/loader/machoXX.c384
-rw-r--r--grub-core/loader/mips/linux.c508
-rw-r--r--grub-core/loader/multiboot.c469
-rw-r--r--grub-core/loader/multiboot_elfxx.c298
-rw-r--r--grub-core/loader/multiboot_mbi2.c1128
-rw-r--r--grub-core/loader/powerpc/ieee1275/linux.c393
-rw-r--r--grub-core/loader/riscv/linux.c59
-rw-r--r--grub-core/loader/sparc64/ieee1275/linux.c521
-rw-r--r--grub-core/loader/xnu.c1555
-rw-r--r--grub-core/loader/xnu_resume.c188
44 files changed, 19336 insertions, 0 deletions
diff --git a/grub-core/loader/aout.c b/grub-core/loader/aout.c
new file mode 100644
index 0000000..69bf6e6
--- /dev/null
+++ b/grub-core/loader/aout.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/aout.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+int
+grub_aout_get_type (union grub_aout_header *header)
+{
+ int magic;
+
+ magic = AOUT_GETMAGIC (header->aout32);
+ if ((magic == AOUT32_OMAGIC) || (magic == AOUT32_NMAGIC) ||
+ (magic == AOUT32_ZMAGIC) || (magic == AOUT32_QMAGIC))
+ return AOUT_TYPE_AOUT32;
+ else if ((magic == AOUT64_OMAGIC) || (magic == AOUT64_NMAGIC) ||
+ (magic == AOUT64_ZMAGIC))
+ return AOUT_TYPE_AOUT64;
+ else
+ return AOUT_TYPE_NONE;
+}
+
+grub_err_t
+grub_aout_load (grub_file_t file, int offset,
+ void *load_addr,
+ int load_size, grub_size_t bss_size)
+{
+ if ((grub_file_seek (file, offset)) == (grub_off_t) - 1)
+ return grub_errno;
+
+ if (!load_size)
+ load_size = file->size - offset;
+
+ grub_file_read (file, load_addr, load_size);
+
+ if (grub_errno)
+ return grub_errno;
+
+ if (bss_size)
+ grub_memset ((char *) load_addr + load_size, 0, bss_size);
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/loader/arm/linux.c b/grub-core/loader/arm/linux.c
new file mode 100644
index 0000000..ed23dc7
--- /dev/null
+++ b/grub-core/loader/arm/linux.c
@@ -0,0 +1,509 @@
+/* linux.c - boot Linux */
+/*
+ * 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/dl.h>
+#include <grub/fdt.h>
+#include <grub/file.h>
+#include <grub/loader.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/command.h>
+#include <grub/cache.h>
+#include <grub/cpu/linux.h>
+#include <grub/lib/cmdline.h>
+#include <grub/linux.h>
+#include <grub/verify.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+
+static grub_addr_t initrd_start;
+static grub_addr_t initrd_end;
+
+static grub_addr_t linux_addr;
+static grub_size_t linux_size;
+
+static char *linux_args;
+
+static grub_uint32_t machine_type;
+static const void *current_fdt;
+
+typedef void (*kernel_entry_t) (int, unsigned long, void *);
+
+#define LINUX_PHYS_OFFSET (0x00008000)
+#define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x03000000)
+#define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000)
+
+static grub_size_t
+get_atag_size (const grub_uint32_t *atag)
+{
+ const grub_uint32_t *atag0 = atag;
+ while (atag[0] && atag[1])
+ atag += atag[0];
+ return atag - atag0;
+}
+
+/*
+ * linux_prepare_fdt():
+ * Prepares a loaded FDT for being passed to Linux.
+ * Merges in command line parameters and sets up initrd addresses.
+ */
+static grub_err_t
+linux_prepare_atag (void *target_atag)
+{
+ const grub_uint32_t *atag_orig = (const grub_uint32_t *) current_fdt;
+ grub_uint32_t *tmp_atag, *to;
+ const grub_uint32_t *from;
+ grub_size_t tmp_size;
+ grub_size_t arg_size = grub_strlen (linux_args);
+ char *cmdline_orig = NULL;
+ grub_size_t cmdline_orig_len = 0;
+
+ /* some place for cmdline, initrd and terminator. */
+ tmp_size = get_atag_size (atag_orig) + 20 + (arg_size) / 4;
+ tmp_atag = grub_calloc (tmp_size, sizeof (grub_uint32_t));
+ if (!tmp_atag)
+ return grub_errno;
+
+ for (from = atag_orig, to = tmp_atag; from[0] && from[1];
+ from += from[0])
+ switch (from[1])
+ {
+ case 0x54410004:
+ case 0x54410005:
+ case 0x54420005:
+ break;
+ case 0x54410009:
+ if (*(char *) (from + 2))
+ {
+ cmdline_orig = (char *) (from + 2);
+ cmdline_orig_len = grub_strlen (cmdline_orig) + 1;
+ }
+ break;
+ default:
+ grub_memcpy (to, from, sizeof (grub_uint32_t) * from[0]);
+ to += from[0];
+ break;
+ }
+
+ grub_dprintf ("linux", "linux inherited args: '%s'\n",
+ cmdline_orig ? : "");
+ grub_dprintf ("linux", "linux_args: '%s'\n", linux_args);
+
+ /* Generate and set command line */
+ to[0] = 3 + (arg_size + cmdline_orig_len) / 4;
+ to[1] = 0x54410009;
+ if (cmdline_orig)
+ {
+ grub_memcpy ((char *) to + 8, cmdline_orig, cmdline_orig_len - 1);
+ *((char *) to + 8 + cmdline_orig_len - 1) = ' ';
+ }
+ grub_memcpy ((char *) to + 8 + cmdline_orig_len, linux_args, arg_size);
+ grub_memset ((char *) to + 8 + cmdline_orig_len + arg_size, 0,
+ 4 - ((arg_size + cmdline_orig_len) & 3));
+ to += to[0];
+
+ if (initrd_start && initrd_end)
+ {
+ /*
+ * We're using physical addresses, so even if we have LPAE, we're
+ * restricted to a 32-bit address space.
+ */
+ grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n",
+ initrd_start, initrd_end);
+
+ to[0] = 4;
+ to[1] = 0x54420005;
+ to[2] = initrd_start;
+ to[3] = initrd_end - initrd_start;
+ to += 4;
+ }
+
+ to[0] = 0;
+ to[1] = 0;
+ to += 2;
+
+ /* Copy updated FDT to its launch location */
+ grub_memcpy (target_atag, tmp_atag, sizeof (grub_uint32_t) * (to - tmp_atag));
+ grub_free (tmp_atag);
+
+ grub_dprintf ("loader", "ATAG updated for Linux boot\n");
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * linux_prepare_fdt():
+ * Prepares a loaded FDT for being passed to Linux.
+ * Merges in command line parameters and sets up initrd addresses.
+ */
+static grub_err_t
+linux_prepare_fdt (void *target_fdt)
+{
+ int node;
+ int retval;
+ int tmp_size;
+ void *tmp_fdt;
+
+ tmp_size = grub_fdt_get_totalsize (current_fdt) + 0x100 + grub_strlen (linux_args);
+ tmp_fdt = grub_malloc (tmp_size);
+ if (!tmp_fdt)
+ return grub_errno;
+
+ grub_memcpy (tmp_fdt, current_fdt, grub_fdt_get_totalsize (current_fdt));
+ grub_fdt_set_totalsize (tmp_fdt, tmp_size);
+
+ /* Find or create '/chosen' node */
+ node = grub_fdt_find_subnode (tmp_fdt, 0, "chosen");
+ if (node < 0)
+ {
+ grub_dprintf ("linux", "No 'chosen' node in FDT - creating.\n");
+ node = grub_fdt_add_subnode (tmp_fdt, 0, "chosen");
+ if (node < 0)
+ goto failure;
+ }
+
+ grub_dprintf ("linux", "linux_args: '%s'\n", linux_args);
+
+ /* Generate and set command line */
+ retval = grub_fdt_set_prop (tmp_fdt, node, "bootargs", linux_args,
+ grub_strlen (linux_args) + 1);
+ if (retval)
+ goto failure;
+
+ if (initrd_start && initrd_end)
+ {
+ /*
+ * We're using physical addresses, so even if we have LPAE, we're
+ * restricted to a 32-bit address space.
+ */
+ grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n",
+ initrd_start, initrd_end);
+
+ retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-start",
+ initrd_start);
+ if (retval)
+ goto failure;
+ retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-end",
+ initrd_end);
+ if (retval)
+ goto failure;
+ }
+
+ /* Copy updated FDT to its launch location */
+ grub_memcpy (target_fdt, tmp_fdt, tmp_size);
+ grub_free (tmp_fdt);
+
+ grub_dprintf ("loader", "FDT updated for Linux boot\n");
+
+ return GRUB_ERR_NONE;
+
+failure:
+ grub_free (tmp_fdt);
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "unable to prepare FDT");
+}
+
+static grub_err_t
+linux_boot (void)
+{
+ kernel_entry_t linuxmain;
+ int fdt_valid, atag_valid;
+ void *target_fdt = 0;
+
+ fdt_valid = (current_fdt && grub_fdt_check_header_nosize (current_fdt) == 0);
+ atag_valid = ((((const grub_uint16_t *) current_fdt)[3] & ~3) == 0x5440
+ && *((const grub_uint32_t *) current_fdt));
+ grub_dprintf ("loader", "atag: %p, %x, %x, %s, %s\n",
+ current_fdt,
+ ((const grub_uint16_t *) current_fdt)[3],
+ *((const grub_uint32_t *) current_fdt),
+ (const char *) current_fdt,
+ (const char *) current_fdt + 1);
+
+ if (!fdt_valid && machine_type == GRUB_ARM_MACHINE_TYPE_FDT)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ N_("device tree must be supplied (see `devicetree' command)"));
+
+ grub_arch_sync_caches ((void *) linux_addr, linux_size);
+
+ grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr);
+
+ if (fdt_valid || atag_valid)
+ {
+#ifdef GRUB_MACHINE_EFI
+ grub_size_t size;
+ if (fdt_valid)
+ size = grub_fdt_get_totalsize (current_fdt);
+ else
+ size = 4 * get_atag_size (current_fdt);
+ size += grub_strlen (linux_args) + 256;
+ target_fdt = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size);
+ if (!target_fdt)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+#else
+ target_fdt = (void *) LINUX_FDT_ADDRESS;
+#endif
+ }
+
+ if (fdt_valid)
+ {
+ grub_err_t err;
+
+ err = linux_prepare_fdt (target_fdt);
+ if (err)
+ return err;
+ grub_dprintf ("loader", "FDT @ %p\n", target_fdt);
+ }
+ else if (atag_valid)
+ {
+ grub_err_t err;
+
+ err = linux_prepare_atag (target_fdt);
+ if (err)
+ return err;
+ grub_dprintf ("loader", "ATAG @ %p\n", target_fdt);
+ }
+
+ grub_dprintf ("loader", "Jumping to Linux...\n");
+
+ /* Boot the kernel.
+ * Arguments to kernel:
+ * r0 - 0
+ * r1 - machine type
+ * r2 - address of DTB
+ */
+ linuxmain = (kernel_entry_t) linux_addr;
+
+ grub_arm_disable_caches_mmu ();
+
+ linuxmain (0, machine_type, target_fdt);
+
+ return grub_error (GRUB_ERR_BAD_OS, "Linux call returned");
+}
+
+/*
+ * Only support zImage, so no relocations necessary
+ */
+static grub_err_t
+linux_load (const char *filename, grub_file_t file)
+{
+ struct linux_arm_kernel_header *lh;
+ int size;
+
+ size = grub_file_size (file);
+
+ linux_addr = LINUX_ADDRESS;
+ grub_dprintf ("loader", "Loading Linux to 0x%08x\n",
+ (grub_addr_t) linux_addr);
+
+ if (grub_file_read (file, (void *) linux_addr, size) != size)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ return grub_errno;
+ }
+
+ lh = (void *) linux_addr;
+
+ if ((grub_size_t) size > sizeof (*lh) &&
+ lh->magic == GRUB_LINUX_ARM_MAGIC_SIGNATURE)
+ ;
+ else if (size > 0x8000 && *(grub_uint32_t *) (linux_addr) == 0xea000006
+ && machine_type == GRUB_ARM_MACHINE_TYPE_RASPBERRY_PI)
+ grub_memmove ((void *) linux_addr, (void *) (linux_addr + 0x8000),
+ size - 0x8000);
+ else
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid zImage"));
+
+ linux_size = size;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+linux_unload (void)
+{
+ grub_dl_unref (my_mod);
+
+ grub_free (linux_args);
+ linux_args = NULL;
+
+ initrd_start = initrd_end = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ int size;
+ grub_err_t err;
+ grub_file_t file;
+ grub_dl_ref (my_mod);
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
+ if (!file)
+ goto fail;
+
+ err = linux_load (argv[0], file);
+ grub_file_close (file);
+ if (err)
+ goto fail;
+
+ grub_loader_set (linux_boot, linux_unload, 0);
+
+ size = grub_loader_cmdline_size (argc, argv);
+ linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
+ if (!linux_args)
+ {
+ grub_loader_unset();
+ goto fail;
+ }
+
+ /* Create kernel command line. */
+ grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+ err = grub_create_loader_cmdline (argc, argv,
+ linux_args + sizeof (LINUX_IMAGE) - 1, size,
+ GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ goto fail;
+
+ return GRUB_ERR_NONE;
+
+fail:
+ grub_dl_unref (my_mod);
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file;
+ grub_size_t size = 0;
+ struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_INITRD);
+ if (!file)
+ return grub_errno;
+
+ if (grub_initrd_init (argc, argv, &initrd_ctx))
+ goto fail;
+
+ size = grub_get_initrd_size (&initrd_ctx);
+
+ initrd_start = LINUX_INITRD_ADDRESS;
+
+ grub_dprintf ("loader", "Loading initrd to 0x%08x\n",
+ (grub_addr_t) initrd_start);
+
+ if (grub_initrd_load (&initrd_ctx, argv, (void *) initrd_start))
+ goto fail;
+
+ initrd_end = initrd_start + size;
+
+ return GRUB_ERR_NONE;
+
+fail:
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_err_t
+load_dtb (grub_file_t dtb, int size)
+{
+ void *new_fdt = grub_zalloc (size);
+ if (!new_fdt)
+ return grub_errno;
+ grub_dprintf ("loader", "Loading device tree to %p\n",
+ new_fdt);
+ if ((grub_file_read (dtb, new_fdt, size) != size)
+ || (grub_fdt_check_header (new_fdt, size) != 0))
+ {
+ grub_free (new_fdt);
+ return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree"));
+ }
+
+ grub_fdt_set_totalsize (new_fdt, size);
+ current_fdt = new_fdt;
+ /*
+ * We've successfully loaded an FDT, so any machine type passed
+ * from firmware is now obsolete.
+ */
+ machine_type = GRUB_ARM_MACHINE_TYPE_FDT;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t dtb;
+ int size;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ dtb = grub_file_open (argv[0], GRUB_FILE_TYPE_DEVICE_TREE_IMAGE);
+ if (!dtb)
+ return grub_errno;
+
+ size = grub_file_size (dtb);
+ if (size == 0)
+ grub_error (GRUB_ERR_BAD_OS, "empty file");
+ else
+ load_dtb (dtb, size);
+ grub_file_close (dtb);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_linux, cmd_initrd, cmd_devicetree;
+
+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."));
+ cmd_devicetree = grub_register_command_lockdown ("devicetree", grub_cmd_devicetree,
+ /* TRANSLATORS: DTB stands for device tree blob. */
+ 0, N_("Load DTB file."));
+ my_mod = mod;
+ current_fdt = (const void *) grub_arm_firmware_get_boot_data ();
+ machine_type = grub_arm_firmware_get_machine_type ();
+}
+
+GRUB_MOD_FINI (linux)
+{
+ grub_unregister_command (cmd_linux);
+ grub_unregister_command (cmd_initrd);
+ grub_unregister_command (cmd_devicetree);
+}
diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
new file mode 100644
index 0000000..ef3e9f9
--- /dev/null
+++ b/grub-core/loader/arm64/linux.c
@@ -0,0 +1,393 @@
+/*
+ * 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/charset.h>
+#include <grub/command.h>
+#include <grub/err.h>
+#include <grub/file.h>
+#include <grub/fdt.h>
+#include <grub/linux.h>
+#include <grub/loader.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/cpu/linux.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/fdtload.h>
+#include <grub/efi/memory.h>
+#include <grub/efi/pe32.h>
+#include <grub/i18n.h>
+#include <grub/lib/cmdline.h>
+#include <grub/verify.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+static int loaded;
+
+static void *kernel_addr;
+static grub_uint64_t kernel_size;
+
+static char *linux_args;
+static grub_uint32_t cmdline_size;
+
+static grub_addr_t initrd_start;
+static grub_addr_t initrd_end;
+
+grub_err_t
+grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh)
+{
+ if (lh->magic != GRUB_LINUX_ARMXX_MAGIC_SIGNATURE)
+ return grub_error(GRUB_ERR_BAD_OS, "invalid magic number");
+
+ if ((lh->code0 & 0xffff) != GRUB_PE32_MAGIC)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled"));
+
+ grub_dprintf ("linux", "UEFI stub kernel:\n");
+ grub_dprintf ("linux", "PE/COFF header @ %08x\n", lh->hdr_offset);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+finalize_params_linux (void)
+{
+ int node, retval;
+
+ void *fdt;
+
+ fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE);
+
+ if (!fdt)
+ goto failure;
+
+ node = grub_fdt_find_subnode (fdt, 0, "chosen");
+ if (node < 0)
+ node = grub_fdt_add_subnode (fdt, 0, "chosen");
+
+ if (node < 1)
+ goto failure;
+
+ /* Set initrd info */
+ if (initrd_start && initrd_end > initrd_start)
+ {
+ grub_dprintf ("linux", "Initrd @ %p-%p\n",
+ (void *) initrd_start, (void *) initrd_end);
+
+ retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-start",
+ initrd_start);
+ if (retval)
+ goto failure;
+ retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end",
+ initrd_end);
+ if (retval)
+ goto failure;
+ }
+
+ if (grub_fdt_install() != GRUB_ERR_NONE)
+ goto failure;
+
+ return GRUB_ERR_NONE;
+
+failure:
+ grub_fdt_unload();
+ return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
+}
+
+grub_err_t
+grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
+{
+ grub_efi_memory_mapped_device_path_t *mempath;
+ grub_efi_handle_t image_handle;
+ grub_efi_boot_services_t *b;
+ grub_efi_status_t status;
+ grub_efi_loaded_image_t *loaded_image;
+ int len;
+
+ mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t));
+ if (!mempath)
+ return grub_errno;
+
+ mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE;
+ mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE;
+ mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath));
+ mempath[0].memory_type = GRUB_EFI_LOADER_DATA;
+ mempath[0].start_address = addr;
+ mempath[0].end_address = addr + size;
+
+ mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+ mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ mempath[1].header.length = sizeof (grub_efi_device_path_t);
+
+ b = grub_efi_system_table->boot_services;
+ status = b->load_image (0, grub_efi_image_handle,
+ (grub_efi_device_path_t *) mempath,
+ (void *) addr, size, &image_handle);
+ if (status != GRUB_EFI_SUCCESS)
+ return grub_error (GRUB_ERR_BAD_OS, "cannot load image");
+
+ grub_dprintf ("linux", "linux command line: '%s'\n", args);
+
+ /* Convert command line to UCS-2 */
+ loaded_image = grub_efi_get_loaded_image (image_handle);
+ loaded_image->load_options_size = len =
+ (grub_strlen (args) + 1) * sizeof (grub_efi_char16_t);
+ loaded_image->load_options =
+ grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
+ if (!loaded_image->load_options)
+ return grub_errno;
+
+ loaded_image->load_options_size =
+ 2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
+ (grub_uint8_t *) args, len, NULL);
+
+ grub_dprintf ("linux", "starting image %p\n", image_handle);
+ status = b->start_image (image_handle, 0, NULL);
+
+ /* When successful, not reached */
+ b->unload_image (image_handle);
+ grub_efi_free_pages ((grub_addr_t) loaded_image->load_options,
+ GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ if (finalize_params_linux () != GRUB_ERR_NONE)
+ return grub_errno;
+
+ return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr,
+ kernel_size, linux_args));
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ if (initrd_start)
+ grub_efi_free_pages ((grub_efi_physical_address_t) initrd_start,
+ GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start));
+ initrd_start = initrd_end = 0;
+ grub_free (linux_args);
+ if (kernel_addr)
+ grub_efi_free_pages ((grub_addr_t) kernel_addr,
+ GRUB_EFI_BYTES_TO_PAGES (kernel_size));
+ grub_fdt_unload ();
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * As per linux/Documentation/arm/Booting
+ * ARM initrd needs to be covered by kernel linear mapping,
+ * so place it in the first 512MB of DRAM.
+ *
+ * As per linux/Documentation/arm64/booting.txt
+ * ARM64 initrd needs to be contained entirely within a 1GB aligned window
+ * of up to 32GB of size that covers the kernel image as well.
+ * Since the EFI stub loader will attempt to load the kernel near start of
+ * RAM, place the buffer in the first 32GB of RAM.
+ */
+#ifdef __arm__
+#define INITRD_MAX_ADDRESS_OFFSET (512U * 1024 * 1024)
+#else /* __aarch64__ */
+#define INITRD_MAX_ADDRESS_OFFSET (32ULL * 1024 * 1024 * 1024)
+#endif
+
+/*
+ * This function returns a pointer to a legally allocated initrd buffer,
+ * or NULL if unsuccessful
+ */
+static void *
+allocate_initrd_mem (int initrd_pages)
+{
+ grub_addr_t max_addr;
+
+ if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE)
+ return NULL;
+
+ max_addr += INITRD_MAX_ADDRESS_OFFSET - 1;
+
+ return grub_efi_allocate_pages_real (max_addr, initrd_pages,
+ GRUB_EFI_ALLOCATE_MAX_ADDRESS,
+ GRUB_EFI_LOADER_DATA);
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
+ int initrd_size, initrd_pages;
+ void *initrd_mem = NULL;
+
+ 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;
+
+ initrd_size = grub_get_initrd_size (&initrd_ctx);
+ grub_dprintf ("linux", "Loading initrd\n");
+
+ initrd_pages = (GRUB_EFI_BYTES_TO_PAGES (initrd_size));
+ initrd_mem = allocate_initrd_mem (initrd_pages);
+
+ if (!initrd_mem)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto fail;
+ }
+
+ if (grub_initrd_load (&initrd_ctx, argv, initrd_mem))
+ goto fail;
+
+ initrd_start = (grub_addr_t) initrd_mem;
+ initrd_end = initrd_start + initrd_size;
+ grub_dprintf ("linux", "[addr=%p, size=0x%x]\n",
+ (void *) initrd_start, initrd_size);
+
+ fail:
+ grub_initrd_close (&initrd_ctx);
+ if (initrd_mem && !initrd_start)
+ grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ struct linux_arch_kernel_header lh;
+ 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;
+
+ kernel_size = grub_file_size (file);
+
+ if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh))
+ return grub_errno;
+
+ if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE)
+ goto fail;
+
+ grub_loader_unset();
+
+ grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size);
+ kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size));
+ grub_dprintf ("linux", "kernel numpages: %lld\n",
+ (long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size));
+ if (!kernel_addr)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto fail;
+ }
+
+ grub_file_seek (file, 0);
+ if (grub_file_read (file, kernel_addr, kernel_size)
+ < (grub_int64_t) kernel_size)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
+ goto fail;
+ }
+
+ grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
+
+ cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
+ linux_args = grub_malloc (cmdline_size);
+ if (!linux_args)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto fail;
+ }
+ grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+ err = grub_create_loader_cmdline (argc, argv,
+ linux_args + sizeof (LINUX_IMAGE) - 1,
+ cmdline_size,
+ GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ goto fail;
+
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ grub_loader_set (grub_linux_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;
+ }
+
+ if (linux_args && !loaded)
+ grub_free (linux_args);
+
+ if (kernel_addr && !loaded)
+ grub_efi_free_pages ((grub_addr_t) kernel_addr,
+ GRUB_EFI_BYTES_TO_PAGES (kernel_size));
+
+ 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/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c
new file mode 100644
index 0000000..22cc25e
--- /dev/null
+++ b/grub-core/loader/arm64/xen_boot.c
@@ -0,0 +1,527 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2014 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/cache.h>
+#include <grub/charset.h>
+#include <grub/command.h>
+#include <grub/err.h>
+#include <grub/file.h>
+#include <grub/fdt.h>
+#include <grub/list.h>
+#include <grub/loader.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/cpu/linux.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/fdtload.h>
+#include <grub/efi/memory.h>
+#include <grub/efi/pe32.h> /* required by struct xen_hypervisor_header */
+#include <grub/i18n.h>
+#include <grub/lib/cmdline.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define XEN_HYPERVISOR_NAME "xen_hypervisor"
+#define MODULE_CUSTOM_COMPATIBLE "multiboot,module"
+
+/* This maximum size is defined in Power.org ePAPR V1.1
+ * https://www.power.org/documentation/epapr-version-1-1/
+ * 2.2.1.1 Node Name Requirements
+ * node-name@unit-address
+ * 31 + 1(@) + 16(64bit address in hex format) + 1(\0) = 49
+ */
+#define FDT_NODE_NAME_MAX_SIZE (49)
+
+struct compat_string_struct
+{
+ grub_size_t size;
+ const char *compat_string;
+};
+typedef struct compat_string_struct compat_string_struct_t;
+#define FDT_COMPATIBLE(x) {.size = sizeof(x), .compat_string = (x)}
+
+enum module_type
+{
+ MODULE_IMAGE,
+ MODULE_INITRD,
+ MODULE_XSM,
+ MODULE_CUSTOM
+};
+typedef enum module_type module_type_t;
+
+struct xen_hypervisor_header
+{
+ struct linux_arm64_kernel_header efi_head;
+
+ /* This is always PE\0\0. */
+ grub_uint8_t signature[GRUB_PE32_SIGNATURE_SIZE];
+ /* The COFF file header. */
+ struct grub_pe32_coff_header coff_header;
+ /* The Optional header. */
+ struct grub_pe64_optional_header optional_header;
+};
+
+struct xen_boot_binary
+{
+ struct xen_boot_binary *next;
+ struct xen_boot_binary **prev;
+ int is_hypervisor;
+
+ grub_addr_t start;
+ grub_size_t size;
+ grub_size_t align;
+
+ char *cmdline;
+ int cmdline_size;
+};
+
+static grub_dl_t my_mod;
+
+static int loaded;
+
+static struct xen_boot_binary *xen_hypervisor;
+static struct xen_boot_binary *module_head;
+
+static __inline grub_addr_t
+xen_boot_address_align (grub_addr_t start, grub_size_t align)
+{
+ return (align ? (ALIGN_UP (start, align)) : start);
+}
+
+static grub_err_t
+prepare_xen_hypervisor_params (void *xen_boot_fdt)
+{
+ int chosen_node = 0;
+ int retval;
+
+ chosen_node = grub_fdt_find_subnode (xen_boot_fdt, 0, "chosen");
+ if (chosen_node < 0)
+ chosen_node = grub_fdt_add_subnode (xen_boot_fdt, 0, "chosen");
+ if (chosen_node < 1)
+ return grub_error (GRUB_ERR_IO, "failed to get chosen node in FDT");
+
+ /*
+ * The address and size are always written using 64-bits value. Set
+ * #address-cells and #size-cells accordingly.
+ */
+ retval = grub_fdt_set_prop32 (xen_boot_fdt, chosen_node, "#address-cells", 2);
+ if (retval)
+ return grub_error (GRUB_ERR_IO, "failed to set #address-cells");
+ retval = grub_fdt_set_prop32 (xen_boot_fdt, chosen_node, "#size-cells", 2);
+ if (retval)
+ return grub_error (GRUB_ERR_IO, "failed to set #size-cells");
+
+ grub_dprintf ("xen_loader",
+ "Xen Hypervisor cmdline : %s @ %p size:%d\n",
+ xen_hypervisor->cmdline, xen_hypervisor->cmdline,
+ xen_hypervisor->cmdline_size);
+
+ retval = grub_fdt_set_prop (xen_boot_fdt, chosen_node, "bootargs",
+ xen_hypervisor->cmdline,
+ xen_hypervisor->cmdline_size);
+ if (retval)
+ return grub_error (GRUB_ERR_IO, "failed to install/update FDT");
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+prepare_xen_module_params (struct xen_boot_binary *module, void *xen_boot_fdt)
+{
+ int retval, chosen_node = 0, module_node = 0;
+ char module_name[FDT_NODE_NAME_MAX_SIZE];
+
+ retval = grub_snprintf (module_name, FDT_NODE_NAME_MAX_SIZE, "module@%lx",
+ xen_boot_address_align (module->start,
+ module->align));
+ grub_dprintf ("xen_loader", "Module node name %s \n", module_name);
+
+ if (retval < (int) sizeof ("module@"))
+ return grub_error (GRUB_ERR_IO, N_("failed to get FDT"));
+
+ chosen_node = grub_fdt_find_subnode (xen_boot_fdt, 0, "chosen");
+ if (chosen_node < 0)
+ chosen_node = grub_fdt_add_subnode (xen_boot_fdt, 0, "chosen");
+ if (chosen_node < 1)
+ return grub_error (GRUB_ERR_IO, "failed to get chosen node in FDT");
+
+ module_node =
+ grub_fdt_find_subnode (xen_boot_fdt, chosen_node, module_name);
+ if (module_node < 0)
+ module_node =
+ grub_fdt_add_subnode (xen_boot_fdt, chosen_node, module_name);
+
+ retval = grub_fdt_set_prop (xen_boot_fdt, module_node, "compatible",
+ MODULE_CUSTOM_COMPATIBLE, sizeof(MODULE_CUSTOM_COMPATIBLE));
+ if (retval)
+ return grub_error (GRUB_ERR_IO, "failed to update FDT");
+
+ grub_dprintf ("xen_loader", "Module\n");
+
+ retval = grub_fdt_set_reg64 (xen_boot_fdt, module_node,
+ xen_boot_address_align (module->start,
+ module->align),
+ module->size);
+ if (retval)
+ return grub_error (GRUB_ERR_IO, "failed to update FDT");
+
+ if (module->cmdline && module->cmdline_size > 0)
+ {
+ grub_dprintf ("xen_loader",
+ "Module cmdline : %s @ %p size:%d\n",
+ module->cmdline, module->cmdline, module->cmdline_size);
+
+ retval = grub_fdt_set_prop (xen_boot_fdt, module_node, "bootargs",
+ module->cmdline, module->cmdline_size + 1);
+ if (retval)
+ return grub_error (GRUB_ERR_IO, "failed to update FDT");
+ }
+ else
+ {
+ grub_dprintf ("xen_loader", "Module has no bootargs!\n");
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+finalize_params_xen_boot (void)
+{
+ struct xen_boot_binary *module;
+ void *xen_boot_fdt;
+ grub_size_t additional_size = 0x1000;
+
+ /* Hypervisor. */
+ additional_size += FDT_NODE_NAME_MAX_SIZE + xen_hypervisor->cmdline_size;
+ FOR_LIST_ELEMENTS (module, module_head)
+ {
+ additional_size += 6 * FDT_NODE_NAME_MAX_SIZE + sizeof(MODULE_CUSTOM_COMPATIBLE) - 1
+ + module->cmdline_size;
+ }
+
+ xen_boot_fdt = grub_fdt_load (additional_size);
+ if (!xen_boot_fdt)
+ return grub_error (GRUB_ERR_IO, "failed to get FDT");
+
+ if (xen_hypervisor)
+ {
+ if (prepare_xen_hypervisor_params (xen_boot_fdt) != GRUB_ERR_NONE)
+ goto fail;
+ }
+ else
+ {
+ grub_dprintf ("xen_loader", "Failed to get Xen Hypervisor info!\n");
+ goto fail;
+ }
+
+ /* Set module params info */
+ FOR_LIST_ELEMENTS (module, module_head)
+ {
+ if (module->start && module->size > 0)
+ {
+ grub_dprintf ("xen_loader", "Module @ 0x%lx size:0x%lx\n",
+ xen_boot_address_align (module->start, module->align),
+ module->size);
+ if (prepare_xen_module_params (module, xen_boot_fdt) != GRUB_ERR_NONE)
+ goto fail;
+ }
+ else
+ {
+ grub_dprintf ("xen_loader", "Module info error!\n");
+ goto fail;
+ }
+ }
+
+ if (grub_fdt_install() == GRUB_ERR_NONE)
+ return GRUB_ERR_NONE;
+
+fail:
+ grub_fdt_unload ();
+
+ return grub_error (GRUB_ERR_IO, "failed to install/update FDT");
+}
+
+
+static grub_err_t
+xen_boot (void)
+{
+ grub_err_t err = finalize_params_xen_boot ();
+ if (err)
+ return err;
+
+ return grub_arch_efi_linux_boot_image (xen_hypervisor->start,
+ xen_hypervisor->size,
+ xen_hypervisor->cmdline);
+}
+
+static void
+single_binary_unload (struct xen_boot_binary *binary)
+{
+ if (!binary)
+ return;
+
+ if (binary->start && binary->size > 0)
+ {
+ grub_efi_free_pages ((grub_efi_physical_address_t) binary->start,
+ GRUB_EFI_BYTES_TO_PAGES (binary->size + binary->align));
+ }
+
+ if (binary->cmdline && binary->cmdline_size > 0)
+ {
+ grub_free (binary->cmdline);
+ grub_dprintf ("xen_loader",
+ "Module cmdline memory free @ %p size: %d\n",
+ binary->cmdline, binary->cmdline_size);
+ }
+
+ if (!binary->is_hypervisor)
+ grub_list_remove (GRUB_AS_LIST (binary));
+
+ grub_dprintf ("xen_loader",
+ "Module struct memory free @ %p size: 0x%lx\n",
+ binary, sizeof (binary));
+ grub_free (binary);
+
+ return;
+}
+
+static void
+all_binaries_unload (void)
+{
+ struct xen_boot_binary *binary;
+
+ FOR_LIST_ELEMENTS (binary, module_head)
+ {
+ single_binary_unload (binary);
+ }
+
+ if (xen_hypervisor)
+ single_binary_unload (xen_hypervisor);
+
+ return;
+}
+
+static grub_err_t
+xen_unload (void)
+{
+ loaded = 0;
+ all_binaries_unload ();
+ grub_fdt_unload ();
+ grub_dl_unref (my_mod);
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+xen_boot_binary_load (struct xen_boot_binary *binary, grub_file_t file,
+ int argc, char *argv[])
+{
+ binary->size = grub_file_size (file);
+ grub_dprintf ("xen_loader", "Xen_boot file size: 0x%lx\n", binary->size);
+
+ binary->start
+ = (grub_addr_t) grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES
+ (binary->size +
+ binary->align));
+ if (!binary->start)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ return;
+ }
+
+ grub_dprintf ("xen_loader", "Xen_boot numpages: 0x%lx\n",
+ GRUB_EFI_BYTES_TO_PAGES (binary->size + binary->align));
+
+ if (grub_file_read (file, (void *) xen_boot_address_align (binary->start,
+ binary->align),
+ binary->size) != (grub_ssize_t) binary->size)
+ {
+ single_binary_unload (binary);
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
+ return;
+ }
+
+ if (argc > 1)
+ {
+ binary->cmdline_size = grub_loader_cmdline_size (argc - 1, argv + 1);
+ binary->cmdline = grub_zalloc (binary->cmdline_size);
+ if (!binary->cmdline)
+ {
+ single_binary_unload (binary);
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ return;
+ }
+ grub_create_loader_cmdline (argc - 1, argv + 1, binary->cmdline,
+ binary->cmdline_size,
+ GRUB_VERIFY_KERNEL_CMDLINE);
+ grub_dprintf ("xen_loader",
+ "Xen_boot cmdline @ %p %s, size: %d\n",
+ binary->cmdline, binary->cmdline, binary->cmdline_size);
+ }
+ else
+ {
+ binary->cmdline_size = 0;
+ binary->cmdline = NULL;
+ }
+
+ grub_errno = GRUB_ERR_NONE;
+ return;
+}
+
+static grub_err_t
+grub_cmd_xen_module (grub_command_t cmd __attribute__((unused)),
+ int argc, char *argv[])
+{
+
+ struct xen_boot_binary *module = NULL;
+ grub_file_t file = 0;
+ int nounzip = 0;
+
+ if (!argc)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto fail;
+ }
+
+ if (grub_strcmp (argv[0], "--nounzip") == 0)
+ {
+ argv++;
+ argc--;
+ nounzip = 1;
+ }
+
+ if (!argc)
+ {
+ 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 Xen Hypervisor first"));
+ goto fail;
+ }
+
+ module =
+ (struct xen_boot_binary *) grub_zalloc (sizeof (struct xen_boot_binary));
+ if (!module)
+ return grub_errno;
+
+ module->is_hypervisor = 0;
+ module->align = 4096;
+
+ grub_dprintf ("xen_loader", "Init module and node info\n");
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_XEN_MODULE
+ | (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS
+ : GRUB_FILE_TYPE_NONE));
+ if (!file)
+ goto fail;
+
+ xen_boot_binary_load (module, file, argc, argv);
+ if (grub_errno == GRUB_ERR_NONE)
+ grub_list_push (GRUB_AS_LIST_P (&module_head), GRUB_AS_LIST (module));
+
+ fail:
+ if (file)
+ grub_file_close (file);
+ if (grub_errno != GRUB_ERR_NONE)
+ single_binary_unload (module);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_xen_hypervisor (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ struct xen_hypervisor_header sh;
+ grub_file_t file = NULL;
+
+ grub_dl_ref (my_mod);
+
+ if (!argc)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_XEN_HYPERVISOR);
+ if (!file)
+ goto fail;
+
+ if (grub_file_read (file, &sh, sizeof (sh)) != (long) sizeof (sh))
+ goto fail;
+ if (grub_arch_efi_linux_check_image
+ ((struct linux_arch_kernel_header *) &sh) != GRUB_ERR_NONE)
+ goto fail;
+ grub_file_seek (file, 0);
+
+ /* if another module has called grub_loader_set,
+ we need to make sure that another module is unloaded properly */
+ grub_loader_unset ();
+
+ xen_hypervisor =
+ (struct xen_boot_binary *) grub_zalloc (sizeof (struct xen_boot_binary));
+ if (!xen_hypervisor)
+ return grub_errno;
+
+ xen_hypervisor->is_hypervisor = 1;
+ xen_hypervisor->align = (grub_size_t) sh.optional_header.section_alignment;
+
+ xen_boot_binary_load (xen_hypervisor, file, argc, argv);
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ grub_loader_set (xen_boot, xen_unload, 0);
+ loaded = 1;
+ }
+
+fail:
+ if (file)
+ grub_file_close (file);
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ loaded = 0;
+ all_binaries_unload ();
+ grub_dl_unref (my_mod);
+ }
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_xen_hypervisor;
+static grub_command_t cmd_xen_module;
+
+GRUB_MOD_INIT (xen_boot)
+{
+ cmd_xen_hypervisor =
+ grub_register_command ("xen_hypervisor", grub_cmd_xen_hypervisor, 0,
+ N_("Load a xen hypervisor."));
+ cmd_xen_module =
+ grub_register_command ("xen_module", grub_cmd_xen_module, 0,
+ N_("Load a xen module."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI (xen_boot)
+{
+ grub_unregister_command (cmd_xen_hypervisor);
+ grub_unregister_command (cmd_xen_module);
+}
diff --git a/grub-core/loader/efi/appleloader.c b/grub-core/loader/efi/appleloader.c
new file mode 100644
index 0000000..74888c4
--- /dev/null
+++ b/grub-core/loader/efi/appleloader.c
@@ -0,0 +1,242 @@
+/* appleloader.c - apple legacy boot loader. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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/err.h>
+#include <grub/mm.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+
+static grub_efi_handle_t image_handle;
+static grub_efi_char16_t *cmdline;
+
+static grub_err_t
+grub_appleloader_unload (void)
+{
+ grub_efi_boot_services_t *b;
+
+ b = grub_efi_system_table->boot_services;
+ efi_call_1 (b->unload_image, image_handle);
+
+ grub_free (cmdline);
+ cmdline = 0;
+
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_appleloader_boot (void)
+{
+ grub_efi_boot_services_t *b;
+
+ b = grub_efi_system_table->boot_services;
+ efi_call_3 (b->start_image, image_handle, 0, 0);
+
+ grub_appleloader_unload ();
+
+ return grub_errno;
+}
+
+struct piwg_full_device_path
+{
+ struct grub_efi_memory_mapped_device_path comp1;
+ struct grub_efi_piwg_device_path comp2;
+ struct grub_efi_device_path end;
+};
+
+#define MAKE_PIWG_PATH(st, en) \
+ { \
+ .comp1 = \
+ { \
+ .header = { \
+ .type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE, \
+ .subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE, \
+ .length = sizeof (struct grub_efi_memory_mapped_device_path) \
+ }, \
+ .memory_type = GRUB_EFI_MEMORY_MAPPED_IO, \
+ .start_address = st, \
+ .end_address = en \
+ }, \
+ .comp2 = \
+ { \
+ .header = { \
+ .type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE, \
+ .subtype = GRUB_EFI_PIWG_DEVICE_PATH_SUBTYPE, \
+ .length = sizeof (struct grub_efi_piwg_device_path) \
+ }, \
+ .guid = GRUB_EFI_VENDOR_APPLE_GUID \
+ }, \
+ .end = \
+ { \
+ .type = GRUB_EFI_END_DEVICE_PATH_TYPE, \
+ .subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE, \
+ .length = sizeof (struct grub_efi_device_path) \
+ } \
+ }
+
+/* early 2006 Core Duo / Core Solo models */
+static struct piwg_full_device_path devpath_1 = MAKE_PIWG_PATH (0xffe00000,
+ 0xfff9ffff);
+
+/* mid-2006 Mac Pro (and probably other Core 2 models) */
+static struct piwg_full_device_path devpath_2 = MAKE_PIWG_PATH (0xffe00000,
+ 0xfff7ffff);
+
+/* mid-2007 MBP ("Santa Rosa" based models) */
+static struct piwg_full_device_path devpath_3 = MAKE_PIWG_PATH (0xffe00000,
+ 0xfff8ffff);
+
+/* early-2008 MBA */
+static struct piwg_full_device_path devpath_4 = MAKE_PIWG_PATH (0xffc00000,
+ 0xfff8ffff);
+
+/* late-2008 MB/MBP (NVidia chipset) */
+static struct piwg_full_device_path devpath_5 = MAKE_PIWG_PATH (0xffcb4000,
+ 0xffffbfff);
+
+/* mid-2010 MB/MBP (NVidia chipset) */
+static struct piwg_full_device_path devpath_6 = MAKE_PIWG_PATH (0xffcc4000,
+ 0xffffbfff);
+
+static struct piwg_full_device_path devpath_7 = MAKE_PIWG_PATH (0xff981000,
+ 0xffc8ffff);
+
+/* mid-2012 MBP retina (MacBookPro10,1) */
+static struct piwg_full_device_path devpath_8 = MAKE_PIWG_PATH (0xff990000,
+ 0xffb2ffff);
+
+struct devdata
+{
+ const char *model;
+ grub_efi_device_path_t *devpath;
+};
+
+struct devdata devs[] =
+{
+ {"Core Duo/Solo", (grub_efi_device_path_t *) &devpath_1},
+ {"Mac Pro", (grub_efi_device_path_t *) &devpath_2},
+ {"MBP", (grub_efi_device_path_t *) &devpath_3},
+ {"MBA", (grub_efi_device_path_t *) &devpath_4},
+ {"MB NV", (grub_efi_device_path_t *) &devpath_5},
+ {"MB NV2", (grub_efi_device_path_t *) &devpath_6},
+ {"MBP2011", (grub_efi_device_path_t *) &devpath_7},
+ {"MBP2012", (grub_efi_device_path_t *) &devpath_8},
+ {NULL, NULL},
+};
+
+static grub_err_t
+grub_cmd_appleloader (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_efi_boot_services_t *b;
+ grub_efi_loaded_image_t *loaded_image;
+ struct devdata *pdev;
+
+ grub_dl_ref (my_mod);
+
+ /* Initialize some global variables. */
+ image_handle = 0;
+
+ b = grub_efi_system_table->boot_services;
+
+ for (pdev = devs ; pdev->devpath ; pdev++)
+ if (efi_call_6 (b->load_image, 0, grub_efi_image_handle, pdev->devpath,
+ NULL, 0, &image_handle) == GRUB_EFI_SUCCESS)
+ break;
+
+ if (! pdev->devpath)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "can't find model");
+ goto fail;
+ }
+
+ grub_dprintf ("appleload", "Model: %s\n", pdev->model);
+
+ loaded_image = grub_efi_get_loaded_image (image_handle);
+ if (! loaded_image)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
+ goto fail;
+ }
+
+ if (argc > 0)
+ {
+ int i, len;
+ grub_efi_char16_t *p16;
+
+ for (i = 0, len = 0; i < argc; i++)
+ len += grub_strlen (argv[i]) + 1;
+
+ len *= sizeof (grub_efi_char16_t);
+ cmdline = p16 = grub_malloc (len);
+ if (! cmdline)
+ goto fail;
+
+ for (i = 0; i < argc; i++)
+ {
+ char *p8;
+
+ p8 = argv[i];
+ while (*p8)
+ *(p16++) = *(p8++);
+
+ *(p16++) = ' ';
+ }
+ *(--p16) = 0;
+
+ loaded_image->load_options = cmdline;
+ loaded_image->load_options_size = len;
+ }
+
+ grub_loader_set (grub_appleloader_boot, grub_appleloader_unload, 0);
+
+ return 0;
+
+ fail:
+
+ grub_dl_unref (my_mod);
+ return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(appleloader)
+{
+ cmd = grub_register_command ("appleloader", grub_cmd_appleloader,
+ N_("[OPTS]"),
+ /* TRANSLATORS: This command is used on EFI to
+ switch to BIOS mode and boot the OS requiring
+ BIOS. */
+ N_("Boot BIOS-based system."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(appleloader)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
new file mode 100644
index 0000000..2bd80f4
--- /dev/null
+++ b/grub-core/loader/efi/chainloader.c
@@ -0,0 +1,444 @@
+/* chainloader.c - boot another boot loader */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2004,2006,2007,2008 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/>.
+ */
+
+/* TODO: support load options. */
+
+#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/charset.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/disk.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/net.h>
+#if defined (__i386__) || defined (__x86_64__)
+#include <grub/macho.h>
+#include <grub/i386/macho.h>
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+
+static grub_efi_physical_address_t address;
+static grub_efi_uintn_t pages;
+static grub_efi_device_path_t *file_path;
+static grub_efi_handle_t image_handle;
+static grub_efi_char16_t *cmdline;
+
+static grub_err_t
+grub_chainloader_unload (void)
+{
+ grub_efi_boot_services_t *b;
+
+ b = grub_efi_system_table->boot_services;
+ efi_call_1 (b->unload_image, image_handle);
+ efi_call_2 (b->free_pages, address, pages);
+
+ grub_free (file_path);
+ grub_free (cmdline);
+ cmdline = 0;
+ file_path = 0;
+
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_chainloader_boot (void)
+{
+ grub_efi_boot_services_t *b;
+ grub_efi_status_t status;
+ grub_efi_uintn_t exit_data_size;
+ grub_efi_char16_t *exit_data = NULL;
+
+ b = grub_efi_system_table->boot_services;
+ status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ if (exit_data)
+ {
+ char *buf;
+
+ buf = grub_malloc (exit_data_size * 4 + 1);
+ if (buf)
+ {
+ *grub_utf16_to_utf8 ((grub_uint8_t *) buf,
+ exit_data, exit_data_size) = 0;
+
+ grub_error (GRUB_ERR_BAD_OS, "%s", buf);
+ grub_free (buf);
+ }
+ }
+ else
+ grub_error (GRUB_ERR_BAD_OS, "unknown error");
+ }
+
+ if (exit_data)
+ efi_call_1 (b->free_pool, exit_data);
+
+ grub_loader_unset ();
+
+ return grub_errno;
+}
+
+static grub_err_t
+copy_file_path (grub_efi_file_path_device_path_t *fp,
+ const char *str, grub_efi_uint16_t len)
+{
+ grub_efi_char16_t *p, *path_name;
+ grub_efi_uint16_t size;
+
+ fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
+ fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
+
+ path_name = grub_calloc (len, GRUB_MAX_UTF16_PER_UTF8 * sizeof (*path_name));
+ if (!path_name)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate path buffer");
+
+ size = grub_utf8_to_utf16 (path_name, len * GRUB_MAX_UTF16_PER_UTF8,
+ (const grub_uint8_t *) str, len, 0);
+ for (p = path_name; p < path_name + size; p++)
+ if (*p == '/')
+ *p = '\\';
+
+ grub_memcpy (fp->path_name, path_name, size * sizeof (*fp->path_name));
+ /* File Path is NULL terminated */
+ fp->path_name[size++] = '\0';
+ fp->header.length = size * sizeof (grub_efi_char16_t) + sizeof (*fp);
+ grub_free (path_name);
+ return GRUB_ERR_NONE;
+}
+
+static grub_efi_device_path_t *
+make_file_path (grub_efi_device_path_t *dp, const char *filename)
+{
+ char *dir_start;
+ char *dir_end;
+ grub_size_t size;
+ grub_efi_device_path_t *d;
+
+ dir_start = grub_strchr (filename, ')');
+ if (! dir_start)
+ dir_start = (char *) filename;
+ else
+ dir_start++;
+
+ dir_end = grub_strrchr (dir_start, '/');
+ if (! dir_end)
+ {
+ grub_error (GRUB_ERR_BAD_FILENAME, "invalid EFI file path");
+ return 0;
+ }
+
+ size = 0;
+ d = dp;
+ while (d)
+ {
+ grub_size_t len = GRUB_EFI_DEVICE_PATH_LENGTH (d);
+
+ if (len < 4)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "malformed EFI Device Path node has length=%" PRIuGRUB_SIZE, len);
+ return NULL;
+ }
+
+ size += len;
+ if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
+ break;
+ d = GRUB_EFI_NEXT_DEVICE_PATH (d);
+ }
+
+ /* File Path is NULL terminated. Allocate space for 2 extra characters */
+ /* FIXME why we split path in two components? */
+ file_path = grub_malloc (size
+ + ((grub_strlen (dir_start) + 2)
+ * GRUB_MAX_UTF16_PER_UTF8
+ * sizeof (grub_efi_char16_t))
+ + sizeof (grub_efi_file_path_device_path_t) * 2);
+ if (! file_path)
+ return 0;
+
+ grub_memcpy (file_path, dp, size);
+
+ /* Fill the file path for the directory. */
+ d = (grub_efi_device_path_t *) ((char *) file_path
+ + ((char *) d - (char *) dp));
+ grub_efi_print_device_path (d);
+ if (copy_file_path ((grub_efi_file_path_device_path_t *) d,
+ dir_start, dir_end - dir_start) != GRUB_ERR_NONE)
+ {
+ fail:
+ grub_free (file_path);
+ return 0;
+ }
+
+ /* Fill the file path for the file. */
+ d = GRUB_EFI_NEXT_DEVICE_PATH (d);
+ if (copy_file_path ((grub_efi_file_path_device_path_t *) d,
+ dir_end + 1, grub_strlen (dir_end + 1)) != GRUB_ERR_NONE)
+ goto fail;
+
+ /* Fill the end of device path nodes. */
+ d = GRUB_EFI_NEXT_DEVICE_PATH (d);
+ d->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+ d->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ d->length = sizeof (*d);
+
+ return file_path;
+}
+
+static grub_err_t
+grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_ssize_t size;
+ grub_efi_status_t status;
+ grub_efi_boot_services_t *b;
+ grub_device_t dev = 0;
+ grub_efi_device_path_t *dp = 0;
+ grub_efi_loaded_image_t *loaded_image;
+ char *filename;
+ void *boot_image = 0;
+ grub_efi_handle_t dev_handle = 0;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ filename = argv[0];
+
+ grub_dl_ref (my_mod);
+
+ /* Initialize some global variables. */
+ address = 0;
+ image_handle = 0;
+ file_path = 0;
+
+ b = grub_efi_system_table->boot_services;
+
+ file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE);
+ if (! file)
+ goto fail;
+
+ /* Get the root device's device path. */
+ dev = grub_device_open (0);
+ if (! dev)
+ goto fail;
+
+ if (dev->disk)
+ dev_handle = grub_efidisk_get_device_handle (dev->disk);
+ else if (dev->net && dev->net->server)
+ {
+ grub_net_network_level_address_t addr;
+ struct grub_net_network_level_interface *inf;
+ grub_net_network_level_address_t gateway;
+ grub_err_t err;
+
+ err = grub_net_resolve_address (dev->net->server, &addr);
+ if (err)
+ goto fail;
+
+ err = grub_net_route_address (addr, &gateway, &inf);
+ if (err)
+ goto fail;
+
+ dev_handle = grub_efinet_get_device_handle (inf->card);
+ }
+
+ if (dev_handle)
+ dp = grub_efi_get_device_path (dev_handle);
+
+ if (! dp)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "not a valid root device");
+ goto fail;
+ }
+
+ file_path = make_file_path (dp, filename);
+ if (! file_path)
+ goto fail;
+
+ grub_printf ("file path: ");
+ grub_efi_print_device_path (file_path);
+
+ size = grub_file_size (file);
+ if (!size)
+ {
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ goto fail;
+ }
+ pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12);
+
+ status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES,
+ GRUB_EFI_LOADER_CODE,
+ pages, &address);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_dprintf ("chain", "Failed to allocate %u pages\n",
+ (unsigned int) pages);
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto fail;
+ }
+
+ boot_image = (void *) ((grub_addr_t) address);
+ if (grub_file_read (file, boot_image, size) != size)
+ {
+ if (grub_errno == GRUB_ERR_NONE)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+
+ goto fail;
+ }
+
+#if defined (__i386__) || defined (__x86_64__)
+ if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
+ {
+ struct grub_macho_fat_header *head = boot_image;
+ if (head->magic
+ == grub_cpu_to_le32_compile_time (GRUB_MACHO_FAT_EFI_MAGIC))
+ {
+ grub_uint32_t i;
+ struct grub_macho_fat_arch *archs
+ = (struct grub_macho_fat_arch *) (head + 1);
+ for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++)
+ {
+ if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype))
+ break;
+ }
+ if (i == grub_cpu_to_le32 (head->nfat_arch))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "no compatible arch found");
+ goto fail;
+ }
+ if (grub_cpu_to_le32 (archs[i].offset)
+ > ~grub_cpu_to_le32 (archs[i].size)
+ || grub_cpu_to_le32 (archs[i].offset)
+ + grub_cpu_to_le32 (archs[i].size)
+ > (grub_size_t) size)
+ {
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ goto fail;
+ }
+ boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset);
+ size = grub_cpu_to_le32 (archs[i].size);
+ }
+ }
+#endif
+
+ status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
+ boot_image, size,
+ &image_handle);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ if (status == GRUB_EFI_OUT_OF_RESOURCES)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
+ else
+ grub_error (GRUB_ERR_BAD_OS, "cannot load image");
+
+ goto fail;
+ }
+
+ /* LoadImage does not set a device handler when the image is
+ loaded from memory, so it is necessary to set it explicitly here.
+ This is a mess. */
+ loaded_image = grub_efi_get_loaded_image (image_handle);
+ if (! loaded_image)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
+ goto fail;
+ }
+ loaded_image->device_handle = dev_handle;
+
+ if (argc > 1)
+ {
+ int i, len;
+ grub_efi_char16_t *p16;
+
+ for (i = 1, len = 0; i < argc; i++)
+ len += grub_strlen (argv[i]) + 1;
+
+ len *= sizeof (grub_efi_char16_t);
+ cmdline = p16 = grub_malloc (len);
+ if (! cmdline)
+ goto fail;
+
+ for (i = 1; i < argc; i++)
+ {
+ char *p8;
+
+ p8 = argv[i];
+ while (*p8)
+ *(p16++) = *(p8++);
+
+ *(p16++) = ' ';
+ }
+ *(--p16) = 0;
+
+ loaded_image->load_options = cmdline;
+ loaded_image->load_options_size = len;
+ }
+
+ grub_file_close (file);
+ grub_device_close (dev);
+
+ grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
+ return 0;
+
+ fail:
+
+ if (dev)
+ grub_device_close (dev);
+
+ if (file)
+ grub_file_close (file);
+
+ grub_free (file_path);
+
+ if (address)
+ efi_call_2 (b->free_pages, address, pages);
+
+ grub_dl_unref (my_mod);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(chainloader)
+{
+ cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
+ 0, N_("Load another boot loader."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(chainloader)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/loader/efi/fdt.c b/grub-core/loader/efi/fdt.c
new file mode 100644
index 0000000..c86f283
--- /dev/null
+++ b/grub-core/loader/efi/fdt.c
@@ -0,0 +1,179 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2013-2015 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/fdt.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/file.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/fdtload.h>
+#include <grub/efi/memory.h>
+#include <grub/cpu/efi/memory.h>
+
+static void *loaded_fdt;
+static void *fdt;
+
+#define FDT_ADDR_CELLS_STRING "#address-cells"
+#define FDT_SIZE_CELLS_STRING "#size-cells"
+#define FDT_ADDR_SIZE_EXTRA ((2 * grub_fdt_prop_entry_size (sizeof(grub_uint32_t))) + \
+ sizeof (FDT_ADDR_CELLS_STRING) + \
+ sizeof (FDT_SIZE_CELLS_STRING))
+
+void *
+grub_fdt_load (grub_size_t additional_size)
+{
+ void *raw_fdt;
+ unsigned int size;
+
+ if (fdt)
+ {
+ size = GRUB_EFI_BYTES_TO_PAGES (grub_fdt_get_totalsize (fdt));
+ grub_efi_free_pages ((grub_addr_t) fdt, size);
+ }
+
+ if (loaded_fdt)
+ raw_fdt = loaded_fdt;
+ else
+ raw_fdt = grub_efi_get_firmware_fdt();
+
+ if (raw_fdt)
+ size = grub_fdt_get_totalsize (raw_fdt);
+ else
+ size = GRUB_FDT_EMPTY_TREE_SZ + FDT_ADDR_SIZE_EXTRA;
+
+ size += additional_size;
+
+ grub_dprintf ("linux", "allocating %d bytes for fdt\n", size);
+ fdt = grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS,
+ GRUB_EFI_BYTES_TO_PAGES (size),
+ GRUB_EFI_ALLOCATE_MAX_ADDRESS,
+ GRUB_EFI_ACPI_RECLAIM_MEMORY);
+ if (!fdt)
+ return NULL;
+
+ if (raw_fdt)
+ {
+ grub_memmove (fdt, raw_fdt, size - additional_size);
+ grub_fdt_set_totalsize (fdt, size);
+ }
+ else
+ {
+ grub_fdt_create_empty_tree (fdt, size);
+ grub_fdt_set_prop32 (fdt, 0, FDT_ADDR_CELLS_STRING, 2);
+ grub_fdt_set_prop32 (fdt, 0, FDT_SIZE_CELLS_STRING, 2);
+ }
+ return fdt;
+}
+
+grub_err_t
+grub_fdt_install (void)
+{
+ grub_efi_boot_services_t *b;
+ grub_efi_guid_t fdt_guid = GRUB_EFI_DEVICE_TREE_GUID;
+ grub_efi_status_t status;
+
+ b = grub_efi_system_table->boot_services;
+ status = b->install_configuration_table (&fdt_guid, fdt);
+ if (status != GRUB_EFI_SUCCESS)
+ return grub_error (GRUB_ERR_IO, "failed to install FDT");
+
+ grub_dprintf ("fdt", "Installed/updated FDT configuration table @ %p\n",
+ fdt);
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_fdt_unload (void) {
+ if (!fdt) {
+ return;
+ }
+ grub_efi_free_pages ((grub_addr_t) fdt,
+ GRUB_EFI_BYTES_TO_PAGES (grub_fdt_get_totalsize (fdt)));
+ fdt = NULL;
+}
+
+static grub_err_t
+grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t dtb;
+ void *blob = NULL;
+ int size;
+
+ if (loaded_fdt)
+ grub_free (loaded_fdt);
+ loaded_fdt = NULL;
+
+ /* No arguments means "use firmware FDT". */
+ if (argc == 0)
+ {
+ return GRUB_ERR_NONE;
+ }
+
+ dtb = grub_file_open (argv[0], GRUB_FILE_TYPE_DEVICE_TREE_IMAGE);
+ if (!dtb)
+ goto out;
+
+ size = grub_file_size (dtb);
+ blob = grub_malloc (size);
+ if (!blob)
+ goto out;
+
+ if (grub_file_read (dtb, blob, size) < size)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
+ goto out;
+ }
+
+ if (grub_fdt_check_header (blob, size) != 0)
+ {
+ grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree"));
+ goto out;
+ }
+
+out:
+ if (dtb)
+ grub_file_close (dtb);
+
+ if (blob)
+ {
+ if (grub_errno == GRUB_ERR_NONE)
+ loaded_fdt = blob;
+ else
+ grub_free (blob);
+ }
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_devicetree;
+
+GRUB_MOD_INIT (fdt)
+{
+ cmd_devicetree =
+ grub_register_command_lockdown ("devicetree", grub_cmd_devicetree, 0,
+ N_("Load DTB file."));
+}
+
+GRUB_MOD_FINI (fdt)
+{
+ grub_unregister_command (cmd_devicetree);
+}
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);
+}
diff --git a/grub-core/loader/ia64/efi/linux.c b/grub-core/loader/ia64/efi/linux.c
new file mode 100644
index 0000000..7987fd1
--- /dev/null
+++ b/grub-core/loader/ia64/efi/linux.c
@@ -0,0 +1,607 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,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/disk.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/command.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/cache.h>
+#include <grub/kernel.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/elf.h>
+#include <grub/i18n.h>
+#include <grub/env.h>
+#include <grub/linux.h>
+#include <grub/verify.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+#define ALIGN_MIN (256*1024*1024)
+
+#define GRUB_ELF_SEARCH 1024
+
+#define BOOT_PARAM_SIZE 16384
+
+struct ia64_boot_param
+{
+ grub_uint64_t command_line; /* physical address of command line. */
+ grub_uint64_t efi_systab; /* physical address of EFI system table */
+ grub_uint64_t efi_memmap; /* physical address of EFI memory map */
+ grub_uint64_t efi_memmap_size; /* size of EFI memory map */
+ grub_uint64_t efi_memdesc_size; /* size of an EFI memory map descriptor */
+ grub_uint32_t efi_memdesc_version; /* memory descriptor version */
+ struct
+ {
+ grub_uint16_t num_cols; /* number of columns on console output dev */
+ grub_uint16_t num_rows; /* number of rows on console output device */
+ grub_uint16_t orig_x; /* cursor's x position */
+ grub_uint16_t orig_y; /* cursor's y position */
+ } console_info;
+ grub_uint64_t fpswa; /* physical address of the fpswa interface */
+ grub_uint64_t initrd_start;
+ grub_uint64_t initrd_size;
+};
+
+typedef struct
+{
+ grub_uint32_t revision;
+ grub_uint32_t reserved;
+ void *fpswa;
+} fpswa_interface_t;
+static fpswa_interface_t *fpswa;
+
+#define NEXT_MEMORY_DESCRIPTOR(desc, size) \
+ ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
+
+static grub_dl_t my_mod;
+
+static int loaded;
+
+/* Kernel base and size. */
+static void *kernel_mem;
+static grub_efi_uintn_t kernel_pages;
+static grub_uint64_t entry;
+
+/* Initrd base and size. */
+static void *initrd_mem;
+static grub_efi_uintn_t initrd_pages;
+static grub_efi_uintn_t initrd_size;
+
+static struct ia64_boot_param *boot_param;
+static grub_efi_uintn_t boot_param_pages;
+
+static inline grub_size_t
+page_align (grub_size_t size)
+{
+ return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
+}
+
+static void
+query_fpswa (void)
+{
+ grub_efi_handle_t fpswa_image;
+ grub_efi_boot_services_t *bs;
+ grub_efi_status_t status;
+ grub_efi_uintn_t size;
+ static const grub_efi_guid_t fpswa_protocol =
+ { 0xc41b6531, 0x97b9, 0x11d3,
+ {0x9a, 0x29, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d} };
+
+ if (fpswa != NULL)
+ return;
+
+ size = sizeof(grub_efi_handle_t);
+
+ bs = grub_efi_system_table->boot_services;
+ status = bs->locate_handle (GRUB_EFI_BY_PROTOCOL,
+ (void *) &fpswa_protocol,
+ NULL, &size, &fpswa_image);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_printf ("%s\n", _("Could not locate FPSWA driver"));
+ return;
+ }
+ status = bs->handle_protocol (fpswa_image,
+ (void *) &fpswa_protocol, (void *) &fpswa);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_printf ("%s\n",
+ _("FPSWA protocol wasn't able to find the interface"));
+ return;
+ }
+}
+
+static void
+free_pages (void)
+{
+ if (kernel_mem)
+ {
+ grub_efi_free_pages ((grub_addr_t) kernel_mem, kernel_pages);
+ kernel_mem = 0;
+ }
+
+ if (initrd_mem)
+ {
+ grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages);
+ initrd_mem = 0;
+ }
+
+ if (boot_param)
+ {
+ /* Free bootparam. */
+ grub_efi_free_pages ((grub_efi_physical_address_t) boot_param,
+ boot_param_pages);
+ boot_param = 0;
+ }
+}
+
+static void *
+allocate_pages (grub_uint64_t align, grub_uint64_t size_pages,
+ grub_uint64_t nobase)
+{
+ grub_uint64_t size;
+ grub_efi_uintn_t desc_size;
+ grub_efi_memory_descriptor_t *mmap, *mmap_end;
+ grub_efi_uintn_t mmap_size, tmp_mmap_size;
+ grub_efi_memory_descriptor_t *desc;
+ void *mem = NULL;
+
+ size = size_pages << 12;
+
+ mmap_size = grub_efi_find_mmap_size ();
+ if (!mmap_size)
+ return 0;
+
+ /* Read the memory map temporarily, to find free space. */
+ mmap = grub_malloc (mmap_size);
+ if (! mmap)
+ return 0;
+
+ tmp_mmap_size = mmap_size;
+ if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
+ {
+ grub_error (GRUB_ERR_IO, "cannot get memory map");
+ goto fail;
+ }
+
+ mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
+
+ /* First, find free pages for the real mode code
+ and the memory map buffer. */
+ for (desc = mmap;
+ desc < mmap_end;
+ desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
+ {
+ grub_uint64_t start, end;
+ grub_uint64_t aligned_start;
+
+ if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY)
+ continue;
+
+ start = desc->physical_start;
+ end = start + (desc->num_pages << 12);
+ /* Align is a power of 2. */
+ aligned_start = (start + align - 1) & ~(align - 1);
+ if (aligned_start + size > end)
+ continue;
+ if (aligned_start == nobase)
+ aligned_start += align;
+ if (aligned_start + size > end)
+ continue;
+ mem = grub_efi_allocate_fixed (aligned_start, size_pages);
+ if (! mem)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
+ goto fail;
+ }
+ break;
+ }
+
+ if (! mem)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
+ goto fail;
+ }
+
+ grub_free (mmap);
+ return mem;
+
+ fail:
+ grub_free (mmap);
+ free_pages ();
+ return 0;
+}
+
+static void
+set_boot_param_console (void)
+{
+ grub_efi_simple_text_output_interface_t *conout;
+ grub_efi_uintn_t cols, rows;
+
+ conout = grub_efi_system_table->con_out;
+ if (conout->query_mode (conout, conout->mode->mode, &cols, &rows)
+ != GRUB_EFI_SUCCESS)
+ return;
+
+ grub_dprintf ("linux",
+ "Console info: cols=%lu rows=%lu x=%u y=%u\n",
+ cols, rows,
+ conout->mode->cursor_column, conout->mode->cursor_row);
+
+ boot_param->console_info.num_cols = cols;
+ boot_param->console_info.num_rows = rows;
+ boot_param->console_info.orig_x = conout->mode->cursor_column;
+ boot_param->console_info.orig_y = conout->mode->cursor_row;
+}
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ grub_efi_uintn_t mmap_size;
+ grub_efi_uintn_t map_key;
+ grub_efi_uintn_t desc_size;
+ grub_efi_uint32_t desc_version;
+ grub_efi_memory_descriptor_t *mmap_buf;
+ grub_err_t err;
+
+ /* FPSWA. */
+ query_fpswa ();
+ boot_param->fpswa = (grub_uint64_t)fpswa;
+
+ /* Initrd. */
+ boot_param->initrd_start = (grub_uint64_t)initrd_mem;
+ boot_param->initrd_size = (grub_uint64_t)initrd_size;
+
+ set_boot_param_console ();
+
+ grub_dprintf ("linux", "Jump to %016lx\n", entry);
+
+ /* MDT.
+ Must be done after grub_machine_fini because map_key is used by
+ exit_boot_services. */
+ mmap_size = grub_efi_find_mmap_size ();
+ if (! mmap_size)
+ return grub_errno;
+ mmap_buf = grub_efi_allocate_any_pages (page_align (mmap_size) >> 12);
+ if (! mmap_buf)
+ return grub_error (GRUB_ERR_IO, "cannot allocate memory map");
+ err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, &map_key,
+ &desc_size, &desc_version);
+ if (err)
+ return err;
+
+ boot_param->efi_memmap = (grub_uint64_t)mmap_buf;
+ boot_param->efi_memmap_size = mmap_size;
+ boot_param->efi_memdesc_size = desc_size;
+ boot_param->efi_memdesc_version = desc_version;
+
+ /* See you next boot. */
+ asm volatile ("mov r28=%1; br.sptk.few %0" :: "b"(entry),"r"(boot_param));
+
+ /* Never reach here. */
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ free_pages ();
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_load_elf64 (grub_file_t file, void *buffer, const char *filename)
+{
+ Elf64_Ehdr *ehdr = (Elf64_Ehdr *) buffer;
+ Elf64_Phdr *phdr;
+ int i;
+ grub_uint64_t low_addr;
+ grub_uint64_t high_addr;
+ grub_uint64_t align;
+ grub_uint64_t reloc_offset;
+ const char *relocate;
+
+ if (ehdr->e_ident[EI_MAG0] != ELFMAG0
+ || ehdr->e_ident[EI_MAG1] != ELFMAG1
+ || ehdr->e_ident[EI_MAG2] != ELFMAG2
+ || ehdr->e_ident[EI_MAG3] != ELFMAG3
+ || ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
+ return grub_error(GRUB_ERR_UNKNOWN_OS,
+ N_("invalid arch-independent ELF magic"));
+
+ if (ehdr->e_ident[EI_CLASS] != ELFCLASS64
+ || ehdr->e_version != EV_CURRENT
+ || ehdr->e_machine != EM_IA_64)
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ N_("invalid arch-dependent ELF magic"));
+
+ if (ehdr->e_type != ET_EXEC)
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ N_("this ELF file is not of the right type"));
+
+ /* FIXME: Should we support program headers at strange locations? */
+ if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > GRUB_ELF_SEARCH)
+ return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
+
+ entry = ehdr->e_entry;
+
+ /* Compute low, high and align addresses. */
+ low_addr = ~0UL;
+ high_addr = 0;
+ align = 0;
+ for (i = 0; i < ehdr->e_phnum; i++)
+ {
+ phdr = (Elf64_Phdr *) ((char *) buffer + ehdr->e_phoff
+ + i * ehdr->e_phentsize);
+ if (phdr->p_type == PT_LOAD)
+ {
+ if (phdr->p_paddr < low_addr)
+ low_addr = phdr->p_paddr;
+ if (phdr->p_paddr + phdr->p_memsz > high_addr)
+ high_addr = phdr->p_paddr + phdr->p_memsz;
+ if (phdr->p_align > align)
+ align = phdr->p_align;
+ }
+ }
+
+ if (align < ALIGN_MIN)
+ align = ALIGN_MIN;
+
+ if (high_addr == 0)
+ return grub_error (GRUB_ERR_BAD_OS, "no program entries");
+
+ kernel_pages = page_align (high_addr - low_addr) >> 12;
+
+ /* Undocumented on purpose. */
+ relocate = grub_env_get ("linux_relocate");
+ if (!relocate || grub_strcmp (relocate, "force") != 0)
+ {
+ kernel_mem = grub_efi_allocate_fixed (low_addr, kernel_pages);
+ reloc_offset = 0;
+ }
+ /* Try to relocate. */
+ if (! kernel_mem && (!relocate || grub_strcmp (relocate, "off") != 0))
+ {
+ kernel_mem = allocate_pages (align, kernel_pages, low_addr);
+ if (kernel_mem)
+ {
+ reloc_offset = (grub_uint64_t)kernel_mem - low_addr;
+ grub_dprintf ("linux", " Relocated at %p (offset=%016lx)\n",
+ kernel_mem, reloc_offset);
+ entry += reloc_offset;
+ }
+ }
+ if (! kernel_mem)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "cannot allocate memory for OS");
+
+ /* Load every loadable segment in memory. */
+ for (i = 0; i < ehdr->e_phnum; i++)
+ {
+ phdr = (Elf64_Phdr *) ((char *) buffer + ehdr->e_phoff
+ + i * ehdr->e_phentsize);
+ if (phdr->p_type == PT_LOAD)
+ {
+ grub_dprintf ("linux", " [paddr=%lx load=%lx memsz=%08lx "
+ "off=%lx flags=%x]\n",
+ phdr->p_paddr, phdr->p_paddr + reloc_offset,
+ phdr->p_memsz, phdr->p_offset, phdr->p_flags);
+
+ if (grub_file_seek (file, phdr->p_offset) == (grub_off_t)-1)
+ return grub_errno;
+
+ if (grub_file_read (file, (void *) (phdr->p_paddr + reloc_offset),
+ phdr->p_filesz)
+ != (grub_ssize_t) phdr->p_filesz)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ return grub_errno;
+ }
+
+ if (phdr->p_filesz < phdr->p_memsz)
+ grub_memset
+ ((char *)(phdr->p_paddr + reloc_offset + phdr->p_filesz),
+ 0, phdr->p_memsz - phdr->p_filesz);
+
+ /* Sync caches if necessary. */
+ if (phdr->p_flags & PF_X)
+ grub_arch_sync_caches
+ ((void *)(phdr->p_paddr + reloc_offset), phdr->p_memsz);
+ }
+ }
+ loaded = 1;
+ return 0;
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ char buffer[GRUB_ELF_SEARCH];
+ char *cmdline, *p;
+ grub_ssize_t len;
+ int i;
+
+ grub_dl_ref (my_mod);
+
+ grub_loader_unset ();
+
+ 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;
+
+ len = grub_file_read (file, buffer, sizeof (buffer));
+ if (len < (grub_ssize_t) sizeof (Elf64_Ehdr))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+ goto fail;
+ }
+
+ grub_dprintf ("linux", "Loading linux: %s\n", argv[0]);
+
+ if (grub_load_elf64 (file, buffer, argv[0]))
+ goto fail;
+
+ len = sizeof("BOOT_IMAGE=") + 8;
+ for (i = 0; i < argc; i++)
+ len += grub_strlen (argv[i]) + 1;
+ len += sizeof (struct ia64_boot_param) + 512; /* Room for extensions. */
+ boot_param_pages = page_align (len) >> 12;
+ boot_param = grub_efi_allocate_any_pages (boot_param_pages);
+ if (boot_param == 0)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "cannot allocate memory for bootparams");
+ goto fail;
+ }
+
+ grub_memset (boot_param, 0, len);
+ cmdline = ((char *)(boot_param + 1)) + 256;
+
+ /* Build cmdline. */
+ p = grub_stpcpy (cmdline, "BOOT_IMAGE");
+ for (i = 0; i < argc; i++)
+ {
+ *p++ = ' ';
+ p = grub_stpcpy (p, argv[i]);
+ }
+ cmdline[10] = '=';
+
+ *p = '\0';
+
+ if (grub_verify_string (cmdline, GRUB_VERIFY_KERNEL_CMDLINE))
+ goto fail;
+
+ boot_param->command_line = (grub_uint64_t) cmdline;
+ boot_param->efi_systab = (grub_uint64_t) grub_efi_system_table;
+
+ grub_errno = GRUB_ERR_NONE;
+
+ grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
+
+ fail:
+ if (file)
+ grub_file_close (file);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_efi_free_pages ((grub_efi_physical_address_t) boot_param,
+ boot_param_pages);
+ grub_dl_unref (my_mod);
+ }
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ 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;
+
+ initrd_size = grub_get_initrd_size (&initrd_ctx);
+ grub_dprintf ("linux", "Loading initrd\n");
+
+ initrd_pages = (page_align (initrd_size) >> 12);
+ initrd_mem = grub_efi_allocate_any_pages (initrd_pages);
+ if (! initrd_mem)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate pages");
+ goto fail;
+ }
+
+ grub_dprintf ("linux", "[addr=0x%lx, size=0x%lx]\n",
+ (grub_uint64_t) initrd_mem, initrd_size);
+
+ if (grub_initrd_load (&initrd_ctx, argv, initrd_mem))
+ goto fail;
+ fail:
+ grub_initrd_close (&initrd_ctx);
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_fpswa (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__((unused)),
+ char *argv[] __attribute__((unused)))
+{
+ query_fpswa ();
+ if (fpswa == NULL)
+ grub_puts_ (N_("No FPSWA found"));
+ else
+ grub_printf (_("FPSWA revision: %x\n"), fpswa->revision);
+ return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd_linux, cmd_initrd, cmd_fpswa;
+
+GRUB_MOD_INIT(linux)
+{
+ cmd_linux = grub_register_command ("linux", grub_cmd_linux,
+ N_("FILE [ARGS...]"), N_("Load Linux."));
+
+ cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
+ N_("FILE"), N_("Load initrd."));
+
+ cmd_fpswa = grub_register_command ("fpswa", grub_cmd_fpswa,
+ "", N_("Display FPSWA version."));
+
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(linux)
+{
+ grub_unregister_command (cmd_linux);
+ grub_unregister_command (cmd_initrd);
+ grub_unregister_command (cmd_fpswa);
+}
diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c
new file mode 100644
index 0000000..3fe390f
--- /dev/null
+++ b/grub-core/loader/linux.c
@@ -0,0 +1,335 @@
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/linux.h>
+#include <grub/misc.h>
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/safemath.h>
+
+struct newc_head
+{
+ char magic[6];
+ char ino[8];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char nlink[8];
+ char mtime[8];
+ char filesize[8];
+ char devmajor[8];
+ char devminor[8];
+ char rdevmajor[8];
+ char rdevminor[8];
+ char namesize[8];
+ char check[8];
+} GRUB_PACKED;
+
+struct grub_linux_initrd_component
+{
+ grub_file_t file;
+ char *newc_name;
+ grub_off_t size;
+};
+
+struct dir
+{
+ char *name;
+ struct dir *next;
+ struct dir *child;
+};
+
+static char
+hex (grub_uint8_t val)
+{
+ if (val < 10)
+ return '0' + val;
+ return 'a' + val - 10;
+}
+
+static void
+set_field (char *var, grub_uint32_t val)
+{
+ int i;
+ char *ptr = var;
+ for (i = 28; i >= 0; i -= 4)
+ *ptr++ = hex((val >> i) & 0xf);
+}
+
+static grub_uint8_t *
+make_header (grub_uint8_t *ptr,
+ const char *name, grub_size_t len,
+ grub_uint32_t mode,
+ grub_off_t fsize)
+{
+ struct newc_head *head = (struct newc_head *) ptr;
+ grub_uint8_t *optr;
+ grub_size_t oh = 0;
+ grub_memcpy (head->magic, "070701", 6);
+ set_field (head->ino, 0);
+ set_field (head->mode, mode);
+ set_field (head->uid, 0);
+ set_field (head->gid, 0);
+ set_field (head->nlink, 1);
+ set_field (head->mtime, 0);
+ set_field (head->filesize, fsize);
+ set_field (head->devmajor, 0);
+ set_field (head->devminor, 0);
+ set_field (head->rdevmajor, 0);
+ set_field (head->rdevminor, 0);
+ set_field (head->namesize, len);
+ set_field (head->check, 0);
+ optr = ptr;
+ ptr += sizeof (struct newc_head);
+ grub_memcpy (ptr, name, len);
+ ptr += len;
+ oh = ALIGN_UP_OVERHEAD (ptr - optr, 4);
+ grub_memset (ptr, 0, oh);
+ ptr += oh;
+ return ptr;
+}
+
+static void
+free_dir (struct dir *root)
+{
+ if (!root)
+ return;
+ free_dir (root->next);
+ free_dir (root->child);
+ grub_free (root->name);
+ grub_free (root);
+}
+
+static grub_err_t
+insert_dir (const char *name, struct dir **root,
+ grub_uint8_t *ptr, grub_size_t *size)
+{
+ struct dir *cur, **head = root;
+ const char *cb, *ce = name;
+ *size = 0;
+ while (1)
+ {
+ for (cb = ce; *cb == '/'; cb++);
+ for (ce = cb; *ce && *ce != '/'; ce++);
+ if (!*ce)
+ break;
+
+ for (cur = *root; cur; cur = cur->next)
+ if (grub_memcmp (cur->name, cb, ce - cb)
+ && cur->name[ce - cb] == 0)
+ break;
+ if (!cur)
+ {
+ struct dir *n;
+ n = grub_zalloc (sizeof (*n));
+ if (!n)
+ return 0;
+ n->next = *head;
+ n->name = grub_strndup (cb, ce - cb);
+ if (ptr)
+ {
+ grub_dprintf ("linux", "Creating directory %s, %s\n", name, ce);
+ ptr = make_header (ptr, name, ce - name,
+ 040777, 0);
+ }
+ if (grub_add (*size,
+ ALIGN_UP ((ce - (char *) name)
+ + sizeof (struct newc_head), 4),
+ size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+ grub_free (n->name);
+ grub_free (n);
+ return grub_errno;
+ }
+ *head = n;
+ cur = n;
+ }
+ root = &cur->next;
+ }
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_initrd_init (int argc, char *argv[],
+ struct grub_linux_initrd_context *initrd_ctx)
+{
+ int i;
+ int newc = 0;
+ struct dir *root = 0;
+
+ initrd_ctx->nfiles = 0;
+ initrd_ctx->components = 0;
+
+ initrd_ctx->components = grub_calloc (argc, sizeof (initrd_ctx->components[0]));
+ if (!initrd_ctx->components)
+ return grub_errno;
+
+ initrd_ctx->size = 0;
+
+ for (i = 0; i < argc; i++)
+ {
+ const char *fname = argv[i];
+
+ initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4);
+
+ if (grub_memcmp (argv[i], "newc:", 5) == 0)
+ {
+ const char *ptr, *eptr;
+ ptr = argv[i] + 5;
+ while (*ptr == '/')
+ ptr++;
+ eptr = grub_strchr (ptr, ':');
+ if (eptr)
+ {
+ grub_size_t dir_size, name_len;
+
+ initrd_ctx->components[i].newc_name = grub_strndup (ptr, eptr - ptr);
+ if (!initrd_ctx->components[i].newc_name ||
+ insert_dir (initrd_ctx->components[i].newc_name, &root, 0,
+ &dir_size))
+ {
+ grub_initrd_close (initrd_ctx);
+ return grub_errno;
+ }
+ name_len = grub_strlen (initrd_ctx->components[i].newc_name);
+ if (grub_add (initrd_ctx->size,
+ ALIGN_UP (sizeof (struct newc_head) + name_len, 4),
+ &initrd_ctx->size) ||
+ grub_add (initrd_ctx->size, dir_size, &initrd_ctx->size))
+ goto overflow;
+ newc = 1;
+ fname = eptr + 1;
+ }
+ }
+ else if (newc)
+ {
+ if (grub_add (initrd_ctx->size,
+ ALIGN_UP (sizeof (struct newc_head)
+ + sizeof ("TRAILER!!!") - 1, 4),
+ &initrd_ctx->size))
+ goto overflow;
+ free_dir (root);
+ root = 0;
+ newc = 0;
+ }
+ initrd_ctx->components[i].file = grub_file_open (fname,
+ GRUB_FILE_TYPE_LINUX_INITRD
+ | GRUB_FILE_TYPE_NO_DECOMPRESS);
+ if (!initrd_ctx->components[i].file)
+ {
+ grub_initrd_close (initrd_ctx);
+ return grub_errno;
+ }
+ initrd_ctx->nfiles++;
+ initrd_ctx->components[i].size
+ = grub_file_size (initrd_ctx->components[i].file);
+ if (grub_add (initrd_ctx->size, initrd_ctx->components[i].size,
+ &initrd_ctx->size))
+ goto overflow;
+ }
+
+ if (newc)
+ {
+ initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4);
+ if (grub_add (initrd_ctx->size,
+ ALIGN_UP (sizeof (struct newc_head)
+ + sizeof ("TRAILER!!!") - 1, 4),
+ &initrd_ctx->size))
+ goto overflow;
+ free_dir (root);
+ root = 0;
+ }
+
+ return GRUB_ERR_NONE;
+
+ overflow:
+ free_dir (root);
+ grub_initrd_close (initrd_ctx);
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+}
+
+grub_size_t
+grub_get_initrd_size (struct grub_linux_initrd_context *initrd_ctx)
+{
+ return initrd_ctx->size;
+}
+
+void
+grub_initrd_close (struct grub_linux_initrd_context *initrd_ctx)
+{
+ int i;
+ if (!initrd_ctx->components)
+ return;
+ for (i = 0; i < initrd_ctx->nfiles; i++)
+ {
+ grub_free (initrd_ctx->components[i].newc_name);
+ grub_file_close (initrd_ctx->components[i].file);
+ }
+ grub_free (initrd_ctx->components);
+ initrd_ctx->components = 0;
+}
+
+grub_err_t
+grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx,
+ char *argv[], void *target)
+{
+ grub_uint8_t *ptr = target;
+ int i;
+ int newc = 0;
+ struct dir *root = 0;
+ grub_ssize_t cursize = 0;
+
+ for (i = 0; i < initrd_ctx->nfiles; i++)
+ {
+ grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4));
+ ptr += ALIGN_UP_OVERHEAD (cursize, 4);
+
+ if (initrd_ctx->components[i].newc_name)
+ {
+ grub_size_t dir_size;
+
+ if (insert_dir (initrd_ctx->components[i].newc_name, &root, ptr,
+ &dir_size))
+ {
+ free_dir (root);
+ grub_initrd_close (initrd_ctx);
+ return grub_errno;
+ }
+ ptr += dir_size;
+ ptr = make_header (ptr, initrd_ctx->components[i].newc_name,
+ grub_strlen (initrd_ctx->components[i].newc_name),
+ 0100777,
+ initrd_ctx->components[i].size);
+ newc = 1;
+ }
+ else if (newc)
+ {
+ ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!") - 1,
+ 0, 0);
+ free_dir (root);
+ root = 0;
+ newc = 0;
+ }
+
+ cursize = initrd_ctx->components[i].size;
+ if (grub_file_read (initrd_ctx->components[i].file, ptr, cursize)
+ != cursize)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
+ argv[i]);
+ grub_initrd_close (initrd_ctx);
+ return grub_errno;
+ }
+ ptr += cursize;
+ }
+ if (newc)
+ {
+ grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4));
+ ptr += ALIGN_UP_OVERHEAD (cursize, 4);
+ ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!") - 1, 0, 0);
+ }
+ free_dir (root);
+ root = 0;
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/loader/lzss.c b/grub-core/loader/lzss.c
new file mode 100644
index 0000000..532d8e4
--- /dev/null
+++ b/grub-core/loader/lzss.c
@@ -0,0 +1,56 @@
+/**************************************************************
+ LZSS.C -- A Data Compression Program
+ (tab = 4 spaces)
+***************************************************************
+ 4/6/1989 Haruhiko Okumura
+ Use, distribute, and modify this program freely.
+ Please send me your improved versions.
+ PC-VAN SCIENCE
+ NIFTY-Serve PAF01022
+ CompuServe 74050,1022
+**************************************************************/
+
+#include <grub/types.h>
+#include <grub/macho.h>
+
+#define N 4096 /* size of ring buffer */
+#define F 18 /* upper limit for match_length */
+#define THRESHOLD 2 /* encode string into position and length
+ if match_length is greater than this */
+#define NIL N /* index for root of binary search trees */
+
+#define EOF -1
+#define getc(file) ((src < srcend) ? *src++ : EOF)
+#define putc(c, file) (dst < dstend) ? (*dst++ = (c)) : 0;
+
+grub_size_t
+grub_decompress_lzss (grub_uint8_t *dst, grub_uint8_t *dstend,
+ grub_uint8_t *src, grub_uint8_t *srcend)
+{
+ int i, j, k, r, c;
+ unsigned int flags;
+ static unsigned char text_buf[N + F - 1];
+ grub_uint8_t *dst0 = dst;
+
+ for (i = 0; i < N - F; i++) text_buf[i] = ' ';
+ r = N - F; flags = 0;
+ for ( ; ; ) {
+ if (((flags >>= 1) & 256) == 0) {
+ if ((c = getc(infile)) == EOF) break;
+ flags = c | 0xff00; /* uses higher byte cleverly */
+ } /* to count eight */
+ if (flags & 1) {
+ if ((c = getc(infile)) == EOF) break;
+ putc(c, outfile); text_buf[r++] = c; r &= (N - 1);
+ } else {
+ if ((i = getc(infile)) == EOF) break;
+ if ((j = getc(infile)) == EOF) break;
+ i |= ((j & 0xf0) << 4); j = (j & 0x0f) + THRESHOLD;
+ for (k = 0; k <= j; k++) {
+ c = text_buf[(i + k) & (N - 1)];
+ putc(c, outfile); text_buf[r++] = c; r &= (N - 1);
+ }
+ }
+ }
+ return dst - dst0;
+}
diff --git a/grub-core/loader/macho.c b/grub-core/loader/macho.c
new file mode 100644
index 0000000..05710c4
--- /dev/null
+++ b/grub-core/loader/macho.c
@@ -0,0 +1,205 @@
+/* macho.c - load Mach-O files. */
+/*
+ * 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/>.
+ */
+
+/* This Mach-O loader is incomplete and can load only non-relocatable segments.
+ This is however enough to boot xnu (otool -l and Mach-O specs for more info).
+*/
+
+#include <grub/err.h>
+#include <grub/macho.h>
+#include <grub/machoload.h>
+#include <grub/file.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/i18n.h>
+#include <grub/dl.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+grub_err_t
+grub_macho_close (grub_macho_t macho)
+{
+ grub_file_t file = macho->file;
+
+ if (!macho->uncompressed32)
+ grub_free (macho->cmds32);
+ grub_free (macho->uncompressed32);
+ if (!macho->uncompressed64)
+ grub_free (macho->cmds64);
+ grub_free (macho->uncompressed64);
+
+ grub_free (macho);
+
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+grub_macho_t
+grub_macho_file (grub_file_t file, const char *filename, int is_64bit)
+{
+ grub_macho_t macho;
+ union grub_macho_filestart filestart;
+
+ macho = grub_malloc (sizeof (*macho));
+ if (! macho)
+ return 0;
+
+ macho->file = file;
+ macho->offset32 = -1;
+ macho->offset64 = -1;
+ macho->end32 = -1;
+ macho->end64 = -1;
+ macho->cmds32 = 0;
+ macho->cmds64 = 0;
+ macho->uncompressed32 = 0;
+ macho->uncompressed64 = 0;
+ macho->compressed32 = 0;
+ macho->compressed64 = 0;
+
+ if (grub_file_seek (macho->file, 0) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (macho->file, &filestart, sizeof (filestart))
+ != sizeof (filestart))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ goto fail;
+ }
+
+ /* Is it a fat file? */
+ if (filestart.fat.magic == grub_cpu_to_be32_compile_time (GRUB_MACHO_FAT_MAGIC))
+ {
+ struct grub_macho_fat_arch *archs;
+ int i, narchs;
+
+ /* Load architecture description. */
+ narchs = grub_be_to_cpu32 (filestart.fat.nfat_arch);
+ if (grub_file_seek (macho->file, sizeof (struct grub_macho_fat_header))
+ == (grub_off_t) -1)
+ goto fail;
+ archs = grub_calloc (narchs, sizeof (struct grub_macho_fat_arch));
+ if (!archs)
+ goto fail;
+ if (grub_file_read (macho->file, archs,
+ sizeof (struct grub_macho_fat_arch) * narchs)
+ != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
+ {
+ grub_free (archs);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ goto fail;
+ }
+
+ for (i = 0; i < narchs; i++)
+ {
+ if ((archs[i].cputype
+ == grub_cpu_to_be32_compile_time (GRUB_MACHO_CPUTYPE_IA32))
+ && !is_64bit)
+ {
+ macho->offset32 = grub_be_to_cpu32 (archs[i].offset);
+ macho->end32 = grub_be_to_cpu32 (archs[i].offset)
+ + grub_be_to_cpu32 (archs[i].size);
+ }
+ if ((archs[i].cputype
+ == grub_cpu_to_be32_compile_time (GRUB_MACHO_CPUTYPE_AMD64))
+ && is_64bit)
+ {
+ macho->offset64 = grub_be_to_cpu32 (archs[i].offset);
+ macho->end64 = grub_be_to_cpu32 (archs[i].offset)
+ + grub_be_to_cpu32 (archs[i].size);
+ }
+ }
+ grub_free (archs);
+ }
+
+ /* Is it a thin 32-bit file? */
+ if (filestart.thin32.magic == GRUB_MACHO_MAGIC32 && !is_64bit)
+ {
+ macho->offset32 = 0;
+ macho->end32 = grub_file_size (file);
+ }
+
+ /* Is it a thin 64-bit file? */
+ if (filestart.thin64.magic == GRUB_MACHO_MAGIC64 && is_64bit)
+ {
+ macho->offset64 = 0;
+ macho->end64 = grub_file_size (file);
+ }
+
+ if (grub_memcmp (filestart.lzss.magic, GRUB_MACHO_LZSS_MAGIC,
+ sizeof (filestart.lzss.magic)) == 0 && !is_64bit)
+ {
+ macho->offset32 = 0;
+ macho->end32 = grub_file_size (file);
+ }
+
+ /* Is it a thin 64-bit file? */
+ if (grub_memcmp (filestart.lzss.magic, GRUB_MACHO_LZSS_MAGIC,
+ sizeof (filestart.lzss.magic)) == 0 && is_64bit)
+ {
+ macho->offset64 = 0;
+ macho->end64 = grub_file_size (file);
+ }
+
+ grub_macho_parse32 (macho, filename);
+ grub_macho_parse64 (macho, filename);
+
+ if (macho->offset32 == -1 && !is_64bit)
+ {
+ grub_error (GRUB_ERR_BAD_OS,
+ "Mach-O doesn't contain suitable 32-bit architecture");
+ goto fail;
+ }
+
+ if (macho->offset64 == -1 && is_64bit)
+ {
+ grub_error (GRUB_ERR_BAD_OS,
+ "Mach-O doesn't contain suitable 64-bit architecture");
+ goto fail;
+ }
+
+ return macho;
+
+fail:
+ macho->file = 0;
+ grub_macho_close (macho);
+ return 0;
+}
+
+grub_macho_t
+grub_macho_open (const char *name, enum grub_file_type type, int is_64bit)
+{
+ grub_file_t file;
+ grub_macho_t macho;
+
+ file = grub_file_open (name, type);
+ if (! file)
+ return 0;
+
+ macho = grub_macho_file (file, name, is_64bit);
+ if (! macho)
+ grub_file_close (file);
+
+ return macho;
+}
diff --git a/grub-core/loader/macho32.c b/grub-core/loader/macho32.c
new file mode 100644
index 0000000..2de3a5c
--- /dev/null
+++ b/grub-core/loader/macho32.c
@@ -0,0 +1,22 @@
+#include <grub/macho.h>
+#include <grub/machoload.h>
+
+#define SUFFIX(x) x ## 32
+typedef struct grub_macho_header32 grub_macho_header_t;
+typedef struct grub_macho_segment32 grub_macho_segment_t;
+typedef grub_uint32_t grub_macho_addr_t;
+typedef struct grub_macho_thread32 grub_macho_thread_t;
+#define offsetXX offset32
+#define ncmdsXX ncmds32
+#define cmdsizeXX cmdsize32
+#define cmdsXX cmds32
+#define endXX end32
+#define uncompressedXX uncompressed32
+#define compressedXX compressed32
+#define uncompressed_sizeXX uncompressed_size32
+#define compressed_sizeXX compressed_size32
+#define XX "32"
+#define GRUB_MACHO_MAGIC GRUB_MACHO_MAGIC32
+#define GRUB_MACHO_CMD_SEGMENT GRUB_MACHO_CMD_SEGMENT32
+#include "machoXX.c"
+
diff --git a/grub-core/loader/macho64.c b/grub-core/loader/macho64.c
new file mode 100644
index 0000000..0affceb
--- /dev/null
+++ b/grub-core/loader/macho64.c
@@ -0,0 +1,22 @@
+#include <grub/macho.h>
+#include <grub/machoload.h>
+
+#define SUFFIX(x) x ## 64
+typedef struct grub_macho_header64 grub_macho_header_t;
+typedef struct grub_macho_segment64 grub_macho_segment_t;
+typedef grub_uint64_t grub_macho_addr_t;
+typedef struct grub_macho_thread64 grub_macho_thread_t;
+#define offsetXX offset64
+#define ncmdsXX ncmds64
+#define cmdsizeXX cmdsize64
+#define cmdsXX cmds64
+#define endXX end64
+#define uncompressedXX uncompressed64
+#define compressedXX compressed64
+#define uncompressed_sizeXX uncompressed_size64
+#define compressed_sizeXX compressed_size64
+#define XX "64"
+#define GRUB_MACHO_MAGIC GRUB_MACHO_MAGIC64
+#define GRUB_MACHO_CMD_SEGMENT GRUB_MACHO_CMD_SEGMENT64
+#include "machoXX.c"
+
diff --git a/grub-core/loader/machoXX.c b/grub-core/loader/machoXX.c
new file mode 100644
index 0000000..95c3fe5
--- /dev/null
+++ b/grub-core/loader/machoXX.c
@@ -0,0 +1,384 @@
+
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+static int
+SUFFIX (grub_macho_contains_macho) (grub_macho_t macho)
+{
+ return macho->offsetXX != -1;
+}
+
+void
+SUFFIX (grub_macho_parse) (grub_macho_t macho, const char *filename)
+{
+ union {
+ struct grub_macho_lzss_header lzss;
+ grub_macho_header_t macho;
+ } head;
+
+ /* Is there any candidate at all? */
+ if (macho->offsetXX == -1)
+ return;
+
+ /* Read header and check magic. */
+ if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1
+ || grub_file_read (macho->file, &head, sizeof (head))
+ != sizeof (head))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ macho->offsetXX = -1;
+ return;
+ }
+ if (grub_memcmp (head.lzss.magic, GRUB_MACHO_LZSS_MAGIC,
+ sizeof (head.lzss.magic)) == 0)
+ {
+ macho->compressed_sizeXX = grub_be_to_cpu32 (head.lzss.compressed_size);
+ macho->uncompressed_sizeXX
+ = grub_be_to_cpu32 (head.lzss.uncompressed_size);
+ if (macho->uncompressed_sizeXX < sizeof (head.macho))
+ {
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ macho->offsetXX = -1;
+ return;
+ }
+ /* Skip header check. */
+ macho->compressedXX = 1;
+ return;
+ }
+
+ if (head.macho.magic != GRUB_MACHO_MAGIC)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid Mach-O header");
+ macho->offsetXX = -1;
+ return;
+ }
+
+ /* Read commands. */
+ macho->ncmdsXX = head.macho.ncmds;
+ macho->cmdsizeXX = head.macho.sizeofcmds;
+ macho->cmdsXX = grub_malloc (macho->cmdsizeXX);
+ if (! macho->cmdsXX)
+ return;
+ if (grub_file_seek (macho->file, macho->offsetXX
+ + sizeof (grub_macho_header_t)) == (grub_off_t) -1
+ || grub_file_read (macho->file, macho->cmdsXX,
+ (grub_size_t) macho->cmdsizeXX)
+ != (grub_ssize_t) macho->cmdsizeXX)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ macho->offsetXX = -1;
+ }
+}
+
+typedef int (*grub_macho_iter_hook_t)
+(grub_macho_t , struct grub_macho_cmd *,
+ void *);
+
+static grub_err_t
+grub_macho_cmds_iterate (grub_macho_t macho,
+ grub_macho_iter_hook_t hook,
+ void *hook_arg,
+ const char *filename)
+{
+ grub_uint8_t *hdrs;
+ int i;
+
+ if (macho->compressedXX && !macho->uncompressedXX)
+ {
+ grub_uint8_t *tmp;
+ grub_macho_header_t *head;
+ macho->uncompressedXX = grub_malloc (macho->uncompressed_sizeXX);
+ if (!macho->uncompressedXX)
+ return grub_errno;
+ tmp = grub_malloc (macho->compressed_sizeXX);
+ if (!tmp)
+ {
+ grub_free (macho->uncompressedXX);
+ macho->uncompressedXX = 0;
+ return grub_errno;
+ }
+ if (grub_file_seek (macho->file, macho->offsetXX
+ + GRUB_MACHO_LZSS_OFFSET) == (grub_off_t) -1
+ || grub_file_read (macho->file, tmp,
+ (grub_size_t) macho->compressed_sizeXX)
+ != (grub_ssize_t) macho->compressed_sizeXX)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ grub_free (tmp);
+ grub_free (macho->uncompressedXX);
+ macho->uncompressedXX = 0;
+ macho->offsetXX = -1;
+ return grub_errno;
+ }
+ if (grub_decompress_lzss (macho->uncompressedXX,
+ macho->uncompressedXX
+ + macho->uncompressed_sizeXX,
+ tmp, tmp + macho->compressed_sizeXX)
+ != macho->uncompressed_sizeXX)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ grub_free (tmp);
+ grub_free (macho->uncompressedXX);
+ macho->uncompressedXX = 0;
+ macho->offsetXX = -1;
+ return grub_errno;
+ }
+ grub_free (tmp);
+ head = (grub_macho_header_t *) macho->uncompressedXX;
+ macho->ncmdsXX = head->ncmds;
+ macho->cmdsizeXX = head->sizeofcmds;
+ macho->cmdsXX = macho->uncompressedXX + sizeof (grub_macho_header_t);
+ if (sizeof (grub_macho_header_t) + macho->cmdsizeXX
+ >= macho->uncompressed_sizeXX)
+ {
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ grub_free (macho->uncompressedXX);
+ macho->uncompressedXX = 0;
+ macho->offsetXX = -1;
+ return grub_errno;
+ }
+ }
+
+ if (! macho->cmdsXX)
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't find Mach-O commands");
+ hdrs = macho->cmdsXX;
+ for (i = 0; i < macho->ncmdsXX; i++)
+ {
+ struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
+ if (hook (macho, hdr, hook_arg))
+ break;
+ hdrs += hdr->cmdsize;
+ }
+
+ return grub_errno;
+}
+
+grub_size_t
+SUFFIX (grub_macho_filesize) (grub_macho_t macho)
+{
+ if (SUFFIX (grub_macho_contains_macho) (macho))
+ return macho->endXX - macho->offsetXX;
+ return 0;
+}
+
+grub_err_t
+SUFFIX (grub_macho_readfile) (grub_macho_t macho,
+ const char *filename,
+ void *dest)
+{
+ grub_ssize_t read;
+ if (! SUFFIX (grub_macho_contains_macho) (macho))
+ return grub_error (GRUB_ERR_BAD_OS,
+ "couldn't read architecture-specific part");
+
+ if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1)
+ return grub_errno;
+
+ read = grub_file_read (macho->file, dest,
+ macho->endXX - macho->offsetXX);
+ if (read != (grub_ssize_t) (macho->endXX - macho->offsetXX))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+ return grub_errno;
+ }
+ return GRUB_ERR_NONE;
+}
+
+struct calcsize_ctx
+{
+ int flags;
+ int nr_phdrs;
+ grub_macho_addr_t *segments_start;
+ grub_macho_addr_t *segments_end;
+};
+
+/* Run through the program headers to calculate the total memory size we
+ should claim. */
+static int
+calcsize (grub_macho_t _macho __attribute__ ((unused)),
+ struct grub_macho_cmd *hdr0,
+ void *_arg)
+{
+ grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
+ struct calcsize_ctx *ctx = _arg;
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
+ return 0;
+
+ if (! hdr->vmsize)
+ return 0;
+
+ if (! hdr->filesize && (ctx->flags & GRUB_MACHO_NOBSS))
+ return 0;
+
+ ctx->nr_phdrs++;
+ if (hdr->vmaddr < *ctx->segments_start)
+ *ctx->segments_start = hdr->vmaddr;
+ if (hdr->vmaddr + hdr->vmsize > *ctx->segments_end)
+ *ctx->segments_end = hdr->vmaddr + hdr->vmsize;
+ return 0;
+}
+
+/* Calculate the amount of memory spanned by the segments. */
+grub_err_t
+SUFFIX (grub_macho_size) (grub_macho_t macho, grub_macho_addr_t *segments_start,
+ grub_macho_addr_t *segments_end, int flags,
+ const char *filename)
+{
+ struct calcsize_ctx ctx = {
+ .flags = flags,
+ .nr_phdrs = 0,
+ .segments_start = segments_start,
+ .segments_end = segments_end,
+ };
+
+ *segments_start = (grub_macho_addr_t) -1;
+ *segments_end = 0;
+
+ grub_macho_cmds_iterate (macho, calcsize, &ctx, filename);
+
+ if (ctx.nr_phdrs == 0)
+ return grub_error (GRUB_ERR_BAD_OS, "no program headers present");
+
+ if (*segments_end < *segments_start)
+ /* Very bad addresses. */
+ return grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
+
+ return GRUB_ERR_NONE;
+}
+
+struct do_load_ctx
+{
+ int flags;
+ char *offset;
+ const char *filename;
+ int *darwin_version;
+};
+
+static int
+do_load(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr0,
+ void *_arg)
+{
+ grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
+ struct do_load_ctx *ctx = _arg;
+
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
+ return 0;
+
+ if (! hdr->filesize && (ctx->flags & GRUB_MACHO_NOBSS))
+ return 0;
+ if (! hdr->vmsize)
+ return 0;
+
+ if (hdr->filesize)
+ {
+ grub_ssize_t read, toread = min (hdr->filesize, hdr->vmsize);
+ if (_macho->uncompressedXX)
+ {
+ if (hdr->fileoff + (grub_size_t) toread
+ > _macho->uncompressed_sizeXX)
+ read = -1;
+ else
+ {
+ read = toread;
+ grub_memcpy (ctx->offset + hdr->vmaddr,
+ _macho->uncompressedXX + hdr->fileoff, read);
+ }
+ }
+ else
+ {
+ if (grub_file_seek (_macho->file, hdr->fileoff
+ + _macho->offsetXX) == (grub_off_t) -1)
+ return 1;
+ read = grub_file_read (_macho->file, ctx->offset + hdr->vmaddr,
+ toread);
+ }
+
+ if (read != toread)
+ {
+ /* XXX How can we free memory from `load_hook'? */
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ ctx->filename);
+
+ return 1;
+ }
+ if (ctx->darwin_version)
+ {
+ const char *ptr = ctx->offset + hdr->vmaddr;
+ const char *end = ptr + min (hdr->filesize, hdr->vmsize)
+ - (sizeof ("Darwin Kernel Version ") - 1);
+ for (; ptr < end; ptr++)
+ if (grub_memcmp (ptr, "Darwin Kernel Version ",
+ sizeof ("Darwin Kernel Version ") - 1) == 0)
+ {
+ ptr += sizeof ("Darwin Kernel Version ") - 1;
+ *ctx->darwin_version = 0;
+ end += (sizeof ("Darwin Kernel Version ") - 1);
+ while (ptr < end && grub_isdigit (*ptr))
+ *ctx->darwin_version = (*ptr++ - '0') + *ctx->darwin_version * 10;
+ break;
+ }
+ }
+ }
+
+ if (hdr->filesize < hdr->vmsize)
+ grub_memset (ctx->offset + hdr->vmaddr + hdr->filesize,
+ 0, hdr->vmsize - hdr->filesize);
+ return 0;
+}
+
+/* Load every loadable segment into memory specified by `_load_hook'. */
+grub_err_t
+SUFFIX (grub_macho_load) (grub_macho_t macho, const char *filename,
+ char *offset, int flags, int *darwin_version)
+{
+ struct do_load_ctx ctx = {
+ .flags = flags,
+ .offset = offset,
+ .filename = filename,
+ .darwin_version = darwin_version
+ };
+
+ if (darwin_version)
+ *darwin_version = 0;
+
+ grub_macho_cmds_iterate (macho, do_load, &ctx, filename);
+
+ return grub_errno;
+}
+
+static int
+find_entry_point (grub_macho_t _macho __attribute__ ((unused)),
+ struct grub_macho_cmd *hdr,
+ void *_arg)
+{
+ grub_macho_addr_t *entry_point = _arg;
+ if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
+ *entry_point = ((grub_macho_thread_t *) hdr)->entry_point;
+ return 0;
+}
+
+grub_macho_addr_t
+SUFFIX (grub_macho_get_entry_point) (grub_macho_t macho, const char *filename)
+{
+ grub_macho_addr_t entry_point = 0;
+ grub_macho_cmds_iterate (macho, find_entry_point, &entry_point, filename);
+ return entry_point;
+}
diff --git a/grub-core/loader/mips/linux.c b/grub-core/loader/mips/linux.c
new file mode 100644
index 0000000..e4ed959
--- /dev/null
+++ b/grub-core/loader/mips/linux.c
@@ -0,0 +1,508 @@
+/* linux.c - boot Linux */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,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/elf.h>
+#include <grub/elfload.h>
+#include <grub/loader.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/command.h>
+#include <grub/mips/relocator.h>
+#include <grub/memory.h>
+#include <grub/i18n.h>
+#include <grub/lib/cmdline.h>
+#include <grub/linux.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+/* For frequencies. */
+#include <grub/machine/time.h>
+
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+#include <grub/pci.h>
+#include <grub/machine/kernel.h>
+
+const char loongson_machtypes[][60] =
+ {
+ [GRUB_ARCH_MACHINE_YEELOONG] = "machtype=lemote-yeeloong-2f-8.9inches",
+ [GRUB_ARCH_MACHINE_FULOONG2F] = "machtype=lemote-fuloong-2f-box",
+ [GRUB_ARCH_MACHINE_FULOONG2E] = "machtype=lemote-fuloong-2e-unknown"
+ };
+#endif
+
+static grub_dl_t my_mod;
+
+static int loaded;
+
+static grub_size_t linux_size;
+
+static struct grub_relocator *relocator;
+static grub_uint8_t *playground;
+static grub_addr_t target_addr, entry_addr;
+#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
+static char *params;
+#else
+static int linux_argc;
+static grub_off_t argv_off;
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+static grub_off_t envp_off;
+#endif
+static grub_off_t rd_addr_arg_off, rd_size_arg_off;
+#endif
+static int initrd_loaded = 0;
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ struct grub_relocator32_state state;
+
+ grub_memset (&state, 0, sizeof (state));
+
+ /* Boot the kernel. */
+ state.gpr[1] = entry_addr;
+
+#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
+ {
+ grub_err_t err;
+ grub_relocator_chunk_t ch;
+ grub_uint32_t *memsize;
+ grub_uint32_t *magic;
+ char *str;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ ((16 << 20) - 264),
+ grub_strlen (params) + 1 + 8);
+ if (err)
+ return err;
+ memsize = get_virtual_current_address (ch);
+ magic = memsize + 1;
+ *memsize = grub_mmap_get_lower ();
+ *magic = 0x12345678;
+ str = (char *) (magic + 1);
+ grub_strcpy (str, params);
+ }
+#endif
+
+#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
+ state.gpr[4] = linux_argc;
+ state.gpr[5] = target_addr + argv_off;
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+ state.gpr[6] = target_addr + envp_off;
+#else
+ state.gpr[6] = 0;
+#endif
+ state.gpr[7] = 0;
+#endif
+ state.jumpreg = 1;
+ grub_relocator32_boot (relocator, state);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ grub_relocator_unload (relocator);
+ grub_dl_unref (my_mod);
+
+#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
+ grub_free (params);
+ params = 0;
+#endif
+
+ loaded = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_load32 (grub_elf_t elf, const char *filename,
+ void **extra_mem, grub_size_t extra_size)
+{
+ Elf32_Addr base;
+ int extraoff;
+ grub_err_t err;
+
+ /* Linux's entry point incorrectly contains a virtual address. */
+ entry_addr = elf->ehdr.ehdr32.e_entry;
+
+ linux_size = grub_elf32_size (elf, &base, 0);
+ if (linux_size == 0)
+ return grub_errno;
+ target_addr = base;
+ /* Pad it; the kernel scribbles over memory beyond its load address. */
+ linux_size += 0x100000;
+ linux_size = ALIGN_UP (base + linux_size, 4) - base;
+ extraoff = linux_size;
+ linux_size += extra_size;
+
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ return grub_errno;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ target_addr & 0x1fffffff,
+ linux_size);
+ if (err)
+ return err;
+ playground = get_virtual_current_address (ch);
+ }
+
+ *extra_mem = playground + extraoff;
+
+ /* Now load the segments into the area we claimed. */
+ return grub_elf32_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
+}
+
+static grub_err_t
+grub_linux_load64 (grub_elf_t elf, const char *filename,
+ void **extra_mem, grub_size_t extra_size)
+{
+ Elf64_Addr base;
+ int extraoff;
+ grub_err_t err;
+
+ /* Linux's entry point incorrectly contains a virtual address. */
+ entry_addr = elf->ehdr.ehdr64.e_entry;
+
+ linux_size = grub_elf64_size (elf, &base, 0);
+ if (linux_size == 0)
+ return grub_errno;
+ target_addr = base;
+ /* Pad it; the kernel scribbles over memory beyond its load address. */
+ linux_size += 0x100000;
+ linux_size = ALIGN_UP (base + linux_size, 4) - base;
+ extraoff = linux_size;
+ linux_size += extra_size;
+
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ return grub_errno;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ target_addr & 0x1fffffff,
+ linux_size);
+ if (err)
+ return err;
+ playground = get_virtual_current_address (ch);
+ }
+
+ *extra_mem = playground + extraoff;
+
+ /* Now load the segments into the area we claimed. */
+ return grub_elf64_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_elf_t elf = 0;
+ int size;
+ void *extra = NULL;
+#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
+ int i;
+ grub_uint32_t *linux_argv;
+ char *linux_args;
+#endif
+ grub_err_t err;
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+ char *linux_envs;
+ grub_uint32_t *linux_envp;
+#endif
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ elf = grub_elf_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
+ if (! elf)
+ return grub_errno;
+
+ if (elf->ehdr.ehdr32.e_type != ET_EXEC)
+ {
+ grub_elf_close (elf);
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ N_("this ELF file is not of the right type"));
+ }
+
+ /* Release the previously used memory. */
+ grub_loader_unset ();
+ loaded = 0;
+
+#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
+ size = 0;
+#else
+ /* For arguments. */
+ linux_argc = argc;
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+ linux_argc++;
+#endif
+ /* Main arguments. */
+ size = (linux_argc) * sizeof (grub_uint32_t);
+ /* Initrd address and size. */
+ size += 2 * sizeof (grub_uint32_t);
+ /* NULL terminator. */
+ size += sizeof (grub_uint32_t);
+
+ /* First argument is always "a0". */
+ size += ALIGN_UP (sizeof ("a0"), 4);
+ /* Normal arguments. */
+ for (i = 1; i < argc; i++)
+ size += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+ size += ALIGN_UP (sizeof (loongson_machtypes[0]), 4);
+#endif
+
+ /* rd arguments. */
+ size += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
+ size += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
+
+ /* For the environment. */
+ size += sizeof (grub_uint32_t);
+ size += 4 * sizeof (grub_uint32_t);
+ size += ALIGN_UP (sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"), 4)
+ + ALIGN_UP (sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"), 4)
+ + ALIGN_UP (sizeof ("busclock=XXXXXXXXXX"), 4)
+ + ALIGN_UP (sizeof ("cpuclock=XXXXXXXXXX"), 4);
+#endif
+
+ if (grub_elf_is_elf32 (elf))
+ err = grub_linux_load32 (elf, argv[0], &extra, size);
+ else
+ if (grub_elf_is_elf64 (elf))
+ err = grub_linux_load64 (elf, argv[0], &extra, size);
+ else
+ err = grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
+
+ grub_elf_close (elf);
+
+ if (err)
+ return err;
+
+#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
+ /* Create kernel command line. */
+ size = grub_loader_cmdline_size(argc, argv);
+ params = grub_malloc (size + sizeof (LINUX_IMAGE));
+ if (! params)
+ {
+ grub_linux_unload ();
+ return grub_errno;
+ }
+
+ grub_memcpy (params, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+ grub_create_loader_cmdline (argc, argv, params + sizeof (LINUX_IMAGE) - 1,
+ size, GRUB_VERIFY_KERNEL_CMDLINE);
+#else
+ linux_argv = extra;
+ argv_off = (grub_uint8_t *) linux_argv - (grub_uint8_t *) playground;
+ extra = linux_argv + (linux_argc + 1 + 2);
+ linux_args = extra;
+
+ grub_memcpy (linux_args, "a0", sizeof ("a0"));
+ *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
+ + target_addr;
+ linux_argv++;
+ linux_args += ALIGN_UP (sizeof ("a0"), 4);
+
+ char *params = linux_args;
+
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+ {
+ unsigned mtype = grub_arch_machine;
+ if (mtype >= ARRAY_SIZE (loongson_machtypes))
+ mtype = 0;
+ /* In Loongson platform, it is the responsibility of the bootloader/firmware
+ to supply the OS kernel with machine type information. */
+ grub_memcpy (linux_args, loongson_machtypes[mtype],
+ sizeof (loongson_machtypes[mtype]));
+ *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
+ + target_addr;
+ linux_argv++;
+ linux_args += ALIGN_UP (sizeof (loongson_machtypes[mtype]), 4);
+ }
+#endif
+
+ for (i = 1; i < argc; i++)
+ {
+ grub_memcpy (linux_args, argv[i], grub_strlen (argv[i]) + 1);
+ *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
+ + target_addr;
+ linux_argv++;
+ linux_args += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
+ }
+
+ *linux_args = '\0';
+
+ err = grub_verify_string (params, GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ return err;
+
+ /* Reserve space for rd arguments. */
+ rd_addr_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
+ linux_args += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
+ *linux_argv = 0;
+ linux_argv++;
+
+ rd_size_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
+ linux_args += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
+ *linux_argv = 0;
+ linux_argv++;
+
+ *linux_argv = 0;
+
+ extra = linux_args;
+
+#ifdef GRUB_MACHINE_MIPS_LOONGSON
+ linux_envp = extra;
+ envp_off = (grub_uint8_t *) linux_envp - (grub_uint8_t *) playground;
+ linux_envs = (char *) (linux_envp + 5);
+ grub_snprintf (linux_envs, sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"),
+ "memsize=%lld",
+ (unsigned long long) grub_mmap_get_lower () >> 20);
+ linux_envp[0] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
+ + target_addr;
+ linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
+ grub_snprintf (linux_envs, sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"),
+ "highmemsize=%lld",
+ (unsigned long long) grub_mmap_get_upper () >> 20);
+ linux_envp[1] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
+ + target_addr;
+ linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
+
+ grub_snprintf (linux_envs, sizeof ("busclock=XXXXXXXXXX"),
+ "busclock=%d", grub_arch_busclock);
+ linux_envp[2] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
+ + target_addr;
+ linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
+ grub_snprintf (linux_envs, sizeof ("cpuclock=XXXXXXXXXX"),
+ "cpuclock=%d", grub_arch_cpuclock);
+ linux_envp[3] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
+ + target_addr;
+ linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
+
+ linux_envp[4] = 0;
+#endif
+#endif
+
+ grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
+ initrd_loaded = 0;
+ loaded = 1;
+ grub_dl_ref (my_mod);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_size_t size = 0;
+ void *initrd_src;
+ grub_addr_t initrd_dest;
+ grub_err_t err;
+ struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ if (!loaded)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
+
+ if (initrd_loaded)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one initrd command can be issued.");
+
+ 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, (target_addr & 0x1fffffff) +
+ linux_size + 0x10000, 0x10000000, size,
+ 0x10000, GRUB_RELOCATOR_PREFERENCE_NONE, 0);
+
+ if (err)
+ goto fail;
+ initrd_src = get_virtual_current_address (ch);
+ initrd_dest = get_physical_target_address (ch) | 0x80000000;
+ }
+
+ if (grub_initrd_load (&initrd_ctx, argv, initrd_src))
+ goto fail;
+
+#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
+ {
+ char *tmp;
+ tmp = grub_xasprintf ("%s rd_start=0x%" PRIxGRUB_ADDR
+ " rd_size=0x%" PRIxGRUB_ADDR, params,
+ initrd_dest, size);
+ if (!tmp)
+ goto fail;
+ grub_free (params);
+ params = tmp;
+ }
+#else
+ grub_snprintf ((char *) playground + rd_addr_arg_off,
+ sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), "rd_start=0x%llx",
+ (unsigned long long) initrd_dest);
+ ((grub_uint32_t *) (playground + argv_off))[linux_argc]
+ = target_addr + rd_addr_arg_off;
+ linux_argc++;
+
+ grub_snprintf ((char *) playground + rd_size_arg_off,
+ sizeof ("rd_size=0xXXXXXXXXXXXXXXXXX"), "rd_size=0x%llx",
+ (unsigned long long) size);
+ ((grub_uint32_t *) (playground + argv_off))[linux_argc]
+ = target_addr + rd_size_arg_off;
+ linux_argc++;
+#endif
+
+ initrd_loaded = 1;
+
+ 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/multiboot.c b/grub-core/loader/multiboot.c
new file mode 100644
index 0000000..facb13f
--- /dev/null
+++ b/grub-core/loader/multiboot.c
@@ -0,0 +1,469 @@
+/* multiboot.c - boot a multiboot OS image. */
+/*
+ * 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/>.
+ */
+
+/*
+ * FIXME: The following features from the Multiboot specification still
+ * need to be implemented:
+ * - drives table
+ * - ROM configuration table
+ * - SMBIOS tables
+ * - Networking information
+ */
+
+#include <grub/loader.h>
+#include <grub/command.h>
+#ifdef GRUB_USE_MULTIBOOT2
+#include <grub/multiboot2.h>
+#define GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER GRUB_MULTIBOOT2_CONSOLE_FRAMEBUFFER
+#define GRUB_MULTIBOOT_CONSOLE_EGA_TEXT GRUB_MULTIBOOT2_CONSOLE_EGA_TEXT
+#define GRUB_MULTIBOOT(x) grub_multiboot2_ ## x
+#else
+#include <grub/multiboot.h>
+#define GRUB_MULTIBOOT(x) grub_multiboot_ ## x
+#endif
+#include <grub/cpu/multiboot.h>
+#include <grub/elf.h>
+#include <grub/aout.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/cpu/relocator.h>
+#include <grub/video.h>
+#include <grub/memory.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#endif
+
+struct grub_relocator *GRUB_MULTIBOOT (relocator) = NULL;
+grub_uint32_t GRUB_MULTIBOOT (payload_eip);
+#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
+#define DEFAULT_VIDEO_MODE "text"
+#else
+#define DEFAULT_VIDEO_MODE "auto"
+#endif
+
+static int accepts_video;
+static int accepts_ega_text;
+static int console_required;
+static grub_dl_t my_mod;
+
+
+/* Helper for grub_get_multiboot_mmap_count. */
+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;
+}
+
+/* Return the length of the Multiboot mmap that will be needed to allocate
+ our platform's map. */
+grub_uint32_t
+GRUB_MULTIBOOT (get_mmap_count) (void)
+{
+ grub_size_t count = 0;
+
+ grub_mmap_iterate (count_hook, &count);
+
+ return count;
+}
+
+grub_err_t
+GRUB_MULTIBOOT (set_video_mode) (void)
+{
+ grub_err_t err;
+ const char *modevar;
+
+#if GRUB_MACHINE_HAS_VGA_TEXT
+ if (accepts_video)
+#endif
+ {
+ modevar = grub_env_get ("gfxpayload");
+ if (! modevar || *modevar == 0)
+ err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
+ else
+ {
+ char *tmp;
+ tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
+ if (! tmp)
+ return grub_errno;
+ err = grub_video_set_mode (tmp, 0, 0);
+ grub_free (tmp);
+ }
+ }
+#if GRUB_MACHINE_HAS_VGA_TEXT
+ else
+ err = grub_video_set_mode ("text", 0, 0);
+#endif
+
+ return err;
+}
+
+#ifdef GRUB_MACHINE_EFI
+#ifdef __x86_64__
+#define grub_relocator_efi_boot grub_relocator64_efi_boot
+#define grub_relocator_efi_state grub_relocator64_efi_state
+#endif
+#endif
+
+#ifdef grub_relocator_efi_boot
+static void
+efi_boot (struct grub_relocator *rel,
+ grub_uint32_t target)
+{
+#ifdef GRUB_USE_MULTIBOOT2
+ struct grub_relocator_efi_state state_efi = MULTIBOOT2_EFI_INITIAL_STATE;
+#else
+ struct grub_relocator_efi_state state_efi = MULTIBOOT_EFI_INITIAL_STATE;
+#endif
+ state_efi.MULTIBOOT_EFI_ENTRY_REGISTER = GRUB_MULTIBOOT (payload_eip);
+ state_efi.MULTIBOOT_EFI_MBI_REGISTER = target;
+
+ grub_relocator_efi_boot (rel, state_efi);
+}
+#else
+#define grub_efi_is_finished 1
+static void
+efi_boot (struct grub_relocator *rel __attribute__ ((unused)),
+ grub_uint32_t target __attribute__ ((unused)))
+{
+}
+#endif
+
+#if defined (__i386__) || defined (__x86_64__)
+static void
+normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state)
+{
+ grub_relocator32_boot (rel, state, 0);
+}
+#else
+static void
+normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state)
+{
+ grub_relocator32_boot (rel, state);
+}
+#endif
+
+static grub_err_t
+grub_multiboot_boot (void)
+{
+ grub_err_t err;
+
+#ifdef GRUB_USE_MULTIBOOT2
+ struct grub_relocator32_state state = MULTIBOOT2_INITIAL_STATE;
+#else
+ struct grub_relocator32_state state = MULTIBOOT_INITIAL_STATE;
+#endif
+ state.MULTIBOOT_ENTRY_REGISTER = GRUB_MULTIBOOT (payload_eip);
+
+ err = GRUB_MULTIBOOT (make_mbi) (&state.MULTIBOOT_MBI_REGISTER);
+
+ if (err)
+ return err;
+
+ if (grub_efi_is_finished)
+ normal_boot (GRUB_MULTIBOOT (relocator), state);
+ else
+ efi_boot (GRUB_MULTIBOOT (relocator), state.MULTIBOOT_MBI_REGISTER);
+
+ /* Not reached. */
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_multiboot_unload (void)
+{
+ GRUB_MULTIBOOT (free_mbi) ();
+
+ grub_relocator_unload (GRUB_MULTIBOOT (relocator));
+ GRUB_MULTIBOOT (relocator) = NULL;
+
+ grub_dl_unref (my_mod);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_uint64_t highest_load;
+
+#define MULTIBOOT_LOAD_ELF64
+#include "multiboot_elfxx.c"
+#undef MULTIBOOT_LOAD_ELF64
+
+#define MULTIBOOT_LOAD_ELF32
+#include "multiboot_elfxx.c"
+#undef MULTIBOOT_LOAD_ELF32
+
+/* Load ELF32 or ELF64. */
+grub_err_t
+GRUB_MULTIBOOT (load_elf) (mbi_load_data_t *mld)
+{
+ if (grub_multiboot_is_elf32 (mld->buffer))
+ return grub_multiboot_load_elf32 (mld);
+ else if (grub_multiboot_is_elf64 (mld->buffer))
+ return grub_multiboot_load_elf64 (mld);
+
+ return grub_error (GRUB_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic"));
+}
+
+grub_err_t
+GRUB_MULTIBOOT (set_console) (int console_type, int accepted_consoles,
+ int width, int height, int depth,
+ int console_req)
+{
+ console_required = console_req;
+ if (!(accepted_consoles
+ & (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER
+ | (GRUB_MACHINE_HAS_VGA_TEXT ? GRUB_MULTIBOOT_CONSOLE_EGA_TEXT : 0))))
+ {
+ if (console_required)
+ return grub_error (GRUB_ERR_BAD_OS,
+ "OS requires a console but none is available");
+ grub_puts_ (N_("WARNING: no console will be available to OS"));
+ accepts_video = 0;
+ accepts_ega_text = 0;
+ return GRUB_ERR_NONE;
+ }
+
+ if (console_type == GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER)
+ {
+ char *buf;
+ if (depth && width && height)
+ buf = grub_xasprintf ("%dx%dx%d,%dx%d,auto", width,
+ height, depth, width, height);
+ else if (width && height)
+ buf = grub_xasprintf ("%dx%d,auto", width, height);
+ else
+ buf = grub_strdup ("auto");
+
+ if (!buf)
+ return grub_errno;
+ grub_env_set ("gfxpayload", buf);
+ grub_free (buf);
+ }
+ else
+ {
+#if GRUB_MACHINE_HAS_VGA_TEXT
+ grub_env_set ("gfxpayload", "text");
+#else
+ /* Always use video if no VGA text is available. */
+ grub_env_set ("gfxpayload", "auto");
+#endif
+ }
+
+ accepts_video = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER);
+ accepts_ega_text = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_EGA_TEXT);
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_multiboot (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_err_t err;
+
+ grub_loader_unset ();
+
+ highest_load = 0;
+
+#ifndef GRUB_USE_MULTIBOOT2
+ grub_multiboot_quirks = GRUB_MULTIBOOT_QUIRKS_NONE;
+ int option_found = 0;
+
+ do
+ {
+ option_found = 0;
+ if (argc != 0 && grub_strcmp (argv[0], "--quirk-bad-kludge") == 0)
+ {
+ argc--;
+ argv++;
+ option_found = 1;
+ grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_BAD_KLUDGE;
+ }
+
+ if (argc != 0 && grub_strcmp (argv[0], "--quirk-modules-after-kernel") == 0)
+ {
+ argc--;
+ argv++;
+ option_found = 1;
+ grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL;
+ }
+ } while (option_found);
+#endif
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_MULTIBOOT_KERNEL);
+ if (! file)
+ return grub_errno;
+
+ grub_dl_ref (my_mod);
+
+ /* Skip filename. */
+ GRUB_MULTIBOOT (init_mbi) (argc - 1, argv + 1);
+
+ grub_relocator_unload (GRUB_MULTIBOOT (relocator));
+ GRUB_MULTIBOOT (relocator) = grub_relocator_new ();
+
+ if (!GRUB_MULTIBOOT (relocator))
+ goto fail;
+
+ err = GRUB_MULTIBOOT (load) (file, argv[0]);
+ if (err)
+ goto fail;
+
+ GRUB_MULTIBOOT (set_bootdev) ();
+
+ grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 0);
+
+ fail:
+ if (file)
+ grub_file_close (file);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_relocator_unload (GRUB_MULTIBOOT (relocator));
+ GRUB_MULTIBOOT (relocator) = NULL;
+ grub_dl_unref (my_mod);
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_ssize_t size;
+ void *module = NULL;
+ grub_addr_t target;
+ grub_err_t err;
+ int nounzip = 0;
+ grub_uint64_t lowest_addr = 0;
+
+ 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 (!GRUB_MULTIBOOT (relocator))
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("you need to load the kernel first"));
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_MULTIBOOT_MODULE
+ | (nounzip ? GRUB_FILE_TYPE_NO_DECOMPRESS : GRUB_FILE_TYPE_NONE));
+ if (! file)
+ return grub_errno;
+
+#ifndef GRUB_USE_MULTIBOOT2
+ lowest_addr = 0x100000;
+ if (grub_multiboot_quirks & GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL)
+ lowest_addr = ALIGN_UP (highest_load + 1048576, 4096);
+#endif
+
+ size = grub_file_size (file);
+ if (size)
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch,
+ lowest_addr, UP_TO_TOP32 (size),
+ size, MULTIBOOT_MOD_ALIGN,
+ GRUB_RELOCATOR_PREFERENCE_NONE, 1);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+ module = get_virtual_current_address (ch);
+ target = get_physical_target_address (ch);
+ }
+ else
+ {
+ module = 0;
+ target = 0;
+ }
+
+ err = GRUB_MULTIBOOT (add_module) (target, size, argc - 1, argv + 1);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+
+ if (size && grub_file_read (file, module, size) != size)
+ {
+ grub_file_close (file);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
+ argv[0]);
+ return grub_errno;
+ }
+
+ grub_file_close (file);
+ return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd_multiboot, cmd_module;
+
+GRUB_MOD_INIT(multiboot)
+{
+ cmd_multiboot =
+#ifdef GRUB_USE_MULTIBOOT2
+ grub_register_command ("multiboot2", grub_cmd_multiboot,
+ 0, N_("Load a multiboot 2 kernel."));
+ cmd_module =
+ grub_register_command ("module2", grub_cmd_module,
+ 0, N_("Load a multiboot 2 module."));
+#else
+ grub_register_command ("multiboot", grub_cmd_multiboot,
+ 0, N_("Load a multiboot kernel."));
+ cmd_module =
+ grub_register_command ("module", grub_cmd_module,
+ 0, N_("Load a multiboot module."));
+#endif
+
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(multiboot)
+{
+ grub_unregister_command (cmd_multiboot);
+ grub_unregister_command (cmd_module);
+}
diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c
new file mode 100644
index 0000000..f2318e0
--- /dev/null
+++ b/grub-core/loader/multiboot_elfxx.c
@@ -0,0 +1,298 @@
+/*
+ * 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/>.
+ */
+
+#if defined(MULTIBOOT_LOAD_ELF32)
+# define XX 32
+# define E_MACHINE MULTIBOOT_ELF32_MACHINE
+# define ELFCLASSXX ELFCLASS32
+# define Elf_Ehdr Elf32_Ehdr
+# define Elf_Phdr Elf32_Phdr
+# define Elf_Shdr Elf32_Shdr
+#elif defined(MULTIBOOT_LOAD_ELF64)
+# define XX 64
+# define E_MACHINE MULTIBOOT_ELF64_MACHINE
+# define ELFCLASSXX ELFCLASS64
+# define Elf_Ehdr Elf64_Ehdr
+# define Elf_Phdr Elf64_Phdr
+# define Elf_Shdr Elf64_Shdr
+#else
+#error "I'm confused"
+#endif
+
+#include <grub/i386/relocator.h>
+
+#define CONCAT(a,b) CONCAT_(a, b)
+#define CONCAT_(a,b) a ## b
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+/* Check if BUFFER contains ELF32 (or ELF64). */
+static int
+CONCAT(grub_multiboot_is_elf, XX) (void *buffer)
+{
+ Elf_Ehdr *ehdr = (Elf_Ehdr *) buffer;
+
+ return ehdr->e_ident[EI_CLASS] == ELFCLASSXX;
+}
+
+static grub_err_t
+CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
+{
+ Elf_Ehdr *ehdr = (Elf_Ehdr *) mld->buffer;
+ char *phdr_base;
+ grub_err_t err;
+ grub_relocator_chunk_t ch;
+ grub_uint32_t load_offset = 0, load_size;
+ int i;
+ void *source = NULL;
+
+ if (ehdr->e_ident[EI_MAG0] != ELFMAG0
+ || ehdr->e_ident[EI_MAG1] != ELFMAG1
+ || ehdr->e_ident[EI_MAG2] != ELFMAG2
+ || ehdr->e_ident[EI_MAG3] != ELFMAG3
+ || ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
+ return grub_error(GRUB_ERR_UNKNOWN_OS, N_("invalid arch-independent ELF magic"));
+
+ if (ehdr->e_ident[EI_CLASS] != ELFCLASSXX || ehdr->e_machine != E_MACHINE
+ || ehdr->e_version != EV_CURRENT)
+ return grub_error (GRUB_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic"));
+
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ return grub_error (GRUB_ERR_UNKNOWN_OS, N_("this ELF file is not of the right type"));
+
+ /* FIXME: Should we support program headers at strange locations? */
+ if (ehdr->e_phoff + (grub_uint32_t) ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
+ return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
+
+ phdr_base = (char *) mld->buffer + ehdr->e_phoff;
+#define phdr(i) ((Elf_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))
+
+ mld->link_base_addr = ~0;
+
+ /* Calculate lowest and highest load address. */
+ for (i = 0; i < ehdr->e_phnum; i++)
+ if (phdr(i)->p_type == PT_LOAD)
+ {
+ mld->link_base_addr = grub_min (mld->link_base_addr, phdr(i)->p_paddr);
+ highest_load = grub_max (highest_load, phdr(i)->p_paddr + phdr(i)->p_memsz);
+ }
+
+#ifdef MULTIBOOT_LOAD_ELF64
+ if (highest_load >= 0x100000000)
+ return grub_error (GRUB_ERR_BAD_OS, "segment crosses 4 GiB border");
+#endif
+
+ if (mld->relocatable)
+ {
+ load_size = highest_load - mld->link_base_addr;
+
+ grub_dprintf ("multiboot_loader", "align=0x%lx, preference=0x%x, "
+ "load_size=0x%x, avoid_efi_boot_services=%d\n",
+ (long) mld->align, mld->preference, load_size,
+ mld->avoid_efi_boot_services);
+
+ if (load_size > mld->max_addr || mld->min_addr > mld->max_addr - load_size)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
+
+ err = grub_relocator_alloc_chunk_align_safe (GRUB_MULTIBOOT (relocator), &ch,
+ mld->min_addr, mld->max_addr,
+ load_size, mld->align ? mld->align : 1,
+ mld->preference, mld->avoid_efi_boot_services);
+
+ if (err)
+ {
+ grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n");
+ return err;
+ }
+
+ mld->load_base_addr = get_physical_target_address (ch);
+ source = get_virtual_current_address (ch);
+ }
+ else
+ mld->load_base_addr = mld->link_base_addr;
+
+ grub_dprintf ("multiboot_loader", "relocatable=%d, link_base_addr=0x%x, "
+ "load_base_addr=0x%x\n", mld->relocatable,
+ mld->link_base_addr, mld->load_base_addr);
+
+ /* Load every loadable segment in memory. */
+ for (i = 0; i < ehdr->e_phnum; i++)
+ {
+ if (phdr(i)->p_type == PT_LOAD)
+ {
+
+ grub_dprintf ("multiboot_loader", "segment %d: paddr=0x%lx, memsz=0x%lx, vaddr=0x%lx\n",
+ i, (long) phdr(i)->p_paddr, (long) phdr(i)->p_memsz, (long) phdr(i)->p_vaddr);
+
+ if (mld->relocatable)
+ {
+ load_offset = phdr(i)->p_paddr - mld->link_base_addr;
+ grub_dprintf ("multiboot_loader", "segment %d: load_offset=0x%x\n", i, load_offset);
+ }
+ else
+ {
+ err = grub_relocator_alloc_chunk_addr (GRUB_MULTIBOOT (relocator), &ch,
+ phdr(i)->p_paddr, phdr(i)->p_memsz);
+
+ if (err)
+ {
+ grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n");
+ return err;
+ }
+
+ source = get_virtual_current_address (ch);
+ }
+
+ if (phdr(i)->p_filesz != 0)
+ {
+ if (grub_file_seek (mld->file, (grub_off_t) phdr(i)->p_offset)
+ == (grub_off_t) -1)
+ return grub_errno;
+
+ if (grub_file_read (mld->file, (grub_uint8_t *) source + load_offset, phdr(i)->p_filesz)
+ != (grub_ssize_t) phdr(i)->p_filesz)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
+ mld->filename);
+ return grub_errno;
+ }
+ }
+
+ if (phdr(i)->p_filesz < phdr(i)->p_memsz)
+ grub_memset ((grub_uint8_t *) source + load_offset + phdr(i)->p_filesz, 0,
+ phdr(i)->p_memsz - phdr(i)->p_filesz);
+ }
+ }
+
+ for (i = 0; i < ehdr->e_phnum; i++)
+ if (phdr(i)->p_vaddr <= ehdr->e_entry
+ && phdr(i)->p_vaddr + phdr(i)->p_memsz > ehdr->e_entry)
+ {
+ GRUB_MULTIBOOT (payload_eip) = (ehdr->e_entry - phdr(i)->p_vaddr)
+ + phdr(i)->p_paddr;
+#ifdef MULTIBOOT_LOAD_ELF64
+# ifdef __mips
+ /* We still in 32-bit mode. */
+ if ((ehdr->e_entry - phdr(i)->p_vaddr)
+ + phdr(i)->p_paddr < 0xffffffff80000000ULL)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
+# else
+ /* We still in 32-bit mode. */
+ if ((ehdr->e_entry - phdr(i)->p_vaddr)
+ + phdr(i)->p_paddr > 0xffffffff)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
+# endif
+#endif
+ break;
+ }
+
+ if (i == ehdr->e_phnum)
+ return grub_error (GRUB_ERR_BAD_OS, "entry point isn't in a segment");
+
+#if defined (__i386__) || defined (__x86_64__)
+
+#elif defined (__mips)
+ GRUB_MULTIBOOT (payload_eip) |= 0x80000000;
+#else
+#error Please complete this
+#endif
+
+ if (ehdr->e_shnum)
+ {
+ grub_uint8_t *shdr, *shdrptr;
+
+ shdr = grub_calloc (ehdr->e_shnum, ehdr->e_shentsize);
+ if (!shdr)
+ return grub_errno;
+
+ if (grub_file_seek (mld->file, ehdr->e_shoff) == (grub_off_t) -1)
+ {
+ grub_free (shdr);
+ return grub_errno;
+ }
+
+ if (grub_file_read (mld->file, shdr, (grub_uint32_t) ehdr->e_shnum * ehdr->e_shentsize)
+ != (grub_ssize_t) ehdr->e_shnum * ehdr->e_shentsize)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
+ mld->filename);
+ return grub_errno;
+ }
+
+ for (shdrptr = shdr, i = 0; i < ehdr->e_shnum;
+ shdrptr += ehdr->e_shentsize, i++)
+ {
+ Elf_Shdr *sh = (Elf_Shdr *) shdrptr;
+ void *src;
+ grub_addr_t target;
+
+ if (mld->mbi_ver >= 2 && (sh->sh_type == SHT_REL || sh->sh_type == SHT_RELA))
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ELF files with relocs are not supported yet");
+
+ /* This section is a loaded section,
+ so we don't care. */
+ if (sh->sh_addr != 0)
+ continue;
+
+ /* This section is empty, so we don't care. */
+ if (sh->sh_size == 0)
+ continue;
+
+ err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch, 0,
+ UP_TO_TOP32 (sh->sh_size),
+ sh->sh_size, sh->sh_addralign,
+ GRUB_RELOCATOR_PREFERENCE_NONE,
+ mld->avoid_efi_boot_services);
+ if (err)
+ {
+ grub_dprintf ("multiboot_loader", "Error loading shdr %d\n", i);
+ return err;
+ }
+ src = get_virtual_current_address (ch);
+ target = get_physical_target_address (ch);
+
+ if (grub_file_seek (mld->file, sh->sh_offset) == (grub_off_t) -1)
+ return grub_errno;
+
+ if (grub_file_read (mld->file, src, sh->sh_size)
+ != (grub_ssize_t) sh->sh_size)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
+ mld->filename);
+ return grub_errno;
+ }
+ sh->sh_addr = target;
+ }
+ GRUB_MULTIBOOT (add_elfsyms) (ehdr->e_shnum, ehdr->e_shentsize,
+ ehdr->e_shstrndx, shdr);
+ }
+
+#undef phdr
+
+ return grub_errno;
+}
+
+#undef XX
+#undef E_MACHINE
+#undef ELFCLASSXX
+#undef Elf_Ehdr
+#undef Elf_Phdr
+#undef Elf_Shdr
diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c
new file mode 100644
index 0000000..9a943d7
--- /dev/null
+++ b/grub-core/loader/multiboot_mbi2.c
@@ -0,0 +1,1128 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010,2011,2012,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/memory.h>
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/machine/biosnum.h>
+#include <grub/machine/apm.h>
+#include <grub/machine/memory.h>
+#endif
+#include <grub/multiboot2.h>
+#include <grub/cpu/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/video.h>
+#include <grub/acpi.h>
+#include <grub/i18n.h>
+#include <grub/net.h>
+#include <grub/lib/cmdline.h>
+
+#if defined (GRUB_MACHINE_EFI)
+#include <grub/efi/efi.h>
+#endif
+
+#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_QEMU)
+#include <grub/i386/pc/vbe.h>
+#define HAS_VGA_TEXT 1
+#else
+#define HAS_VGA_TEXT 0
+#endif
+
+#if defined (__i386__) || defined (__x86_64__)
+#define MBI_MIN_ADDR 0x1000
+#else
+#define MBI_MIN_ADDR 0
+#endif
+
+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 int bootdev_set;
+static grub_uint32_t biosdev, slice, part;
+static grub_size_t elf_sec_num, elf_sec_entsize;
+static unsigned elf_sec_shstrndx;
+static void *elf_sections;
+static int keep_bs = 0;
+static grub_uint32_t load_base_addr;
+
+void
+grub_multiboot2_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;
+}
+
+static struct multiboot_header *
+find_header (grub_properly_aligned_t *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 <= (char *) buffer + len - 12);
+ header = (struct multiboot_header *) ((grub_uint32_t *) header + MULTIBOOT_HEADER_ALIGN / 4))
+ {
+ if (header->magic == MULTIBOOT2_HEADER_MAGIC
+ && !(header->magic + header->architecture
+ + header->header_length + header->checksum)
+ && header->architecture == MULTIBOOT2_ARCHITECTURE_CURRENT)
+ return header;
+ }
+ return NULL;
+}
+
+grub_err_t
+grub_multiboot2_load (grub_file_t file, const char *filename)
+{
+ grub_ssize_t len;
+ struct multiboot_header *header;
+ grub_err_t err;
+ struct multiboot_header_tag *tag;
+ struct multiboot_header_tag_address *addr_tag = NULL;
+ struct multiboot_header_tag_relocatable *rel_tag;
+ int entry_specified = 0, efi_entry_specified = 0;
+ grub_addr_t entry = 0, efi_entry = 0;
+ grub_uint32_t console_required = 0;
+ struct multiboot_header_tag_framebuffer *fbtag = NULL;
+ int accepted_consoles = GRUB_MULTIBOOT2_CONSOLE_EGA_TEXT;
+ mbi_load_data_t mld;
+
+ mld.mbi_ver = 2;
+ mld.relocatable = 0;
+
+ mld.buffer = grub_malloc (MULTIBOOT_SEARCH);
+ if (!mld.buffer)
+ return grub_errno;
+
+ len = grub_file_read (file, mld.buffer, MULTIBOOT_SEARCH);
+ if (len < 32)
+ {
+ grub_free (mld.buffer);
+ return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), filename);
+ }
+
+ COMPILE_TIME_ASSERT (MULTIBOOT_HEADER_ALIGN % 4 == 0);
+
+ header = find_header (mld.buffer, len);
+
+ if (header == 0)
+ {
+ grub_free (mld.buffer);
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found");
+ }
+
+ COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN % 4 == 0);
+
+ keep_bs = 0;
+
+ for (tag = (struct multiboot_header_tag *) (header + 1);
+ tag->type != MULTIBOOT_TAG_TYPE_END;
+ tag = (struct multiboot_header_tag *) ((grub_uint32_t *) tag + ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / 4))
+ switch (tag->type)
+ {
+ case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST:
+ {
+ unsigned i;
+ struct multiboot_header_tag_information_request *request_tag
+ = (struct multiboot_header_tag_information_request *) tag;
+ if (request_tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL)
+ break;
+ for (i = 0; i < (request_tag->size - sizeof (*request_tag))
+ / sizeof (request_tag->requests[0]); i++)
+ switch (request_tag->requests[i])
+ {
+ case MULTIBOOT_TAG_TYPE_END:
+ case MULTIBOOT_TAG_TYPE_CMDLINE:
+ case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME:
+ case MULTIBOOT_TAG_TYPE_MODULE:
+ case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
+ case MULTIBOOT_TAG_TYPE_BOOTDEV:
+ case MULTIBOOT_TAG_TYPE_MMAP:
+ case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
+ case MULTIBOOT_TAG_TYPE_VBE:
+ case MULTIBOOT_TAG_TYPE_ELF_SECTIONS:
+ case MULTIBOOT_TAG_TYPE_APM:
+ case MULTIBOOT_TAG_TYPE_EFI32:
+ case MULTIBOOT_TAG_TYPE_EFI64:
+ case MULTIBOOT_TAG_TYPE_ACPI_OLD:
+ case MULTIBOOT_TAG_TYPE_ACPI_NEW:
+ case MULTIBOOT_TAG_TYPE_NETWORK:
+ case MULTIBOOT_TAG_TYPE_EFI_MMAP:
+ case MULTIBOOT_TAG_TYPE_EFI_BS:
+ case MULTIBOOT_TAG_TYPE_EFI32_IH:
+ case MULTIBOOT_TAG_TYPE_EFI64_IH:
+ case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR:
+ break;
+
+ default:
+ grub_free (mld.buffer);
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ "unsupported information tag: 0x%x",
+ request_tag->requests[i]);
+ }
+ break;
+ }
+
+ case MULTIBOOT_HEADER_TAG_ADDRESS:
+ addr_tag = (struct multiboot_header_tag_address *) tag;
+ break;
+
+ case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS:
+ entry_specified = 1;
+ entry = ((struct multiboot_header_tag_entry_address *) tag)->entry_addr;
+ break;
+
+ case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64:
+#if defined (GRUB_MACHINE_EFI) && defined (__x86_64__)
+ efi_entry_specified = 1;
+ efi_entry = ((struct multiboot_header_tag_entry_address *) tag)->entry_addr;
+#endif
+ break;
+
+ case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS:
+ if (!(((struct multiboot_header_tag_console_flags *) tag)->console_flags
+ & MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED))
+ accepted_consoles &= ~GRUB_MULTIBOOT2_CONSOLE_EGA_TEXT;
+ if (((struct multiboot_header_tag_console_flags *) tag)->console_flags
+ & MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED)
+ console_required = 1;
+ break;
+
+ case MULTIBOOT_HEADER_TAG_FRAMEBUFFER:
+ fbtag = (struct multiboot_header_tag_framebuffer *) tag;
+ accepted_consoles |= GRUB_MULTIBOOT2_CONSOLE_FRAMEBUFFER;
+ break;
+
+ case MULTIBOOT_HEADER_TAG_RELOCATABLE:
+ mld.relocatable = 1;
+ rel_tag = (struct multiboot_header_tag_relocatable *) tag;
+ mld.min_addr = rel_tag->min_addr;
+ mld.max_addr = rel_tag->max_addr;
+ mld.align = rel_tag->align;
+ switch (rel_tag->preference)
+ {
+ case MULTIBOOT_LOAD_PREFERENCE_LOW:
+ mld.preference = GRUB_RELOCATOR_PREFERENCE_LOW;
+ break;
+
+ case MULTIBOOT_LOAD_PREFERENCE_HIGH:
+ mld.preference = GRUB_RELOCATOR_PREFERENCE_HIGH;
+ break;
+
+ default:
+ mld.preference = GRUB_RELOCATOR_PREFERENCE_NONE;
+ }
+ break;
+
+ /* GRUB always page-aligns modules. */
+ case MULTIBOOT_HEADER_TAG_MODULE_ALIGN:
+ break;
+
+ case MULTIBOOT_HEADER_TAG_EFI_BS:
+#ifdef GRUB_MACHINE_EFI
+ keep_bs = 1;
+#endif
+ break;
+
+ default:
+ if (! (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL))
+ {
+ grub_free (mld.buffer);
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ "unsupported tag: 0x%x", tag->type);
+ }
+ break;
+ }
+
+ if (addr_tag && !entry_specified && !(keep_bs && efi_entry_specified))
+ {
+ grub_free (mld.buffer);
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ "load address tag without entry address tag");
+ }
+
+ if (addr_tag)
+ {
+ grub_uint64_t load_addr = (addr_tag->load_addr + 1)
+ ? addr_tag->load_addr : (addr_tag->header_addr
+ - ((char *) header - (char *) mld.buffer));
+ int offset = ((char *) header - (char *) mld.buffer -
+ (addr_tag->header_addr - load_addr));
+ int load_size = ((addr_tag->load_end_addr == 0) ? file->size - offset :
+ addr_tag->load_end_addr - addr_tag->load_addr);
+ grub_size_t code_size;
+ void *source;
+ grub_relocator_chunk_t ch;
+
+ if (addr_tag->bss_end_addr)
+ code_size = (addr_tag->bss_end_addr - load_addr);
+ else
+ code_size = load_size;
+
+ if (mld.relocatable)
+ {
+ if (code_size > mld.max_addr || mld.min_addr > mld.max_addr - code_size)
+ {
+ grub_free (mld.buffer);
+ return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
+ }
+
+ err = grub_relocator_alloc_chunk_align_safe (grub_multiboot2_relocator, &ch,
+ mld.min_addr, mld.max_addr,
+ code_size, mld.align ? mld.align : 1,
+ mld.preference, keep_bs);
+ }
+ else
+ err = grub_relocator_alloc_chunk_addr (grub_multiboot2_relocator,
+ &ch, load_addr, code_size);
+ if (err)
+ {
+ grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
+ grub_free (mld.buffer);
+ return err;
+ }
+ mld.link_base_addr = load_addr;
+ mld.load_base_addr = get_physical_target_address (ch);
+ source = get_virtual_current_address (ch);
+
+ grub_dprintf ("multiboot_loader", "link_base_addr=0x%x, load_base_addr=0x%x, "
+ "load_size=0x%lx, relocatable=%d\n", mld.link_base_addr,
+ mld.load_base_addr, (long) code_size, mld.relocatable);
+
+ if (mld.relocatable)
+ grub_dprintf ("multiboot_loader", "align=0x%lx, preference=0x%x, avoid_efi_boot_services=%d\n",
+ (long) mld.align, mld.preference, keep_bs);
+
+ if ((grub_file_seek (file, offset)) == (grub_off_t) -1)
+ {
+ grub_free (mld.buffer);
+ return grub_errno;
+ }
+
+ grub_file_read (file, source, load_size);
+ if (grub_errno)
+ {
+ grub_free (mld.buffer);
+ return grub_errno;
+ }
+
+ if (addr_tag->bss_end_addr)
+ grub_memset ((grub_uint8_t *) source + load_size, 0,
+ addr_tag->bss_end_addr - load_addr - load_size);
+ }
+ else
+ {
+ mld.file = file;
+ mld.filename = filename;
+ mld.avoid_efi_boot_services = keep_bs;
+ err = grub_multiboot2_load_elf (&mld);
+ if (err)
+ {
+ grub_free (mld.buffer);
+ return err;
+ }
+ }
+
+ load_base_addr = mld.load_base_addr;
+
+ if (keep_bs && efi_entry_specified)
+ grub_multiboot2_payload_eip = efi_entry;
+ else if (entry_specified)
+ grub_multiboot2_payload_eip = entry;
+
+ if (mld.relocatable)
+ {
+ /*
+ * Both branches are mathematically equivalent. However, it looks
+ * that real life (C?) is more complicated. I am trying to avoid
+ * wrap around here if mld.load_base_addr < mld.link_base_addr.
+ * If you look at C operator precedence then everything should work.
+ * However, I am not 100% sure that a given compiler will not
+ * optimize/break this stuff. So, maybe we should use signed
+ * 64-bit int here.
+ */
+ if (mld.load_base_addr >= mld.link_base_addr)
+ grub_multiboot2_payload_eip += mld.load_base_addr - mld.link_base_addr;
+ else
+ grub_multiboot2_payload_eip -= mld.link_base_addr - mld.load_base_addr;
+ }
+
+ if (fbtag)
+ err = grub_multiboot2_set_console (GRUB_MULTIBOOT2_CONSOLE_FRAMEBUFFER,
+ accepted_consoles,
+ fbtag->width, fbtag->height,
+ fbtag->depth, console_required);
+ else
+ err = grub_multiboot2_set_console (GRUB_MULTIBOOT2_CONSOLE_EGA_TEXT,
+ accepted_consoles,
+ 0, 0, 0, console_required);
+ return err;
+}
+
+static grub_size_t
+acpiv2_size (void)
+{
+#if GRUB_MACHINE_HAS_ACPI
+ struct grub_acpi_rsdp_v20 *p = grub_acpi_get_rsdpv2 ();
+
+ if (!p)
+ return 0;
+
+ return ALIGN_UP (sizeof (struct multiboot_tag_old_acpi)
+ + p->length, MULTIBOOT_TAG_ALIGN);
+#else
+ return 0;
+#endif
+}
+
+#ifdef GRUB_MACHINE_EFI
+
+static grub_efi_uintn_t efi_mmap_size = 0;
+
+#endif
+
+static grub_size_t
+net_size (void)
+{
+ struct grub_net_network_level_interface *net;
+ grub_size_t ret = 0;
+
+ FOR_NET_NETWORK_LEVEL_INTERFACES(net)
+ if (net->dhcp_ack)
+ ret += ALIGN_UP (sizeof (struct multiboot_tag_network) + net->dhcp_acklen,
+ MULTIBOOT_TAG_ALIGN);
+ return ret;
+}
+
+static grub_size_t
+grub_multiboot2_get_mbi_size (void)
+{
+#ifdef GRUB_MACHINE_EFI
+ if (!keep_bs && !efi_mmap_size)
+ efi_mmap_size = grub_efi_find_mmap_size ();
+#endif
+ return 2 * sizeof (grub_uint32_t) + sizeof (struct multiboot_tag)
+ + sizeof (struct multiboot_tag)
+ + (sizeof (struct multiboot_tag_string)
+ + ALIGN_UP (cmdline_size, MULTIBOOT_TAG_ALIGN))
+ + (sizeof (struct multiboot_tag_string)
+ + ALIGN_UP (sizeof (PACKAGE_STRING), MULTIBOOT_TAG_ALIGN))
+ + (modcnt * sizeof (struct multiboot_tag_module) + total_modcmd)
+ + ALIGN_UP (sizeof (struct multiboot_tag_basic_meminfo),
+ MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_bootdev), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_elf_sections), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (elf_sec_entsize * elf_sec_num, MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP ((sizeof (struct multiboot_tag_mmap)
+ + grub_multiboot2_get_mmap_count ()
+ * sizeof (struct multiboot_mmap_entry)), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_framebuffer), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_old_acpi)
+ + sizeof (struct grub_acpi_rsdp_v10), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_load_base_addr), MULTIBOOT_TAG_ALIGN)
+ + acpiv2_size ()
+ + net_size ()
+#ifdef GRUB_MACHINE_EFI
+ + ALIGN_UP (sizeof (struct multiboot_tag_efi32), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_efi32_ih), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_efi64), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_efi64_ih), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_efi_mmap)
+ + efi_mmap_size, MULTIBOOT_TAG_ALIGN)
+#endif
+ + sizeof (struct multiboot_tag_vbe) + MULTIBOOT_TAG_ALIGN - 1
+ + sizeof (struct multiboot_tag_apm) + MULTIBOOT_TAG_ALIGN - 1;
+}
+
+/* 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)->zero = 0;
+ (*mmap_entry)++;
+
+ return 0;
+}
+
+/* Fill previously allocated Multiboot mmap. */
+static void
+grub_fill_multiboot_mmap (struct multiboot_tag_mmap *tag)
+{
+ struct multiboot_mmap_entry *mmap_entry = tag->entries;
+
+ tag->type = MULTIBOOT_TAG_TYPE_MMAP;
+ tag->size = sizeof (struct multiboot_tag_mmap)
+ + sizeof (struct multiboot_mmap_entry) * grub_multiboot2_get_mmap_count ();
+ tag->entry_size = sizeof (struct multiboot_mmap_entry);
+ tag->entry_version = 0;
+
+ grub_mmap_iterate (grub_fill_multiboot_mmap_iter, &mmap_entry);
+}
+
+#if defined (GRUB_MACHINE_PCBIOS)
+static void
+fill_vbe_tag (struct multiboot_tag_vbe *tag)
+{
+ grub_vbe_status_t status;
+ void *scratch = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+
+ tag->type = MULTIBOOT_TAG_TYPE_VBE;
+ tag->size = 0;
+
+ status = grub_vbe_bios_get_controller_info (scratch);
+ if (status != GRUB_VBE_STATUS_OK)
+ return;
+
+ grub_memcpy (&tag->vbe_control_info, scratch,
+ sizeof (struct grub_vbe_info_block));
+
+ status = grub_vbe_bios_get_mode (scratch);
+ tag->vbe_mode = *(grub_uint32_t *) scratch;
+ if (status != GRUB_VBE_STATUS_OK)
+ return;
+
+ /* get_mode_info isn't available for mode 3. */
+ if (tag->vbe_mode == 3)
+ {
+ struct grub_vbe_mode_info_block *mode_info = (void *) &tag->vbe_mode_info;
+ 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
+ {
+ status = grub_vbe_bios_get_mode_info (tag->vbe_mode, scratch);
+ if (status != GRUB_VBE_STATUS_OK)
+ return;
+ grub_memcpy (&tag->vbe_mode_info, scratch,
+ sizeof (struct grub_vbe_mode_info_block));
+ }
+ grub_vbe_bios_get_pm_interface (&tag->vbe_interface_seg,
+ &tag->vbe_interface_off,
+ &tag->vbe_interface_len);
+
+ tag->size = sizeof (*tag);
+}
+#endif
+
+static grub_err_t
+retrieve_video_parameters (grub_properly_aligned_t **ptrorig)
+{
+ 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];
+ struct multiboot_tag_framebuffer *tag
+ = (struct multiboot_tag_framebuffer *) *ptrorig;
+
+ err = grub_multiboot2_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 HAS_VGA_TEXT
+ if (driv_id == GRUB_VIDEO_DRIVER_NONE)
+ {
+ struct grub_vbe_mode_info_block vbe_mode_info;
+ grub_uint32_t vbe_mode;
+
+#if defined (GRUB_MACHINE_PCBIOS)
+ {
+ grub_vbe_status_t status;
+ void *scratch = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+ status = grub_vbe_bios_get_mode (scratch);
+ vbe_mode = *(grub_uint32_t *) scratch;
+ if (status != GRUB_VBE_STATUS_OK)
+ return GRUB_ERR_NONE;
+ }
+#else
+ vbe_mode = 3;
+#endif
+
+ /* get_mode_info isn't available for mode 3. */
+ if (vbe_mode == 3)
+ {
+ grub_memset (&vbe_mode_info, 0,
+ sizeof (struct grub_vbe_mode_info_block));
+ vbe_mode_info.memory_model = GRUB_VBE_MEMORY_MODEL_TEXT;
+ vbe_mode_info.x_resolution = 80;
+ vbe_mode_info.y_resolution = 25;
+ }
+#if defined (GRUB_MACHINE_PCBIOS)
+ else
+ {
+ grub_vbe_status_t status;
+ void *scratch = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+ status = grub_vbe_bios_get_mode_info (vbe_mode, scratch);
+ if (status != GRUB_VBE_STATUS_OK)
+ return GRUB_ERR_NONE;
+ grub_memcpy (&vbe_mode_info, scratch,
+ sizeof (struct grub_vbe_mode_info_block));
+ }
+#endif
+
+ if (vbe_mode_info.memory_model == GRUB_VBE_MEMORY_MODEL_TEXT)
+ {
+ tag = (struct multiboot_tag_framebuffer *) *ptrorig;
+ tag->common.type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
+ tag->common.size = 0;
+
+ tag->common.framebuffer_addr = 0xb8000;
+
+ tag->common.framebuffer_pitch = 2 * vbe_mode_info.x_resolution;
+ tag->common.framebuffer_width = vbe_mode_info.x_resolution;
+ tag->common.framebuffer_height = vbe_mode_info.y_resolution;
+
+ tag->common.framebuffer_bpp = 16;
+
+ tag->common.framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
+ tag->common.size = sizeof (tag->common);
+ tag->common.reserved = 0;
+ *ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+ return GRUB_ERR_NONE;
+ }
+#else
+ if (driv_id == GRUB_VIDEO_DRIVER_NONE)
+ return GRUB_ERR_NONE;
+#endif
+
+#if GRUB_MACHINE_HAS_VBE
+ {
+ struct multiboot_tag_vbe *tag_vbe = (struct multiboot_tag_vbe *) *ptrorig;
+
+ fill_vbe_tag (tag_vbe);
+
+ *ptrorig += ALIGN_UP (tag_vbe->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+#endif
+
+ err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+ if (err)
+ return err;
+
+ tag = (struct multiboot_tag_framebuffer *) *ptrorig;
+ tag->common.type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
+ tag->common.size = 0;
+
+ tag->common.framebuffer_addr = (grub_addr_t) framebuffer;
+ tag->common.framebuffer_pitch = mode_info.pitch;
+
+ tag->common.framebuffer_width = mode_info.width;
+ tag->common.framebuffer_height = mode_info.height;
+
+ tag->common.framebuffer_bpp = mode_info.bpp;
+
+ tag->common.reserved = 0;
+
+ if (mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)
+ {
+ unsigned i;
+ tag->common.framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED;
+ tag->framebuffer_palette_num_colors = mode_info.number_of_colors;
+ if (tag->framebuffer_palette_num_colors > ARRAY_SIZE (palette))
+ tag->framebuffer_palette_num_colors = ARRAY_SIZE (palette);
+ tag->common.size = sizeof (struct multiboot_tag_framebuffer_common)
+ + sizeof (multiboot_uint16_t) + tag->framebuffer_palette_num_colors
+ * sizeof (struct multiboot_color);
+ for (i = 0; i < tag->framebuffer_palette_num_colors; i++)
+ {
+ tag->framebuffer_palette[i].red = palette[i].r;
+ tag->framebuffer_palette[i].green = palette[i].g;
+ tag->framebuffer_palette[i].blue = palette[i].b;
+ }
+ }
+ else
+ {
+ tag->common.framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
+ tag->framebuffer_red_field_position = mode_info.red_field_pos;
+ tag->framebuffer_red_mask_size = mode_info.red_mask_size;
+ tag->framebuffer_green_field_position = mode_info.green_field_pos;
+ tag->framebuffer_green_mask_size = mode_info.green_mask_size;
+ tag->framebuffer_blue_field_position = mode_info.blue_field_pos;
+ tag->framebuffer_blue_mask_size = mode_info.blue_mask_size;
+
+ tag->common.size = sizeof (struct multiboot_tag_framebuffer_common) + 6;
+ }
+ *ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_multiboot2_make_mbi (grub_uint32_t *target)
+{
+ grub_properly_aligned_t *ptrorig;
+ grub_properly_aligned_t *mbistart;
+ grub_err_t err;
+ grub_size_t bufsize;
+ grub_relocator_chunk_t ch;
+
+ bufsize = grub_multiboot2_get_mbi_size ();
+
+ COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN % sizeof (grub_properly_aligned_t) == 0);
+
+ err = grub_relocator_alloc_chunk_align (grub_multiboot2_relocator, &ch,
+ MBI_MIN_ADDR, UP_TO_TOP32 (bufsize),
+ bufsize, MULTIBOOT_TAG_ALIGN,
+ GRUB_RELOCATOR_PREFERENCE_NONE, 1);
+ if (err)
+ return err;
+
+ ptrorig = get_virtual_current_address (ch);
+#if defined (__i386__) || defined (__x86_64__)
+ *target = get_physical_target_address (ch);
+#elif defined (__mips)
+ *target = get_physical_target_address (ch) | 0x80000000;
+#else
+#error Please complete this
+#endif
+
+ mbistart = ptrorig;
+ COMPILE_TIME_ASSERT ((2 * sizeof (grub_uint32_t))
+ % sizeof (grub_properly_aligned_t) == 0);
+ COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN
+ % sizeof (grub_properly_aligned_t) == 0);
+ ptrorig += (2 * sizeof (grub_uint32_t)) / sizeof (grub_properly_aligned_t);
+
+ {
+ struct multiboot_tag_load_base_addr *tag = (struct multiboot_tag_load_base_addr *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR;
+ tag->size = sizeof (struct multiboot_tag_load_base_addr);
+ tag->load_base_addr = load_base_addr;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+
+ {
+ struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_CMDLINE;
+ tag->size = sizeof (struct multiboot_tag_string) + cmdline_size;
+ grub_memcpy (tag->string, cmdline, cmdline_size);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+
+ {
+ struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME;
+ tag->size = sizeof (struct multiboot_tag_string) + sizeof (PACKAGE_STRING);
+ grub_memcpy (tag->string, PACKAGE_STRING, sizeof (PACKAGE_STRING));
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+
+#ifdef GRUB_MACHINE_PCBIOS
+ {
+ struct grub_apm_info info;
+ if (grub_apm_get_info (&info))
+ {
+ struct multiboot_tag_apm *tag = (struct multiboot_tag_apm *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_APM;
+ tag->size = sizeof (struct multiboot_tag_apm);
+
+ tag->cseg = info.cseg;
+ tag->offset = info.offset;
+ tag->cseg_16 = info.cseg_16;
+ tag->dseg = info.dseg;
+ tag->flags = info.flags;
+ tag->cseg_len = info.cseg_len;
+ tag->dseg_len = info.dseg_len;
+ tag->cseg_16_len = info.cseg_16_len;
+ tag->version = info.version;
+
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+ }
+#endif
+
+ {
+ unsigned i;
+ struct module *cur;
+
+ for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next)
+ {
+ struct multiboot_tag_module *tag
+ = (struct multiboot_tag_module *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_MODULE;
+ tag->size = sizeof (struct multiboot_tag_module) + cur->cmdline_size;
+ tag->mod_start = cur->start;
+ tag->mod_end = tag->mod_start + cur->size;
+ grub_memcpy (tag->cmdline, cur->cmdline, cur->cmdline_size);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+ }
+
+ if (!keep_bs)
+ {
+ struct multiboot_tag_mmap *tag = (struct multiboot_tag_mmap *) ptrorig;
+ grub_fill_multiboot_mmap (tag);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+
+ {
+ struct multiboot_tag_elf_sections *tag
+ = (struct multiboot_tag_elf_sections *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_ELF_SECTIONS;
+ tag->size = sizeof (struct multiboot_tag_elf_sections)
+ + elf_sec_entsize * elf_sec_num;
+ grub_memcpy (tag->sections, elf_sections, elf_sec_entsize * elf_sec_num);
+ tag->num = elf_sec_num;
+ tag->entsize = elf_sec_entsize;
+ tag->shndx = elf_sec_shstrndx;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+
+ if (!keep_bs)
+ {
+ struct multiboot_tag_basic_meminfo *tag
+ = (struct multiboot_tag_basic_meminfo *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_BASIC_MEMINFO;
+ tag->size = sizeof (struct multiboot_tag_basic_meminfo);
+
+ /* Convert from bytes to kilobytes. */
+ tag->mem_lower = grub_mmap_get_lower () / 1024;
+ tag->mem_upper = grub_mmap_get_upper () / 1024;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+
+ {
+ struct grub_net_network_level_interface *net;
+
+ FOR_NET_NETWORK_LEVEL_INTERFACES(net)
+ if (net->dhcp_ack)
+ {
+ struct multiboot_tag_network *tag
+ = (struct multiboot_tag_network *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_NETWORK;
+ tag->size = sizeof (struct multiboot_tag_network) + net->dhcp_acklen;
+ grub_memcpy (tag->dhcpack, net->dhcp_ack, net->dhcp_acklen);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+ }
+
+ if (bootdev_set)
+ {
+ struct multiboot_tag_bootdev *tag
+ = (struct multiboot_tag_bootdev *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_BOOTDEV;
+ tag->size = sizeof (struct multiboot_tag_bootdev);
+
+ tag->biosdev = biosdev;
+ tag->slice = slice;
+ tag->part = part;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+
+ {
+ err = retrieve_video_parameters (&ptrorig);
+ if (err)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ }
+ }
+
+#if defined (GRUB_MACHINE_EFI) && defined (__x86_64__)
+ {
+ struct multiboot_tag_efi64 *tag = (struct multiboot_tag_efi64 *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_EFI64;
+ tag->size = sizeof (*tag);
+ tag->pointer = (grub_addr_t) grub_efi_system_table;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+#endif
+
+#if defined (GRUB_MACHINE_EFI) && defined (__i386__)
+ {
+ struct multiboot_tag_efi32 *tag = (struct multiboot_tag_efi32 *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_EFI32;
+ tag->size = sizeof (*tag);
+ tag->pointer = (grub_addr_t) grub_efi_system_table;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+#endif
+
+#if GRUB_MACHINE_HAS_ACPI
+ {
+ struct multiboot_tag_old_acpi *tag = (struct multiboot_tag_old_acpi *)
+ ptrorig;
+ struct grub_acpi_rsdp_v10 *a = grub_acpi_get_rsdpv1 ();
+ if (a)
+ {
+ tag->type = MULTIBOOT_TAG_TYPE_ACPI_OLD;
+ tag->size = sizeof (*tag) + sizeof (*a);
+ grub_memcpy (tag->rsdp, a, sizeof (*a));
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+ }
+
+ {
+ struct multiboot_tag_new_acpi *tag = (struct multiboot_tag_new_acpi *)
+ ptrorig;
+ struct grub_acpi_rsdp_v20 *a = grub_acpi_get_rsdpv2 ();
+ if (a)
+ {
+ tag->type = MULTIBOOT_TAG_TYPE_ACPI_NEW;
+ tag->size = sizeof (*tag) + a->length;
+ grub_memcpy (tag->rsdp, a, a->length);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+ }
+#endif
+
+#ifdef GRUB_MACHINE_EFI
+ {
+ struct multiboot_tag_efi_mmap *tag = (struct multiboot_tag_efi_mmap *) ptrorig;
+ grub_efi_uintn_t efi_desc_size;
+ grub_efi_uint32_t efi_desc_version;
+
+ if (!keep_bs)
+ {
+ tag->type = MULTIBOOT_TAG_TYPE_EFI_MMAP;
+ tag->size = sizeof (*tag) + efi_mmap_size;
+
+ err = grub_efi_finish_boot_services (&efi_mmap_size, tag->efi_mmap, NULL,
+ &efi_desc_size, &efi_desc_version);
+
+ if (err)
+ return err;
+
+ tag->descr_size = efi_desc_size;
+ tag->descr_vers = efi_desc_version;
+ tag->size = sizeof (*tag) + efi_mmap_size;
+
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+ }
+
+ if (keep_bs)
+ {
+ {
+ struct multiboot_tag *tag = (struct multiboot_tag *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_EFI_BS;
+ tag->size = sizeof (struct multiboot_tag);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+
+#ifdef __i386__
+ {
+ struct multiboot_tag_efi32_ih *tag = (struct multiboot_tag_efi32_ih *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_EFI32_IH;
+ tag->size = sizeof (struct multiboot_tag_efi32_ih);
+ tag->pointer = (grub_addr_t) grub_efi_image_handle;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+#endif
+
+#ifdef __x86_64__
+ {
+ struct multiboot_tag_efi64_ih *tag = (struct multiboot_tag_efi64_ih *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_EFI64_IH;
+ tag->size = sizeof (struct multiboot_tag_efi64_ih);
+ tag->pointer = (grub_addr_t) grub_efi_image_handle;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+#endif
+ }
+#endif
+
+ {
+ struct multiboot_tag *tag = (struct multiboot_tag *) ptrorig;
+ tag->type = MULTIBOOT_TAG_TYPE_END;
+ tag->size = sizeof (struct multiboot_tag);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)
+ / sizeof (grub_properly_aligned_t);
+ }
+
+ ((grub_uint32_t *) mbistart)[0] = (char *) ptrorig - (char *) mbistart;
+ ((grub_uint32_t *) mbistart)[1] = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_multiboot2_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_err_t
+grub_multiboot2_init_mbi (int argc, char *argv[])
+{
+ grub_ssize_t len = 0;
+
+ grub_multiboot2_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_multiboot2_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;
+
+ 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, MULTIBOOT_TAG_ALIGN);
+
+ err = grub_create_loader_cmdline (argc, argv, newmod->cmdline,
+ newmod->cmdline_size, GRUB_VERIFY_MODULE_CMDLINE);
+ if (err)
+ {
+ grub_free (newmod->cmdline);
+ grub_free (newmod);
+ return err;
+ }
+
+ if (modules_last)
+ modules_last->next = newmod;
+ else
+ modules = newmod;
+ modules_last = newmod;
+
+ modcnt++;
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_multiboot2_set_bootdev (void)
+{
+ grub_device_t dev;
+
+ slice = ~0;
+ part = ~0;
+
+#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_set = 1;
+}
diff --git a/grub-core/loader/powerpc/ieee1275/linux.c b/grub-core/loader/powerpc/ieee1275/linux.c
new file mode 100644
index 0000000..818b2a8
--- /dev/null
+++ b/grub-core/loader/powerpc/ieee1275/linux.c
@@ -0,0 +1,393 @@
+/* linux.c - boot Linux */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,2007,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/elf.h>
+#include <grub/elfload.h>
+#include <grub/loader.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/ieee1275/ieee1275.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/memory.h>
+#include <grub/lib/cmdline.h>
+#include <grub/cache.h>
+#include <grub/linux.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define ELF32_LOADMASK (0xc0000000UL)
+#define ELF64_LOADMASK (0xc000000000000000ULL)
+
+static grub_dl_t my_mod;
+
+static int loaded;
+
+static grub_addr_t initrd_addr;
+static grub_size_t initrd_size;
+
+static grub_addr_t linux_addr;
+static grub_addr_t linux_entry;
+static grub_size_t linux_size;
+
+static char *linux_args;
+
+typedef void (*kernel_entry_t) (void *, unsigned long, int (void *),
+ unsigned long, unsigned long);
+
+/* Context for grub_linux_claimmap_iterate. */
+struct grub_linux_claimmap_iterate_ctx
+{
+ grub_addr_t target;
+ grub_size_t size;
+ grub_size_t align;
+ grub_addr_t found_addr;
+};
+
+/* Helper for grub_linux_claimmap_iterate. */
+static int
+alloc_mem (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+ void *data)
+{
+ struct grub_linux_claimmap_iterate_ctx *ctx = data;
+
+ grub_uint64_t end = addr + len;
+ addr = ALIGN_UP (addr, ctx->align);
+ ctx->target = ALIGN_UP (ctx->target, ctx->align);
+
+ /* Target above the memory chunk. */
+ if (type != GRUB_MEMORY_AVAILABLE || ctx->target > end)
+ return 0;
+
+ /* Target inside the memory chunk. */
+ if (ctx->target >= addr && ctx->target < end &&
+ ctx->size <= end - ctx->target)
+ {
+ if (grub_claimmap (ctx->target, ctx->size) == GRUB_ERR_NONE)
+ {
+ ctx->found_addr = ctx->target;
+ return 1;
+ }
+ grub_print_error ();
+ }
+ /* Target below the memory chunk. */
+ if (ctx->target < addr && addr + ctx->size <= end)
+ {
+ if (grub_claimmap (addr, ctx->size) == GRUB_ERR_NONE)
+ {
+ ctx->found_addr = addr;
+ return 1;
+ }
+ grub_print_error ();
+ }
+ return 0;
+}
+
+static grub_addr_t
+grub_linux_claimmap_iterate (grub_addr_t target, grub_size_t size,
+ grub_size_t align)
+{
+ struct grub_linux_claimmap_iterate_ctx ctx = {
+ .target = target,
+ .size = size,
+ .align = align,
+ .found_addr = (grub_addr_t) -1
+ };
+
+ if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
+ {
+ grub_uint64_t addr = target;
+ if (addr < GRUB_IEEE1275_STATIC_HEAP_START
+ + GRUB_IEEE1275_STATIC_HEAP_LEN)
+ addr = GRUB_IEEE1275_STATIC_HEAP_START
+ + GRUB_IEEE1275_STATIC_HEAP_LEN;
+ addr = ALIGN_UP (addr, align);
+ if (grub_claimmap (addr, size) == GRUB_ERR_NONE)
+ return addr;
+ return (grub_addr_t) -1;
+ }
+
+
+ grub_machine_mmap_iterate (alloc_mem, &ctx);
+
+ return ctx.found_addr;
+}
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ kernel_entry_t linuxmain;
+ grub_ssize_t actual;
+
+ grub_arch_sync_caches ((void *) linux_addr, linux_size);
+ /* Set the command line arguments. */
+ grub_ieee1275_set_property (grub_ieee1275_chosen, "bootargs", linux_args,
+ grub_strlen (linux_args) + 1, &actual);
+
+ grub_dprintf ("loader", "Entry point: 0x%x\n", linux_entry);
+ grub_dprintf ("loader", "Initrd at: 0x%x, size 0x%x\n", initrd_addr,
+ initrd_size);
+ grub_dprintf ("loader", "Boot arguments: %s\n", linux_args);
+ grub_dprintf ("loader", "Jumping to Linux...\n");
+
+ /* Boot the kernel. */
+ linuxmain = (kernel_entry_t) linux_entry;
+ linuxmain ((void *) initrd_addr, initrd_size, grub_ieee1275_entry_fn, 0, 0);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_release_mem (void)
+{
+ grub_free (linux_args);
+ linux_args = 0;
+
+ if (linux_addr && grub_ieee1275_release (linux_addr, linux_size))
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot release memory");
+
+ if (initrd_addr && grub_ieee1275_release (initrd_addr, initrd_size))
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot release memory");
+
+ linux_addr = 0;
+ initrd_addr = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ grub_err_t err;
+
+ err = grub_linux_release_mem ();
+ grub_dl_unref (my_mod);
+
+ loaded = 0;
+
+ return err;
+}
+
+static grub_err_t
+grub_linux_load32 (grub_elf_t elf, const char *filename)
+{
+ Elf32_Addr base_addr;
+ grub_addr_t seg_addr;
+ grub_uint32_t align;
+ grub_uint32_t offset;
+ Elf32_Addr entry;
+
+ linux_size = grub_elf32_size (elf, &base_addr, &align);
+ if (linux_size == 0)
+ return grub_errno;
+ /* Pad it; the kernel scribbles over memory beyond its load address. */
+ linux_size += 0x100000;
+
+ /* Linux's entry point incorrectly contains a virtual address. */
+ entry = elf->ehdr.ehdr32.e_entry & ~ELF32_LOADMASK;
+
+ /* Linux's incorrectly contains a virtual address. */
+ base_addr &= ~ELF32_LOADMASK;
+ offset = entry - base_addr;
+
+ /* On some systems, firmware occupies the memory we're trying to use.
+ * Happily, Linux can be loaded anywhere (it relocates itself). Iterate
+ * until we find an open area. */
+ seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF32_LOADMASK, linux_size, align);
+ if (seg_addr == (grub_addr_t) -1)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory");
+
+ linux_entry = seg_addr + offset;
+ linux_addr = seg_addr;
+
+ /* Now load the segments into the area we claimed. */
+ return grub_elf32_load (elf, filename, (void *) (seg_addr - base_addr), GRUB_ELF_LOAD_FLAGS_30BITS, 0, 0);
+}
+
+static grub_err_t
+grub_linux_load64 (grub_elf_t elf, const char *filename)
+{
+ Elf64_Addr base_addr;
+ grub_addr_t seg_addr;
+ grub_uint64_t align;
+ grub_uint64_t offset;
+ Elf64_Addr entry;
+
+ linux_size = grub_elf64_size (elf, &base_addr, &align);
+ if (linux_size == 0)
+ return grub_errno;
+ /* Pad it; the kernel scribbles over memory beyond its load address. */
+ linux_size += 0x100000;
+
+ base_addr &= ~ELF64_LOADMASK;
+ entry = elf->ehdr.ehdr64.e_entry & ~ELF64_LOADMASK;
+ offset = entry - base_addr;
+ /* Linux's incorrectly contains a virtual address. */
+
+ /* On some systems, firmware occupies the memory we're trying to use.
+ * Happily, Linux can be loaded anywhere (it relocates itself). Iterate
+ * until we find an open area. */
+ seg_addr = grub_linux_claimmap_iterate (base_addr & ~ELF64_LOADMASK, linux_size, align);
+ if (seg_addr == (grub_addr_t) -1)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't claim memory");
+
+ linux_entry = seg_addr + offset;
+ linux_addr = seg_addr;
+
+ /* Now load the segments into the area we claimed. */
+ return grub_elf64_load (elf, filename, (void *) (grub_addr_t) (seg_addr - base_addr), GRUB_ELF_LOAD_FLAGS_62BITS, 0, 0);
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_elf_t elf = 0;
+ int size;
+
+ grub_dl_ref (my_mod);
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto out;
+ }
+
+ elf = grub_elf_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
+ if (! elf)
+ goto out;
+
+ if (elf->ehdr.ehdr32.e_type != ET_EXEC && elf->ehdr.ehdr32.e_type != ET_DYN)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_OS,
+ N_("this ELF file is not of the right type"));
+ goto out;
+ }
+
+ /* Release the previously used memory. */
+ grub_loader_unset ();
+
+ if (grub_elf_is_elf32 (elf))
+ grub_linux_load32 (elf, argv[0]);
+ else
+ if (grub_elf_is_elf64 (elf))
+ grub_linux_load64 (elf, argv[0]);
+ else
+ {
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic"));
+ goto out;
+ }
+
+ size = grub_loader_cmdline_size(argc, argv);
+ linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
+ if (! linux_args)
+ goto out;
+
+ /* Create kernel command line. */
+ grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+ if (grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1,
+ size, GRUB_VERIFY_KERNEL_CMDLINE))
+ goto out;
+
+out:
+
+ if (elf)
+ grub_elf_close (elf);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_linux_release_mem ();
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ }
+ else
+ {
+ grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
+ initrd_addr = 0;
+ loaded = 1;
+ }
+
+ 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 first_addr;
+ grub_addr_t addr;
+ 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);
+
+ first_addr = linux_addr + linux_size;
+
+ /* Attempt to claim at a series of addresses until successful in
+ the same way that grub_rescue_cmd_linux does. */
+ addr = grub_linux_claimmap_iterate (first_addr, size, 0x100000);
+ if (addr == (grub_addr_t) -1)
+ goto fail;
+
+ grub_dprintf ("loader", "Loading initrd at 0x%x, size 0x%x\n", addr, size);
+
+ if (grub_initrd_load (&initrd_ctx, argv, (void *) addr))
+ goto fail;
+
+ initrd_addr = addr;
+ initrd_size = size;
+
+ 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/riscv/linux.c b/grub-core/loader/riscv/linux.c
new file mode 100644
index 0000000..d17c488
--- /dev/null
+++ b/grub-core/loader/riscv/linux.c
@@ -0,0 +1,59 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2018 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/command.h>
+#include <grub/dl.h>
+#include <grub/lib/cmdline.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char *argv[] __attribute__ ((unused)))
+{
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("Linux not supported yet"));
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char *argv[] __attribute__ ((unused)))
+{
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("Linux not supported yet"));
+
+ 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."));
+}
+
+GRUB_MOD_FINI (linux)
+{
+ grub_unregister_command (cmd_linux);
+ grub_unregister_command (cmd_initrd);
+}
diff --git a/grub-core/loader/sparc64/ieee1275/linux.c b/grub-core/loader/sparc64/ieee1275/linux.c
new file mode 100644
index 0000000..bb47ee0
--- /dev/null
+++ b/grub-core/loader/sparc64/ieee1275/linux.c
@@ -0,0 +1,521 @@
+/* linux.c - boot Linux */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003, 2004, 2005, 2007, 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/elf.h>
+#include <grub/elfload.h>
+#include <grub/loader.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/ieee1275/ieee1275.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/memory.h>
+#include <grub/lib/cmdline.h>
+#include <grub/linux.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+
+static int loaded;
+
+/* /virtual-memory/translations property layout */
+struct grub_ieee1275_translation {
+ grub_uint64_t vaddr;
+ grub_uint64_t size;
+ grub_uint64_t data;
+};
+
+static struct grub_ieee1275_translation *of_trans;
+static int of_num_trans;
+
+static grub_addr_t phys_base;
+static grub_addr_t grub_phys_start;
+static grub_addr_t grub_phys_end;
+
+static grub_addr_t initrd_addr;
+static grub_addr_t initrd_paddr;
+static grub_size_t initrd_size;
+
+static Elf64_Addr linux_entry;
+static grub_addr_t linux_addr;
+static grub_addr_t linux_paddr;
+static grub_size_t linux_size;
+
+static char *linux_args;
+
+struct linux_bootstr_info {
+ int len, valid;
+ char buf[];
+};
+
+struct linux_hdrs {
+ /* All HdrS versions support these fields. */
+ unsigned int start_insns[2];
+ char magic[4]; /* "HdrS" */
+ unsigned int linux_kernel_version; /* LINUX_VERSION_CODE */
+ unsigned short hdrs_version;
+ unsigned short root_flags;
+ unsigned short root_dev;
+ unsigned short ram_flags;
+ unsigned int __deprecated_ramdisk_image;
+ unsigned int ramdisk_size;
+
+ /* HdrS versions 0x0201 and higher only */
+ char *reboot_command;
+
+ /* HdrS versions 0x0202 and higher only */
+ struct linux_bootstr_info *bootstr_info;
+
+ /* HdrS versions 0x0301 and higher only */
+ unsigned long ramdisk_image;
+};
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ struct linux_bootstr_info *bp;
+ struct linux_hdrs *hp;
+ grub_addr_t addr;
+
+ hp = (struct linux_hdrs *) linux_addr;
+
+ /* Any pointer we dereference in the kernel image must be relocated
+ to where we actually loaded the kernel. */
+ addr = (grub_addr_t) hp->bootstr_info;
+ addr += (linux_addr - linux_entry);
+ bp = (struct linux_bootstr_info *) addr;
+
+ /* Set the command line arguments, unless the kernel has been
+ built with a fixed CONFIG_CMDLINE. */
+ if (!bp->valid)
+ {
+ int len = grub_strlen (linux_args) + 1;
+ if (bp->len < len)
+ len = bp->len;
+ grub_memcpy(bp->buf, linux_args, len);
+ bp->buf[len-1] = '\0';
+ bp->valid = 1;
+ }
+
+ if (initrd_addr)
+ {
+ /* The kernel expects the physical address, adjusted relative
+ to the lowest address advertised in "/memory"'s available
+ property.
+
+ The history of this is that back when the kernel only supported
+ specifying a 32-bit ramdisk address, this was the way to still
+ be able to specify the ramdisk physical address even if memory
+ started at some place above 4GB.
+
+ The magic 0x400000 is KERNBASE, I have no idea why SILO adds
+ that term into the address, but it does and thus we have to do
+ it too as this is what the kernel expects. */
+ hp->ramdisk_image = initrd_paddr - phys_base + 0x400000;
+ hp->ramdisk_size = initrd_size;
+ }
+
+ grub_dprintf ("loader", "Entry point: 0x%lx\n", linux_addr);
+ grub_dprintf ("loader", "Initrd at: 0x%lx, size 0x%lx\n", initrd_addr,
+ initrd_size);
+ grub_dprintf ("loader", "Boot arguments: %s\n", linux_args);
+ grub_dprintf ("loader", "Jumping to Linux...\n");
+
+ /* Boot the kernel. */
+ asm volatile ("ldx %0, %%o4\n"
+ "ldx %1, %%o6\n"
+ "ldx %2, %%o5\n"
+ "mov %%g0, %%o0\n"
+ "mov %%g0, %%o2\n"
+ "mov %%g0, %%o3\n"
+ "jmp %%o5\n"
+ "mov %%g0, %%o1\n": :
+ "m"(grub_ieee1275_entry_fn),
+ "m"(grub_ieee1275_original_stack),
+ "m"(linux_addr));
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_release_mem (void)
+{
+ grub_free (linux_args);
+ linux_args = 0;
+ linux_addr = 0;
+ initrd_addr = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ grub_err_t err;
+
+ err = grub_linux_release_mem ();
+ grub_dl_unref (my_mod);
+
+ loaded = 0;
+
+ return err;
+}
+
+#define FOUR_MB (4 * 1024 * 1024)
+
+/* Context for alloc_phys. */
+struct alloc_phys_ctx
+{
+ grub_addr_t size;
+ grub_addr_t ret;
+};
+
+/* Helper for alloc_phys. */
+static int
+alloc_phys_choose (grub_uint64_t addr, grub_uint64_t len,
+ grub_memory_type_t type, void *data)
+{
+ struct alloc_phys_ctx *ctx = data;
+ grub_addr_t end = addr + len;
+
+ if (type != GRUB_MEMORY_AVAILABLE)
+ return 0;
+
+ addr = ALIGN_UP (addr, FOUR_MB);
+ if (addr + ctx->size >= end)
+ return 0;
+
+ /* OBP available region contains grub. Start at grub_phys_end. */
+ /* grub_phys_start does not start at the beginning of the memory region */
+ if ((grub_phys_start >= addr && grub_phys_end < end) ||
+ (addr > grub_phys_start && addr < grub_phys_end))
+ {
+ addr = ALIGN_UP (grub_phys_end, FOUR_MB);
+ if (addr + ctx->size >= end)
+ return 0;
+ }
+
+ grub_dprintf("loader",
+ "addr = 0x%lx grub_phys_start = 0x%lx grub_phys_end = 0x%lx\n",
+ addr, grub_phys_start, grub_phys_end);
+
+ if (loaded)
+ {
+ grub_addr_t linux_end = ALIGN_UP (linux_paddr + linux_size, FOUR_MB);
+
+ if (addr >= linux_paddr && addr < linux_end)
+ {
+ addr = linux_end;
+ if (addr + ctx->size >= end)
+ return 0;
+ }
+ if ((addr + ctx->size) >= linux_paddr
+ && (addr + ctx->size) < linux_end)
+ {
+ addr = linux_end;
+ if (addr + ctx->size >= end)
+ return 0;
+ }
+ }
+
+ ctx->ret = addr;
+ return 1;
+}
+
+static grub_addr_t
+alloc_phys (grub_addr_t size)
+{
+ struct alloc_phys_ctx ctx = {
+ .size = size,
+ .ret = (grub_addr_t) -1
+ };
+
+ grub_machine_mmap_iterate (alloc_phys_choose, &ctx);
+
+ return ctx.ret;
+}
+
+static grub_err_t
+grub_linux_load64 (grub_elf_t elf, const char *filename)
+{
+ grub_addr_t off, paddr, base;
+ int ret;
+
+ linux_entry = elf->ehdr.ehdr64.e_entry;
+ linux_addr = 0x40004000;
+ off = 0x4000;
+ linux_size = grub_elf64_size (elf, 0, 0);
+ if (linux_size == 0)
+ return grub_errno;
+
+ grub_dprintf ("loader", "Attempting to claim at 0x%lx, size 0x%lx.\n",
+ linux_addr, linux_size);
+
+ paddr = alloc_phys (linux_size + off);
+ if (paddr == (grub_addr_t) -1)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't allocate physical memory");
+ ret = grub_ieee1275_map (paddr, linux_addr - off,
+ linux_size + off, IEEE1275_MAP_DEFAULT);
+ if (ret)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't map physical memory");
+
+ grub_dprintf ("loader", "Loading Linux at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
+ linux_addr, paddr, linux_size);
+
+ linux_paddr = paddr;
+
+ base = linux_entry - off;
+
+ /* Now load the segments into the area we claimed. */
+ return grub_elf64_load (elf, filename, (void *) (linux_addr - off - base), GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_elf_t elf = 0;
+ int size;
+
+ grub_dl_ref (my_mod);
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto out;
+ }
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
+ if (!file)
+ goto out;
+
+ elf = grub_elf_file (file, argv[0]);
+ if (! elf)
+ goto out;
+
+ if (elf->ehdr.ehdr32.e_type != ET_EXEC)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_OS,
+ N_("this ELF file is not of the right type"));
+ goto out;
+ }
+
+ /* Release the previously used memory. */
+ grub_loader_unset ();
+
+ if (grub_elf_is_elf64 (elf))
+ grub_linux_load64 (elf, argv[0]);
+ else
+ {
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid arch-dependent ELF magic"));
+ goto out;
+ }
+
+ size = grub_loader_cmdline_size(argc, argv);
+
+ linux_args = grub_malloc (size + sizeof (LINUX_IMAGE));
+ if (! linux_args)
+ goto out;
+
+ /* Create kernel command line. */
+ grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+ if (grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1,
+ size, GRUB_VERIFY_KERNEL_CMDLINE))
+ goto out;
+
+out:
+ if (elf)
+ grub_elf_close (elf);
+ else if (file)
+ grub_file_close (file);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_linux_release_mem ();
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ }
+ else
+ {
+ grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
+ initrd_addr = 0;
+ loaded = 1;
+ }
+
+ 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 paddr;
+ grub_addr_t addr;
+ int ret;
+ 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);
+
+ addr = 0x60000000;
+
+ paddr = alloc_phys (size);
+ if (paddr == (grub_addr_t) -1)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't allocate physical memory");
+ goto fail;
+ }
+ ret = grub_ieee1275_map (paddr, addr, size, IEEE1275_MAP_DEFAULT);
+ if (ret)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't map physical memory");
+ goto fail;
+ }
+
+ grub_dprintf ("loader", "Loading initrd at vaddr 0x%lx, paddr 0x%lx, size 0x%lx\n",
+ addr, paddr, size);
+
+ if (grub_initrd_load (&initrd_ctx, argv, (void *) addr))
+ goto fail;
+
+ initrd_addr = addr;
+ initrd_paddr = paddr;
+ initrd_size = size;
+
+ fail:
+ grub_initrd_close (&initrd_ctx);
+
+ return grub_errno;
+}
+
+/* Helper for determine_phys_base. */
+static int
+get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__ ((unused)),
+ grub_memory_type_t type, void *data __attribute__ ((unused)))
+{
+ if (type != GRUB_MEMORY_AVAILABLE)
+ return 0;
+ if (addr < phys_base)
+ phys_base = addr;
+ return 0;
+}
+
+static void
+determine_phys_base (void)
+{
+ phys_base = ~(grub_uint64_t) 0;
+ grub_machine_mmap_iterate (get_physbase, NULL);
+}
+
+static void
+fetch_translations (void)
+{
+ grub_ieee1275_phandle_t node;
+ grub_ssize_t actual;
+ int i;
+
+ if (grub_ieee1275_finddevice ("/virtual-memory", &node))
+ {
+ grub_printf ("Cannot find /virtual-memory node.\n");
+ return;
+ }
+
+ if (grub_ieee1275_get_property_length (node, "translations", &actual))
+ {
+ grub_printf ("Cannot find /virtual-memory/translations size.\n");
+ return;
+ }
+
+ of_trans = grub_malloc (actual);
+ if (!of_trans)
+ {
+ grub_printf ("Cannot allocate translations buffer.\n");
+ return;
+ }
+
+ if (grub_ieee1275_get_property (node, "translations", of_trans, actual, &actual))
+ {
+ grub_printf ("Cannot fetch /virtual-memory/translations property.\n");
+ return;
+ }
+
+ of_num_trans = actual / sizeof(struct grub_ieee1275_translation);
+
+ for (i = 0; i < of_num_trans; i++)
+ {
+ struct grub_ieee1275_translation *p = &of_trans[i];
+
+ if (p->vaddr == 0x2000)
+ {
+ grub_addr_t phys, tte = p->data;
+
+ phys = tte & ~(0xff00000000001fffULL);
+
+ grub_phys_start = phys;
+ grub_phys_end = grub_phys_start + p->size;
+ grub_dprintf ("loader", "Grub lives at phys_start[%lx] phys_end[%lx]\n",
+ (unsigned long) grub_phys_start,
+ (unsigned long) grub_phys_end);
+ break;
+ }
+ }
+}
+
+
+static grub_command_t cmd_linux, cmd_initrd;
+
+GRUB_MOD_INIT(linux)
+{
+ determine_phys_base ();
+ fetch_translations ();
+
+ 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/xnu.c b/grub-core/loader/xnu.c
new file mode 100644
index 0000000..1c0cf6a
--- /dev/null
+++ b/grub-core/loader/xnu.c
@@ -0,0 +1,1555 @@
+/* xnu.c - load xnu kernel. Thanks to Florian Idelberger for all the
+ time he spent testing this
+ */
+/*
+ * 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/file.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/dl.h>
+#include <grub/loader.h>
+#include <grub/machoload.h>
+#include <grub/macho.h>
+#include <grub/cpu/macho.h>
+#include <grub/command.h>
+#include <grub/misc.h>
+#include <grub/extcmd.h>
+#include <grub/env.h>
+#include <grub/i18n.h>
+#include <grub/verify.h>
+#include <grub/safemath.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#if defined (__i386) && !defined (GRUB_MACHINE_EFI)
+#include <grub/autoefi.h>
+#endif
+
+struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0;
+static int driverspackagenum = 0;
+static int driversnum = 0;
+int grub_xnu_is_64bit = 0;
+int grub_xnu_darwin_version = 0;
+
+grub_addr_t grub_xnu_heap_target_start = 0;
+grub_size_t grub_xnu_heap_size = 0;
+struct grub_relocator *grub_xnu_relocator;
+
+static grub_err_t
+grub_xnu_register_memory (const char *prefix, int *suffix,
+ grub_addr_t addr, grub_size_t size);
+grub_err_t
+grub_xnu_heap_malloc (int size, void **src, grub_addr_t *target)
+{
+ grub_err_t err;
+ grub_relocator_chunk_t ch;
+ grub_addr_t tgt;
+
+ if (grub_add (grub_xnu_heap_target_start, grub_xnu_heap_size, &tgt))
+ return GRUB_ERR_OUT_OF_RANGE;
+
+ err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch, tgt, size);
+ if (err)
+ return err;
+
+ *src = get_virtual_current_address (ch);
+ *target = tgt;
+ grub_xnu_heap_size += size;
+ grub_dprintf ("xnu", "val=%p\n", *src);
+ return GRUB_ERR_NONE;
+}
+
+/* Make sure next block of the heap will be aligned.
+ Please notice: aligned are pointers AFTER relocation
+ and not the current ones. */
+grub_err_t
+grub_xnu_align_heap (int align)
+{
+ grub_xnu_heap_size
+ = ALIGN_UP (grub_xnu_heap_target_start+ grub_xnu_heap_size, align)
+ - grub_xnu_heap_target_start;
+ return GRUB_ERR_NONE;
+}
+
+/* Free subtree pointed by CUR. */
+void
+grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur)
+{
+ struct grub_xnu_devtree_key *d;
+ while (cur)
+ {
+ grub_free (cur->name);
+ if (cur->datasize == -1)
+ grub_xnu_free_devtree (cur->first_child);
+ else if (cur->data)
+ grub_free (cur->data);
+ d = cur->next;
+ grub_free (cur);
+ cur = d;
+ }
+}
+
+/* Compute the size of device tree in xnu format. */
+static grub_size_t
+grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start,
+ const char *name)
+{
+ grub_size_t ret;
+ struct grub_xnu_devtree_key *cur;
+
+ /* Key header. */
+ ret = 2 * sizeof (grub_uint32_t);
+
+ /* "name" value. */
+ ret += 32 + sizeof (grub_uint32_t)
+ + grub_strlen (name) + 4
+ - (grub_strlen (name) % 4);
+
+ for (cur = start; cur; cur = cur->next)
+ if (cur->datasize != -1)
+ {
+ int align_overhead;
+
+ align_overhead = 4 - (cur->datasize % 4);
+ if (align_overhead == 4)
+ align_overhead = 0;
+ ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead;
+ }
+ else
+ ret += grub_xnu_writetree_get_size (cur->first_child, cur->name);
+ return ret;
+}
+
+/* Write devtree in XNU format at curptr assuming the head is named NAME.*/
+static void *
+grub_xnu_writetree_toheap_real (void *curptr,
+ struct grub_xnu_devtree_key *start,
+ const char *name)
+{
+ struct grub_xnu_devtree_key *cur;
+ int nkeys = 0, nvals = 0;
+ for (cur = start; cur; cur = cur->next)
+ {
+ if (cur->datasize == -1)
+ nkeys++;
+ else
+ nvals++;
+ }
+ /* For the name. */
+ nvals++;
+
+ *((grub_uint32_t *) curptr) = nvals;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+ *((grub_uint32_t *) curptr) = nkeys;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+
+ /* First comes "name" value. */
+ grub_memset (curptr, 0, 32);
+ grub_memcpy (curptr, "name", 4);
+ curptr = ((grub_uint8_t *) curptr) + 32;
+ *((grub_uint32_t *)curptr) = grub_strlen (name) + 1;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+ grub_memcpy (curptr, name, grub_strlen (name));
+ curptr = ((grub_uint8_t *) curptr) + grub_strlen (name);
+ grub_memset (curptr, 0, 4 - (grub_strlen (name) % 4));
+ curptr = ((grub_uint8_t *) curptr) + (4 - (grub_strlen (name) % 4));
+
+ /* Then the other values. */
+ for (cur = start; cur; cur = cur->next)
+ if (cur->datasize != -1)
+ {
+ int align_overhead;
+
+ align_overhead = 4 - (cur->datasize % 4);
+ if (align_overhead == 4)
+ align_overhead = 0;
+ grub_memset (curptr, 0, 32);
+ grub_strncpy (curptr, cur->name, 31);
+ curptr = ((grub_uint8_t *) curptr) + 32;
+ *((grub_uint32_t *) curptr) = cur->datasize;
+ curptr = ((grub_uint32_t *) curptr) + 1;
+ grub_memcpy (curptr, cur->data, cur->datasize);
+ curptr = ((grub_uint8_t *) curptr) + cur->datasize;
+ grub_memset (curptr, 0, align_overhead);
+ curptr = ((grub_uint8_t *) curptr) + align_overhead;
+ }
+
+ /* And then the keys. Recursively use this function. */
+ for (cur = start; cur; cur = cur->next)
+ if (cur->datasize == -1)
+ {
+ curptr = grub_xnu_writetree_toheap_real (curptr,
+ cur->first_child,
+ cur->name);
+ if (!curptr)
+ return 0;
+ }
+ return curptr;
+}
+
+grub_err_t
+grub_xnu_writetree_toheap (grub_addr_t *target, grub_size_t *size)
+{
+ struct grub_xnu_devtree_key *chosen;
+ struct grub_xnu_devtree_key *memorymap;
+ struct grub_xnu_devtree_key *driverkey;
+ struct grub_xnu_extdesc *extdesc;
+ grub_err_t err;
+ void *src;
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+
+ /* Device tree itself is in the memory map of device tree. */
+ /* Create a dummy value in memory-map. */
+ chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
+ if (! chosen)
+ return grub_errno;
+ memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
+ if (! memorymap)
+ return grub_errno;
+
+ driverkey = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*driverkey));
+ if (! driverkey)
+ return grub_errno;
+ driverkey->name = grub_strdup ("DeviceTree");
+ if (! driverkey->name)
+ {
+ err = grub_errno;
+ goto fail;
+ }
+
+ driverkey->datasize = sizeof (*extdesc);
+ driverkey->next = memorymap->first_child;
+ memorymap->first_child = driverkey;
+ driverkey->data = extdesc
+ = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
+ if (! driverkey->data)
+ {
+ err = grub_errno;
+ goto fail;
+ }
+
+ /* Allocate the space based on the size with dummy value. */
+ *size = grub_xnu_writetree_get_size (grub_xnu_devtree_root, "/");
+ err = grub_xnu_heap_malloc (ALIGN_UP (*size + 1, GRUB_XNU_PAGESIZE),
+ &src, target);
+ if (err)
+ goto fail;
+
+ /* Put real data in the dummy. */
+ extdesc->addr = *target;
+ extdesc->size = (grub_uint32_t) *size;
+
+ /* Write the tree to heap. */
+ grub_xnu_writetree_toheap_real (src, grub_xnu_devtree_root, "/");
+ return GRUB_ERR_NONE;
+
+ fail:
+ memorymap->first_child = NULL;
+
+ grub_free (driverkey->data);
+ grub_free (driverkey->name);
+ grub_free (driverkey);
+
+ return err;
+}
+
+/* Find a key or value in parent key. */
+struct grub_xnu_devtree_key *
+grub_xnu_find_key (struct grub_xnu_devtree_key *parent, const char *name)
+{
+ struct grub_xnu_devtree_key *cur;
+ for (cur = parent; cur; cur = cur->next)
+ if (grub_strcmp (cur->name, name) == 0)
+ return cur;
+ return 0;
+}
+
+struct grub_xnu_devtree_key *
+grub_xnu_create_key (struct grub_xnu_devtree_key **parent, const char *name)
+{
+ struct grub_xnu_devtree_key *ret;
+ ret = grub_xnu_find_key (*parent, name);
+ if (ret)
+ return ret;
+ ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
+ if (! ret)
+ return 0;
+ ret->name = grub_strdup (name);
+ if (! ret->name)
+ {
+ grub_free (ret);
+ return 0;
+ }
+ ret->datasize = -1;
+ ret->next = *parent;
+ *parent = ret;
+ return ret;
+}
+
+struct grub_xnu_devtree_key *
+grub_xnu_create_value (struct grub_xnu_devtree_key **parent, const char *name)
+{
+ struct grub_xnu_devtree_key *ret;
+ ret = grub_xnu_find_key (*parent, name);
+ if (ret)
+ {
+ if (ret->datasize == -1)
+ grub_xnu_free_devtree (ret->first_child);
+ else if (ret->datasize)
+ grub_free (ret->data);
+ ret->datasize = 0;
+ ret->data = 0;
+ return ret;
+ }
+ ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
+ if (! ret)
+ return 0;
+ ret->name = grub_strdup (name);
+ if (! ret->name)
+ {
+ grub_free (ret);
+ return 0;
+ }
+ ret->next = *parent;
+ *parent = ret;
+ return ret;
+}
+
+static grub_err_t
+grub_xnu_unload (void)
+{
+ grub_cpu_xnu_unload ();
+
+ grub_xnu_free_devtree (grub_xnu_devtree_root);
+ grub_xnu_devtree_root = 0;
+
+ /* Free loaded image. */
+ driversnum = 0;
+ driverspackagenum = 0;
+ grub_relocator_unload (grub_xnu_relocator);
+ grub_xnu_relocator = NULL;
+ grub_xnu_heap_target_start = 0;
+ grub_xnu_heap_size = 0;
+ grub_xnu_unlock ();
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_err_t err;
+ grub_macho_t macho;
+ grub_uint32_t startcode, endcode;
+ int i;
+ char *ptr;
+ void *loadaddr;
+ grub_addr_t loadaddr_target;
+
+ if (argc < 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ grub_xnu_unload ();
+
+ macho = grub_macho_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL, 0);
+ if (! macho)
+ return grub_errno;
+
+ err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS,
+ args[0]);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
+ (unsigned long) endcode, (unsigned long) startcode);
+
+ grub_xnu_relocator = grub_relocator_new ();
+ if (!grub_xnu_relocator)
+ return grub_errno;
+ grub_xnu_heap_target_start = startcode;
+ err = grub_xnu_heap_malloc (endcode - startcode, &loadaddr,
+ &loadaddr_target);
+
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ /* Load kernel. */
+ err = grub_macho_load32 (macho, args[0], (char *) loadaddr - startcode,
+ GRUB_MACHO_NOBSS, &grub_xnu_darwin_version);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ grub_xnu_entry_point = grub_macho_get_entry_point32 (macho, args[0]);
+ if (! grub_xnu_entry_point)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
+ }
+
+ grub_macho_close (macho);
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ {
+ grub_xnu_unload ();
+ return err;
+ }
+
+ /* Copy parameters to kernel command line. */
+ ptr = grub_xnu_cmdline;
+ for (i = 1; i < argc; i++)
+ {
+ if (ptr + grub_strlen (args[i]) + 1
+ >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
+ break;
+ grub_memcpy (ptr, args[i], grub_strlen (args[i]));
+ ptr += grub_strlen (args[i]);
+ *ptr = ' ';
+ ptr++;
+ }
+
+ /* Replace last space by '\0'. */
+ if (ptr != grub_xnu_cmdline)
+ *(ptr - 1) = 0;
+
+ err = grub_verify_string (grub_xnu_cmdline, GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ return err;
+
+#if defined (__i386) && !defined (GRUB_MACHINE_EFI)
+ err = grub_efiemu_autocore ();
+ if (err)
+ return err;
+#endif
+
+ grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
+
+ grub_xnu_lock ();
+ grub_xnu_is_64bit = 0;
+
+ return 0;
+}
+
+static grub_err_t
+grub_cmd_xnu_kernel64 (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_err_t err;
+ grub_macho_t macho;
+ grub_uint64_t startcode, endcode;
+ int i;
+ char *ptr;
+ void *loadaddr;
+ grub_addr_t loadaddr_target;
+
+ if (argc < 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ grub_xnu_unload ();
+
+ macho = grub_macho_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL, 1);
+ if (! macho)
+ return grub_errno;
+
+ err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS,
+ args[0]);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ startcode &= 0x0fffffff;
+ endcode &= 0x0fffffff;
+
+ grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
+ (unsigned long) endcode, (unsigned long) startcode);
+
+ grub_xnu_relocator = grub_relocator_new ();
+ if (!grub_xnu_relocator)
+ return grub_errno;
+ grub_xnu_heap_target_start = startcode;
+ err = grub_xnu_heap_malloc (endcode - startcode, &loadaddr,
+ &loadaddr_target);
+
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ /* Load kernel. */
+ err = grub_macho_load64 (macho, args[0], (char *) loadaddr - startcode,
+ GRUB_MACHO_NOBSS, &grub_xnu_darwin_version);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ grub_xnu_entry_point = grub_macho_get_entry_point64 (macho, args[0])
+ & 0x0fffffff;
+ if (! grub_xnu_entry_point)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
+ }
+
+ grub_macho_close (macho);
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ {
+ grub_xnu_unload ();
+ return err;
+ }
+
+ /* Copy parameters to kernel command line. */
+ ptr = grub_xnu_cmdline;
+ for (i = 1; i < argc; i++)
+ {
+ if (ptr + grub_strlen (args[i]) + 1
+ >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
+ break;
+ grub_memcpy (ptr, args[i], grub_strlen (args[i]));
+ ptr += grub_strlen (args[i]);
+ *ptr = ' ';
+ ptr++;
+ }
+
+ /* Replace last space by '\0'. */
+ if (ptr != grub_xnu_cmdline)
+ *(ptr - 1) = 0;
+
+ err = grub_verify_string (grub_xnu_cmdline, GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ return err;
+
+#if defined (__i386) && !defined (GRUB_MACHINE_EFI)
+ err = grub_efiemu_autocore ();
+ if (err)
+ return err;
+#endif
+
+ grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
+
+ grub_xnu_lock ();
+ grub_xnu_is_64bit = 1;
+
+ return 0;
+}
+
+/* Register a memory in a memory map under name PREFIXSUFFIX
+ and increment SUFFIX. */
+static grub_err_t
+grub_xnu_register_memory (const char *prefix, int *suffix,
+ grub_addr_t addr, grub_size_t size)
+{
+ struct grub_xnu_devtree_key *chosen;
+ struct grub_xnu_devtree_key *memorymap;
+ struct grub_xnu_devtree_key *driverkey;
+ struct grub_xnu_extdesc *extdesc;
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
+
+ chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
+ if (! chosen)
+ return grub_errno;
+ memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
+ if (! memorymap)
+ return grub_errno;
+
+ driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
+ if (! driverkey)
+ return grub_errno;
+ if (suffix)
+ driverkey->name = grub_xasprintf ("%s%d", prefix, (*suffix)++);
+ else
+ driverkey->name = grub_strdup (prefix);
+ if (!driverkey->name)
+ {
+ grub_free (driverkey);
+ return grub_errno;
+ }
+ driverkey->datasize = sizeof (*extdesc);
+ driverkey->next = memorymap->first_child;
+ driverkey->data = extdesc
+ = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
+ if (! driverkey->data)
+ {
+ grub_free (driverkey->name);
+ grub_free (driverkey);
+ return grub_errno;
+ }
+ memorymap->first_child = driverkey;
+ extdesc->addr = addr;
+ extdesc->size = (grub_uint32_t) size;
+ return GRUB_ERR_NONE;
+}
+
+static inline char *
+get_name_ptr (char *name)
+{
+ char *p = name, *p2;
+ /* Skip Info.plist. */
+ p2 = grub_strrchr (p, '/');
+ if (!p2)
+ return name;
+ if (p2 == name)
+ return name + 1;
+ p = p2 - 1;
+
+ p2 = grub_strrchr (p, '/');
+ if (!p2)
+ return name;
+ if (p2 == name)
+ return name + 1;
+ if (grub_memcmp (p2, "/Contents/", sizeof ("/Contents/") - 1) != 0)
+ return p2 + 1;
+
+ p = p2 - 1;
+
+ p2 = grub_strrchr (p, '/');
+ if (!p2)
+ return name;
+ return p2 + 1;
+}
+
+/* Load .kext. */
+static grub_err_t
+grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile,
+ const char *filename)
+{
+ grub_macho_t macho;
+ grub_err_t err;
+ grub_file_t infoplist;
+ struct grub_xnu_extheader *exthead;
+ int neededspace = sizeof (*exthead);
+ grub_uint8_t *buf;
+ void *buf0;
+ grub_addr_t buf_target;
+ grub_size_t infoplistsize = 0, machosize = 0;
+ char *name, *nameend;
+ int namelen;
+
+ if (infoplistname == NULL)
+ return grub_error (GRUB_ERR_BAD_FILENAME, N_("missing p-list filename"));
+
+ name = get_name_ptr (infoplistname);
+ nameend = grub_strchr (name, '/');
+
+ if (nameend)
+ namelen = nameend - name;
+ else
+ namelen = grub_strlen (name);
+
+ neededspace += namelen + 1;
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
+
+ /* Compute the needed space. */
+ if (binaryfile)
+ {
+ macho = grub_macho_file (binaryfile, filename, grub_xnu_is_64bit);
+ if (!macho)
+ grub_file_close (binaryfile);
+ else
+ {
+ if (grub_xnu_is_64bit)
+ machosize = grub_macho_filesize64 (macho);
+ else
+ machosize = grub_macho_filesize32 (macho);
+ }
+ neededspace += machosize;
+ }
+ else
+ macho = 0;
+
+ infoplist = grub_file_open (infoplistname, GRUB_FILE_TYPE_XNU_INFO_PLIST);
+ grub_errno = GRUB_ERR_NONE;
+ if (infoplist)
+ {
+ infoplistsize = grub_file_size (infoplist);
+ neededspace += infoplistsize + 1;
+ }
+ else
+ infoplistsize = 0;
+
+ /* Allocate the space. */
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ goto fail;
+ err = grub_xnu_heap_malloc (neededspace, &buf0, &buf_target);
+ if (err)
+ goto fail;
+ buf = buf0;
+
+ exthead = (struct grub_xnu_extheader *) buf;
+ grub_memset (exthead, 0, sizeof (*exthead));
+ buf += sizeof (*exthead);
+
+ /* Load the binary. */
+ if (macho)
+ {
+ exthead->binaryaddr = buf_target + (buf - (grub_uint8_t *) buf0);
+ exthead->binarysize = machosize;
+ if (grub_xnu_is_64bit)
+ err = grub_macho_readfile64 (macho, filename, buf);
+ else
+ err = grub_macho_readfile32 (macho, filename, buf);
+ if (err)
+ goto fail;
+ grub_macho_close (macho);
+ buf += machosize;
+ }
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Load the plist. */
+ if (infoplist)
+ {
+ exthead->infoplistaddr = buf_target + (buf - (grub_uint8_t *) buf0);
+ exthead->infoplistsize = infoplistsize + 1;
+ if (grub_file_read (infoplist, buf, infoplistsize)
+ != (grub_ssize_t) (infoplistsize))
+ {
+ grub_file_close (infoplist);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ infoplistname);
+ return grub_errno;
+ }
+ grub_file_close (infoplist);
+ buf[infoplistsize] = 0;
+ buf += infoplistsize + 1;
+ }
+ grub_errno = GRUB_ERR_NONE;
+
+ exthead->nameaddr = (buf - (grub_uint8_t *) buf0) + buf_target;
+ exthead->namesize = namelen + 1;
+ grub_memcpy (buf, name, namelen);
+ buf[namelen] = 0;
+ buf += namelen + 1;
+
+ /* Announce to kernel */
+ return grub_xnu_register_memory ("Driver-", &driversnum, buf_target,
+ neededspace);
+fail:
+ if (macho)
+ grub_macho_close (macho);
+ return err;
+}
+
+/* Load mkext. */
+static grub_err_t
+grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *loadto;
+ grub_addr_t loadto_target;
+ grub_err_t err;
+ grub_off_t readoff = 0;
+ grub_ssize_t readlen = -1;
+ struct grub_macho_fat_header head;
+ struct grub_macho_fat_arch *archs;
+ int narchs, i;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
+
+ file = grub_file_open (args[0], GRUB_FILE_TYPE_XNU_MKEXT);
+ if (! file)
+ return grub_errno;
+
+ /* Sometimes caches are fat binary. Errgh. */
+ if (grub_file_read (file, &head, sizeof (head))
+ != (grub_ssize_t) (sizeof (head)))
+ {
+ /* I don't know the internal structure of package but
+ can hardly imagine a valid package shorter than 20 bytes. */
+ grub_file_close (file);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
+ return grub_errno;
+ }
+
+ /* Find the corresponding architecture. */
+ if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
+ {
+ narchs = grub_be_to_cpu32 (head.nfat_arch);
+ archs = grub_calloc (narchs, sizeof (struct grub_macho_fat_arch));
+ if (! archs)
+ {
+ grub_file_close (file);
+ return grub_errno;
+
+ }
+ if (grub_file_read (file, archs,
+ sizeof (struct grub_macho_fat_arch) * narchs)
+ != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
+ {
+ grub_free (archs);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_READ_ERROR, N_("premature end of file %s"),
+ args[0]);
+ return grub_errno;
+ }
+ for (i = 0; i < narchs; i++)
+ {
+ if (!grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST32
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ readoff = grub_be_to_cpu32 (archs[i].offset);
+ readlen = grub_be_to_cpu32 (archs[i].size);
+ }
+ if (grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST64
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ readoff = grub_be_to_cpu32 (archs[i].offset);
+ readlen = grub_be_to_cpu32 (archs[i].size);
+ }
+ }
+ grub_free (archs);
+ }
+ else
+ {
+ /* It's a flat file. Some sane people still exist. */
+ readoff = 0;
+ readlen = grub_file_size (file);
+ }
+
+ if (readlen == -1)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
+ }
+
+ /* Allocate space. */
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+
+ err = grub_xnu_heap_malloc (readlen, &loadto, &loadto_target);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+
+ /* Read the file. */
+ grub_file_seek (file, readoff);
+ if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
+ {
+ 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_file_close (file);
+
+ /* Pass it to kernel. */
+ return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
+ loadto_target, readlen);
+}
+
+static grub_err_t
+grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *loadto;
+ grub_addr_t loadto_target;
+ grub_err_t err;
+ grub_size_t size;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
+
+ file = grub_file_open (args[0], GRUB_FILE_TYPE_XNU_RAMDISK);
+ if (! file)
+ return grub_errno;
+
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+
+ size = grub_file_size (file);
+
+ err = grub_xnu_heap_malloc (size, &loadto, &loadto_target);
+ if (err)
+ return err;
+ if (grub_file_read (file, loadto, 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;
+ }
+ return grub_xnu_register_memory ("RAMDisk", 0, loadto_target, size);
+}
+
+/* Returns true if the kext should be loaded according to plist
+ and osbundlereq. Also fill BINNAME. */
+static int
+grub_xnu_check_os_bundle_required (char *plistname,
+ const char *osbundlereq,
+ char **binname)
+{
+ grub_file_t file;
+ char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
+ char *stringptr = 0, *ptr2 = 0;
+ grub_size_t size;
+ int depth = 0;
+ int ret;
+ int osbundlekeyfound = 0, binnamekeyfound = 0;
+ if (binname)
+ *binname = 0;
+
+ file = grub_file_open (plistname, GRUB_FILE_TYPE_XNU_INFO_PLIST);
+ if (! file)
+ return 0;
+
+ size = grub_file_size (file);
+ buf = grub_malloc (size);
+ if (! buf)
+ {
+ grub_file_close (file);
+ return 0;
+ }
+ if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), plistname);
+ return 0;
+ }
+ grub_file_close (file);
+
+ /* Set the return value for the case when no OSBundleRequired tag is found. */
+ if (osbundlereq)
+ ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
+ else
+ ret = 1;
+
+ /* Parse plist. It's quite dirty and inextensible but does its job. */
+ for (ptr1 = buf; ptr1 < buf + size; ptr1++)
+ switch (*ptr1)
+ {
+ case '<':
+ tagstart = ptr1;
+ *ptr1 = 0;
+ if (keyptr && depth == 4
+ && grub_strcmp (keyptr, "OSBundleRequired") == 0)
+ osbundlekeyfound = 1;
+ if (keyptr && depth == 4 &&
+ grub_strcmp (keyptr, "CFBundleExecutable") == 0)
+ binnamekeyfound = 1;
+ if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
+ {
+ for (ptr2 = stringptr; *ptr2; ptr2++)
+ *ptr2 = grub_tolower (*ptr2);
+ ret = grub_strword (osbundlereq, stringptr)
+ || grub_strword (osbundlereq, "all");
+ }
+ if (stringptr && binnamekeyfound && binname && depth == 4)
+ {
+ if (*binname)
+ grub_free (*binname);
+ *binname = grub_strdup (stringptr);
+ }
+
+ *ptr1 = '<';
+ keyptr = 0;
+ stringptr = 0;
+ break;
+ case '>':
+ if (! tagstart)
+ {
+ grub_free (buf);
+ grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
+ return 0;
+ }
+ *ptr1 = 0;
+ if (tagstart[1] == '?' || ptr1[-1] == '/')
+ {
+ osbundlekeyfound = 0;
+ *ptr1 = '>';
+ break;
+ }
+ if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
+ keyptr = ptr1 + 1;
+ if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
+ stringptr = ptr1 + 1;
+ else if (grub_strcmp (tagstart + 1, "/key") != 0)
+ {
+ osbundlekeyfound = 0;
+ binnamekeyfound = 0;
+ }
+ *ptr1 = '>';
+
+ if (tagstart[1] == '/')
+ depth--;
+ else
+ depth++;
+ break;
+ }
+ grub_free (buf);
+
+ return ret;
+}
+
+/* Context for grub_xnu_scan_dir_for_kexts. */
+struct grub_xnu_scan_dir_for_kexts_ctx
+{
+ char *dirname;
+ const char *osbundlerequired;
+ int maxrecursion;
+};
+
+/* Helper for grub_xnu_scan_dir_for_kexts. */
+static int
+grub_xnu_scan_dir_for_kexts_load (const char *filename,
+ const struct grub_dirhook_info *info,
+ void *data)
+{
+ struct grub_xnu_scan_dir_for_kexts_ctx *ctx = data;
+ char *newdirname;
+
+ if (! info->dir)
+ return 0;
+ if (filename[0] == '.')
+ return 0;
+
+ if (grub_strlen (filename) < 5 ||
+ grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
+ return 0;
+
+ newdirname
+ = grub_malloc (grub_strlen (ctx->dirname) + grub_strlen (filename) + 2);
+
+ /* It's a .kext. Try to load it. */
+ if (newdirname)
+ {
+ grub_strcpy (newdirname, ctx->dirname);
+ newdirname[grub_strlen (newdirname) + 1] = 0;
+ newdirname[grub_strlen (newdirname)] = '/';
+ grub_strcpy (newdirname + grub_strlen (newdirname), filename);
+ grub_xnu_load_kext_from_dir (newdirname, ctx->osbundlerequired,
+ ctx->maxrecursion);
+ if (grub_errno == GRUB_ERR_BAD_OS)
+ grub_errno = GRUB_ERR_NONE;
+ grub_free (newdirname);
+ }
+ return 0;
+}
+
+/* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
+grub_err_t
+grub_xnu_scan_dir_for_kexts (char *dirname, const char *osbundlerequired,
+ int maxrecursion)
+{
+ struct grub_xnu_scan_dir_for_kexts_ctx ctx = {
+ .dirname = dirname,
+ .osbundlerequired = osbundlerequired,
+ .maxrecursion = maxrecursion
+ };
+ grub_device_t dev;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
+
+ device_name = grub_file_get_device_name (dirname);
+ dev = grub_device_open (device_name);
+ if (dev)
+ {
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (dirname, ')');
+ if (! path)
+ path = dirname;
+ else
+ path++;
+
+ if (fs)
+ (fs->fs_dir) (dev, path, grub_xnu_scan_dir_for_kexts_load, &ctx);
+ grub_device_close (dev);
+ }
+ grub_free (device_name);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Context for grub_xnu_load_kext_from_dir. */
+struct grub_xnu_load_kext_from_dir_ctx
+{
+ char *dirname;
+ const char *osbundlerequired;
+ int maxrecursion;
+ char *plistname;
+ char *newdirname;
+ int usemacos;
+};
+
+/* Helper for grub_xnu_load_kext_from_dir. */
+static int
+grub_xnu_load_kext_from_dir_load (const char *filename,
+ const struct grub_dirhook_info *info,
+ void *data)
+{
+ struct grub_xnu_load_kext_from_dir_ctx *ctx = data;
+
+ if (grub_strlen (filename) > 15)
+ return 0;
+ grub_strcpy (ctx->newdirname + grub_strlen (ctx->dirname) + 1, filename);
+
+ /* If the kext contains directory "Contents" all real stuff is in
+ this directory. */
+ if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
+ grub_xnu_load_kext_from_dir (ctx->newdirname, ctx->osbundlerequired,
+ ctx->maxrecursion - 1);
+
+ /* Directory "Plugins" contains nested kexts. */
+ if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
+ grub_xnu_scan_dir_for_kexts (ctx->newdirname, ctx->osbundlerequired,
+ ctx->maxrecursion - 1);
+
+ /* Directory "MacOS" contains executable, otherwise executable is
+ on the top. */
+ if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
+ ctx->usemacos = 1;
+
+ /* Info.plist is the file which governs our future actions. */
+ if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
+ && ! ctx->plistname)
+ ctx->plistname = grub_strdup (ctx->newdirname);
+ return 0;
+}
+
+/* Load extension DIRNAME. (extensions are directories in xnu) */
+grub_err_t
+grub_xnu_load_kext_from_dir (char *dirname, const char *osbundlerequired,
+ int maxrecursion)
+{
+ struct grub_xnu_load_kext_from_dir_ctx ctx = {
+ .dirname = dirname,
+ .osbundlerequired = osbundlerequired,
+ .maxrecursion = maxrecursion,
+ .plistname = 0,
+ .usemacos = 0
+ };
+ grub_device_t dev;
+ char *newpath;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+ char *binsuffix;
+ grub_file_t binfile;
+
+ ctx.newdirname = grub_malloc (grub_strlen (dirname) + 20);
+ if (! ctx.newdirname)
+ return grub_errno;
+ grub_strcpy (ctx.newdirname, dirname);
+ ctx.newdirname[grub_strlen (dirname)] = '/';
+ ctx.newdirname[grub_strlen (dirname) + 1] = 0;
+ device_name = grub_file_get_device_name (dirname);
+ dev = grub_device_open (device_name);
+ if (dev)
+ {
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (dirname, ')');
+ if (! path)
+ path = dirname;
+ else
+ path++;
+
+ newpath = grub_strchr (ctx.newdirname, ')');
+ if (! newpath)
+ newpath = ctx.newdirname;
+ else
+ newpath++;
+
+ /* Look at the directory. */
+ if (fs)
+ (fs->fs_dir) (dev, path, grub_xnu_load_kext_from_dir_load, &ctx);
+
+ if (ctx.plistname && grub_xnu_check_os_bundle_required
+ (ctx.plistname, osbundlerequired, &binsuffix))
+ {
+ if (binsuffix)
+ {
+ /* Open the binary. */
+ char *binname = grub_malloc (grub_strlen (dirname)
+ + grub_strlen (binsuffix)
+ + sizeof ("/MacOS/"));
+ grub_strcpy (binname, dirname);
+ if (ctx.usemacos)
+ grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
+ else
+ grub_strcpy (binname + grub_strlen (binname), "/");
+ grub_strcpy (binname + grub_strlen (binname), binsuffix);
+ grub_dprintf ("xnu", "%s:%s\n", ctx.plistname, binname);
+ binfile = grub_file_open (binname, GRUB_FILE_TYPE_XNU_KEXT);
+ if (! binfile)
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Load the extension. */
+ grub_xnu_load_driver (ctx.plistname, binfile,
+ binname);
+ grub_free (binname);
+ grub_free (binsuffix);
+ }
+ else
+ {
+ grub_dprintf ("xnu", "%s:0\n", ctx.plistname);
+ grub_xnu_load_driver (ctx.plistname, 0, 0);
+ }
+ }
+ grub_free (ctx.plistname);
+ grub_device_close (dev);
+ }
+ grub_free (device_name);
+
+ return GRUB_ERR_NONE;
+}
+
+
+static int locked=0;
+static grub_dl_t my_mod;
+
+/* Load the kext. */
+static grub_err_t
+grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t binfile = 0;
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
+
+ if (argc == 2)
+ {
+ /* User explicitly specified plist and binary. */
+ if (grub_strcmp (args[1], "-") != 0)
+ {
+ binfile = grub_file_open (args[1], GRUB_FILE_TYPE_XNU_KEXT);
+ if (! binfile)
+ return grub_errno;
+ }
+ return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
+ binfile, args[1]);
+ }
+
+ /* load kext normally. */
+ if (argc == 1)
+ return grub_xnu_load_kext_from_dir (args[0], 0, 10);
+
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+}
+
+/* Load a directory containing kexts. */
+static grub_err_t
+grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ if (argc != 1 && argc != 2)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
+
+ if (argc == 1)
+ return grub_xnu_scan_dir_for_kexts (args[0],
+ "console,root,local-root,network-root",
+ 10);
+ else
+ {
+ char *osbundlerequired = grub_strdup (args[1]), *ptr;
+ grub_err_t err;
+ if (! osbundlerequired)
+ return grub_errno;
+ for (ptr = osbundlerequired; *ptr; ptr++)
+ *ptr = grub_tolower (*ptr);
+ err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10);
+ grub_free (osbundlerequired);
+ return err;
+ }
+}
+
+static inline int
+hextoval (char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
+ return 0;
+}
+
+static inline void
+unescape (char *name, char *curdot, char *nextdot, int *len)
+{
+ char *ptr, *dptr;
+ dptr = name;
+ for (ptr = curdot; ptr < nextdot;)
+ if (ptr + 2 < nextdot && *ptr == '%')
+ {
+ *dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2]));
+ ptr += 3;
+ dptr++;
+ }
+ else
+ {
+ *dptr = *ptr;
+ ptr++;
+ dptr++;
+ }
+ *len = dptr - name;
+}
+
+grub_err_t
+grub_xnu_fill_devicetree (void)
+{
+ struct grub_env_var *var;
+ FOR_SORTED_ENV (var)
+ {
+ char *nextdot = 0, *curdot;
+ struct grub_xnu_devtree_key **curkey = &grub_xnu_devtree_root;
+ struct grub_xnu_devtree_key *curvalue;
+ char *name = 0, *data;
+ int len;
+
+ if (grub_memcmp (var->name, "XNU.DeviceTree.",
+ sizeof ("XNU.DeviceTree.") - 1) != 0)
+ continue;
+
+ curdot = var->name + sizeof ("XNU.DeviceTree.") - 1;
+ nextdot = grub_strchr (curdot, '.');
+ if (nextdot)
+ nextdot++;
+ while (nextdot)
+ {
+ name = grub_realloc (name, nextdot - curdot + 1);
+
+ if (!name)
+ return grub_errno;
+
+ unescape (name, curdot, nextdot, &len);
+ name[len - 1] = 0;
+
+ curkey = &(grub_xnu_create_key (curkey, name)->first_child);
+
+ curdot = nextdot;
+ nextdot = grub_strchr (nextdot, '.');
+ if (nextdot)
+ nextdot++;
+ }
+
+ nextdot = curdot + grub_strlen (curdot) + 1;
+
+ name = grub_realloc (name, nextdot - curdot + 1);
+
+ if (!name)
+ return grub_errno;
+
+ unescape (name, curdot, nextdot, &len);
+ name[len] = 0;
+
+ curvalue = grub_xnu_create_value (curkey, name);
+ grub_free (name);
+ if (!curvalue)
+ return grub_errno;
+
+ data = grub_malloc (grub_strlen (var->value) + 1);
+ if (!data)
+ return grub_errno;
+
+ unescape (data, var->value, var->value + grub_strlen (var->value),
+ &len);
+ curvalue->datasize = len;
+ curvalue->data = data;
+ }
+
+ return grub_errno;
+}
+
+struct grub_video_bitmap *grub_xnu_bitmap = 0;
+grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode;
+
+/* Option array indices. */
+#define XNU_SPLASH_CMD_ARGINDEX_MODE 0
+
+static const struct grub_arg_option xnu_splash_cmd_options[] =
+ {
+ {"mode", 'm', 0, N_("Background image mode."), N_("stretch|normal"),
+ ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static grub_err_t
+grub_cmd_xnu_splash (grub_extcmd_context_t ctxt,
+ int argc, char *args[])
+{
+ grub_err_t err;
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
+
+ if (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].set &&
+ grub_strcmp (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].arg,
+ "stretch") == 0)
+ grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_STRETCH;
+ else
+ grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_CENTER;
+
+ err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]);
+ if (err)
+ grub_xnu_bitmap = 0;
+
+ return err;
+}
+
+
+#ifndef GRUB_MACHINE_EMU
+static grub_err_t
+grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ return grub_xnu_resume (args[0]);
+}
+#endif
+
+void
+grub_xnu_lock (void)
+{
+ if (!locked)
+ grub_dl_ref (my_mod);
+ locked = 1;
+}
+
+void
+grub_xnu_unlock (void)
+{
+ if (locked)
+ grub_dl_unref (my_mod);
+ locked = 0;
+}
+
+static grub_command_t cmd_kernel64, cmd_kernel, cmd_mkext, cmd_kext;
+static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_resume;
+static grub_extcmd_t cmd_splash;
+
+GRUB_MOD_INIT(xnu)
+{
+ cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
+ N_("Load XNU image."));
+ cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64,
+ 0, N_("Load 64-bit XNU image."));
+ cmd_mkext = grub_register_command_lockdown ("xnu_mkext", grub_cmd_xnu_mkext, 0,
+ N_("Load XNU extension package."));
+ cmd_kext = grub_register_command_lockdown ("xnu_kext", grub_cmd_xnu_kext, 0,
+ N_("Load XNU extension."));
+ cmd_kextdir = grub_register_command_lockdown ("xnu_kextdir", grub_cmd_xnu_kextdir,
+ /*
+ * TRANSLATORS: OSBundleRequired is
+ * a variable name in xnu extensions
+ * manifests. It behaves mostly like
+ * GNU/Linux runlevels.
+ */
+ N_("DIRECTORY [OSBundleRequired]"),
+ /*
+ * TRANSLATORS: There are many extensions
+ * in extension directory.
+ */
+ N_("Load XNU extension directory."));
+ cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
+ /* TRANSLATORS: ramdisk here isn't identifier. It can be translated. */
+ N_("Load XNU ramdisk. "
+ "It will be available in OS as md0."));
+ cmd_splash = grub_register_extcmd ("xnu_splash",
+ grub_cmd_xnu_splash, 0, 0,
+ N_("Load a splash image for XNU."),
+ xnu_splash_cmd_options);
+
+#ifndef GRUB_MACHINE_EMU
+ cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
+ 0, N_("Load an image of hibernated"
+ " XNU."));
+#endif
+
+ grub_cpu_xnu_init ();
+
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(xnu)
+{
+#ifndef GRUB_MACHINE_EMU
+ grub_unregister_command (cmd_resume);
+#endif
+ grub_unregister_command (cmd_mkext);
+ grub_unregister_command (cmd_kext);
+ grub_unregister_command (cmd_kextdir);
+ grub_unregister_command (cmd_ramdisk);
+ grub_unregister_command (cmd_kernel);
+ grub_unregister_extcmd (cmd_splash);
+ grub_unregister_command (cmd_kernel64);
+
+ grub_cpu_xnu_fini ();
+}
diff --git a/grub-core/loader/xnu_resume.c b/grub-core/loader/xnu_resume.c
new file mode 100644
index 0000000..d648ef0
--- /dev/null
+++ b/grub-core/loader/xnu_resume.c
@@ -0,0 +1,188 @@
+/*
+ * 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/normal.h>
+#include <grub/dl.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/loader.h>
+#include <grub/i18n.h>
+
+static void *grub_xnu_hibernate_image;
+
+static grub_err_t
+grub_xnu_resume_unload (void)
+{
+ /* Free loaded image */
+ if (grub_xnu_hibernate_image)
+ grub_free (grub_xnu_hibernate_image);
+ grub_xnu_hibernate_image = 0;
+ grub_xnu_unlock ();
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_resume (char *imagename)
+{
+ grub_file_t file;
+ grub_size_t total_header_size;
+ struct grub_xnu_hibernate_header hibhead;
+ void *code;
+ void *image;
+ grub_uint32_t codedest;
+ grub_uint32_t codesize;
+ grub_addr_t target_image;
+ grub_err_t err;
+
+ file = grub_file_open (imagename, GRUB_FILE_TYPE_XNU_HIBERNATE_IMAGE
+ | GRUB_FILE_TYPE_NO_DECOMPRESS);
+ if (! file)
+ return 0;
+
+ /* Read the header. */
+ if (grub_file_read (file, &hibhead, sizeof (hibhead))
+ != sizeof (hibhead))
+ {
+ grub_file_close (file);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_READ_ERROR,
+ N_("premature end of file %s"), imagename);
+ return grub_errno;
+ }
+
+ /* Check the header. */
+ if (hibhead.magic != GRUB_XNU_HIBERNATE_MAGIC)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "hibernate header has incorrect magic number");
+ }
+ if (hibhead.encoffset)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "encrypted images aren't supported yet");
+ }
+
+ if (hibhead.image_size == 0)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "hibernate image is empty");
+ }
+
+ codedest = hibhead.launchcode_target_page;
+ codedest *= GRUB_XNU_PAGESIZE;
+ codesize = hibhead.launchcode_numpages;
+ codesize *= GRUB_XNU_PAGESIZE;
+
+ /* FIXME: check that codedest..codedest+codesize is available. */
+
+ /* Calculate total size before pages to copy. */
+ total_header_size = hibhead.extmapsize + sizeof (hibhead);
+
+ /* Unload image if any. */
+ if (grub_xnu_hibernate_image)
+ grub_free (grub_xnu_hibernate_image);
+
+ /* Try to allocate necessary space.
+ FIXME: mm isn't good enough yet to handle huge allocations.
+ */
+ grub_xnu_relocator = grub_relocator_new ();
+ if (!grub_xnu_relocator)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch, codedest,
+ codesize + GRUB_XNU_PAGESIZE);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+ code = get_virtual_current_address (ch);
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (grub_xnu_relocator, &ch, 0,
+ UP_TO_TOP32 (hibhead.image_size),
+ hibhead.image_size,
+ GRUB_XNU_PAGESIZE,
+ GRUB_RELOCATOR_PREFERENCE_NONE, 0);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+ image = get_virtual_current_address (ch);
+ target_image = get_physical_target_address (ch);
+ }
+
+ /* Read code part. */
+ if (grub_file_seek (file, total_header_size) == (grub_off_t) -1
+ || grub_file_read (file, code, codesize)
+ != (grub_ssize_t) codesize)
+ {
+ grub_file_close (file);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_READ_ERROR,
+ N_("premature end of file %s"), imagename);
+ return grub_errno;
+ }
+
+ /* Read image. */
+ if (grub_file_seek (file, 0) == (grub_off_t) -1
+ || grub_file_read (file, image, hibhead.image_size)
+ != (grub_ssize_t) hibhead.image_size)
+ {
+ grub_file_close (file);
+ if (!grub_errno)
+ grub_error (GRUB_ERR_READ_ERROR,
+ N_("premature end of file %s"), imagename);
+ return grub_errno;
+ }
+ grub_file_close (file);
+
+ /* Setup variables needed by asm helper. */
+ grub_xnu_heap_target_start = codedest;
+ grub_xnu_heap_size = target_image + hibhead.image_size - codedest;
+ grub_xnu_stack = (codedest + hibhead.stack);
+ grub_xnu_entry_point = (codedest + hibhead.entry_point);
+ grub_xnu_arg1 = target_image;
+
+ grub_dprintf ("xnu", "entry point 0x%x\n", codedest + hibhead.entry_point);
+ grub_dprintf ("xnu", "image at 0x%x\n",
+ codedest + codesize + GRUB_XNU_PAGESIZE);
+
+ /* We're ready now. */
+ grub_loader_set (grub_xnu_boot_resume,
+ grub_xnu_resume_unload, 0);
+
+ /* Prevent module from unloading. */
+ grub_xnu_lock ();
+ return GRUB_ERR_NONE;
+}