aboutsummaryrefslogtreecommitdiffstats
path: root/grub-core/loader
diff options
context:
space:
mode:
authorJames <james.mckenzie@citrix.com>2012-11-16 10:41:01 +0000
committerJames <james.mckenzie@citrix.com>2012-11-16 10:41:01 +0000
commit041d1ea37802bf7178a31a53f96c26efa6b8fb7b (patch)
treec193e84ad1237f25a79d0f6a267722e44c73f56a /grub-core/loader
downloadgrub-1.99-041d1ea37802bf7178a31a53f96c26efa6b8fb7b.tar.gz
grub-1.99-041d1ea37802bf7178a31a53f96c26efa6b8fb7b.tar.bz2
grub-1.99-041d1ea37802bf7178a31a53f96c26efa6b8fb7b.zip
fish
Diffstat (limited to 'grub-core/loader')
-rw-r--r--grub-core/loader/aout.c62
-rw-r--r--grub-core/loader/efi/appleloader.c230
-rw-r--r--grub-core/loader/efi/chainloader.c355
-rw-r--r--grub-core/loader/i386/bsd.c1992
-rw-r--r--grub-core/loader/i386/bsd32.c6
-rw-r--r--grub-core/loader/i386/bsd64.c6
-rw-r--r--grub-core/loader/i386/bsdXX.c621
-rw-r--r--grub-core/loader/i386/bsd_pagetable.c92
-rw-r--r--grub-core/loader/i386/linux.c1016
-rw-r--r--grub-core/loader/i386/multiboot_mbi.c731
-rw-r--r--grub-core/loader/i386/pc/chainloader.c177
-rw-r--r--grub-core/loader/i386/pc/linux.c450
-rw-r--r--grub-core/loader/i386/pc/ntldr.c159
-rw-r--r--grub-core/loader/i386/xnu.c1137
-rw-r--r--grub-core/loader/macho.c160
-rw-r--r--grub-core/loader/macho32.c18
-rw-r--r--grub-core/loader/macho64.c18
-rw-r--r--grub-core/loader/machoXX.c239
-rw-r--r--grub-core/loader/mips/linux.c443
-rw-r--r--grub-core/loader/multiboot.c358
-rw-r--r--grub-core/loader/multiboot_elfxx.c225
-rw-r--r--grub-core/loader/multiboot_mbi2.c911
-rw-r--r--grub-core/loader/powerpc/ieee1275/linux.c385
-rw-r--r--grub-core/loader/sparc64/ieee1275/linux.c525
-rw-r--r--grub-core/loader/xnu.c1498
-rw-r--r--grub-core/loader/xnu_resume.c172
26 files changed, 11986 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/efi/appleloader.c b/grub-core/loader/efi/appleloader.c
new file mode 100644
index 0000000..db57a57
--- /dev/null
+++ b/grub-core/loader/efi/appleloader.c
@@ -0,0 +1,230 @@
+/* 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), 0} \
+ }, \
+ .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), 0} \
+ }, \
+ .guid = {0x2B0585EB, 0xD8B8, 0x49A9, {0x8B, 0x8C, 0xE2, 0x1B, \
+ 0x01, 0xAE, 0xF2, 0xB7}} \
+ }, \
+ .end = \
+ { \
+ .type = GRUB_EFI_END_DEVICE_PATH_TYPE, \
+ .subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE, \
+ .length = {sizeof (struct grub_efi_device_path), 0} \
+ } \
+ }
+
+/* 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);
+
+struct devdata
+{
+ 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},
+ {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_printf ("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,
+ "[OPTS]", N_("Boot legacy 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..5dd5ada
--- /dev/null
+++ b/grub-core/loader/efi/chainloader.c
@@ -0,0 +1,355 @@
+/* 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>
+
+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, 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 void
+copy_file_path (grub_efi_file_path_device_path_t *fp,
+ const char *str, grub_efi_uint16_t len)
+{
+ grub_efi_char16_t *p;
+ grub_efi_uint16_t size;
+
+ fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
+ fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
+ size = len * sizeof (grub_efi_char16_t) + sizeof (*fp);
+ fp->header.length[0] = (grub_efi_uint8_t) (size & 0xff);
+ fp->header.length[1] = (grub_efi_uint8_t) (size >> 8);
+ for (p = fp->path_name; len > 0; len--, p++, str++)
+ {
+ /* FIXME: this assumes that the path is in ASCII. */
+ *p = (grub_efi_char16_t) (*str == '/' ? '\\' : *str);
+ }
+}
+
+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 (1)
+ {
+ size += GRUB_EFI_DEVICE_PATH_LENGTH (d);
+ if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
+ break;
+ d = GRUB_EFI_NEXT_DEVICE_PATH (d);
+ }
+
+ file_path = grub_malloc (size
+ + ((grub_strlen (dir_start) + 1)
+ * 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);
+ copy_file_path ((grub_efi_file_path_device_path_t *) d,
+ dir_start, dir_end - dir_start);
+
+ /* Fill the file path for the file. */
+ d = GRUB_EFI_NEXT_DEVICE_PATH (d);
+ copy_file_path ((grub_efi_file_path_device_path_t *) d,
+ dir_end + 1, grub_strlen (dir_end + 1));
+
+ /* 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[0] = sizeof (*d);
+ d->length[1] = 0;
+
+ 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_efi_handle_t dev_handle = 0;
+ grub_device_t dev = 0;
+ grub_efi_device_path_t *dp = 0;
+ grub_efi_loaded_image_t *loaded_image;
+ char *filename;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified");
+ 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);
+ 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);
+ if (dev_handle)
+ dp = grub_efi_get_device_path (dev_handle);
+ }
+
+ if (! dev->disk || ! dev_handle || ! 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, "file is empty");
+ 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_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate %u pages", pages);
+ goto fail;
+ }
+
+ if (grub_file_read (file, (void *) ((grub_addr_t) address), size) != size)
+ {
+ if (grub_errno == GRUB_ERR_NONE)
+ grub_error (GRUB_ERR_BAD_OS, "too small");
+
+ goto fail;
+ }
+
+ status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
+ (void *) ((grub_addr_t) address), 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;
+
+ grub_file_close (file);
+
+ 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_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
+ return 0;
+
+ fail:
+
+ if (dev)
+ grub_device_close (dev);
+
+ if (file)
+ grub_file_close (file);
+
+ if (file_path)
+ 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/i386/bsd.c b/grub-core/loader/i386/bsd.c
new file mode 100644
index 0000000..6487dc3
--- /dev/null
+++ b/grub-core/loader/i386/bsd.c
@@ -0,0 +1,1992 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/i386/bsd.h>
+#include <grub/i386/cpuid.h>
+#include <grub/memory.h>
+#include <grub/i386/memory.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/elfload.h>
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/aout.h>
+#include <grub/command.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/ns8250.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#include <grub/video.h>
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/machine/biosnum.h>
+#endif
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#define NETBSD_DEFAULT_VIDEO_MODE "800x600"
+#else
+#define NETBSD_DEFAULT_VIDEO_MODE "text"
+#include <grub/i386/pc/vbe.h>
+#endif
+#include <grub/video.h>
+
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/partition.h>
+#include <grub/relocator.h>
+#include <grub/i386/relocator.h>
+
+#define ALIGN_DWORD(a) ALIGN_UP (a, 4)
+#define ALIGN_QWORD(a) ALIGN_UP (a, 8)
+#define ALIGN_VAR(a) ((is_64bit) ? (ALIGN_QWORD(a)) : (ALIGN_DWORD(a)))
+#define ALIGN_PAGE(a) ALIGN_UP (a, 4096)
+
+static int kernel_type = KERNEL_TYPE_NONE;
+static grub_dl_t my_mod;
+static grub_addr_t entry, entry_hi, kern_start, kern_end;
+static void *kern_chunk_src;
+static grub_uint32_t bootflags;
+static int is_elf_kernel, is_64bit;
+static grub_uint32_t openbsd_root;
+static struct grub_relocator *relocator = NULL;
+static struct grub_openbsd_ramdisk_descriptor openbsd_ramdisk;
+
+struct bsd_tag
+{
+ struct bsd_tag *next;
+ grub_size_t len;
+ grub_uint32_t type;
+ union {
+ grub_uint8_t a;
+ grub_uint16_t b;
+ grub_uint32_t c;
+ } data[0];
+};
+
+static struct bsd_tag *tags, *tags_last;
+
+struct netbsd_module
+{
+ struct netbsd_module *next;
+ struct grub_netbsd_btinfo_module mod;
+};
+
+static struct netbsd_module *netbsd_mods, *netbsd_mods_last;
+
+static const struct grub_arg_option freebsd_opts[] =
+ {
+ {"dual", 'D', 0, N_("Display output on all consoles."), 0, 0},
+ {"serial", 'h', 0, N_("Use serial console."), 0, 0},
+ {"askname", 'a', 0, N_("Ask for file name to reboot from."), 0, 0},
+ {"cdrom", 'C', 0, N_("Use CDROM as root."), 0, 0},
+ {"config", 'c', 0, N_("Invoke user configuration routing."), 0, 0},
+ {"kdb", 'd', 0, N_("Enter in KDB on boot."), 0, 0},
+ {"gdb", 'g', 0, N_("Use GDB remote debugger instead of DDB."), 0, 0},
+ {"mute", 'm', 0, N_("Disable all boot output."), 0, 0},
+ {"nointr", 'n', 0, "", 0, 0},
+ {"pause", 'p', 0, N_("Wait for keypress after every line of output."), 0, 0},
+ {"quiet", 'q', 0, "", 0, 0},
+ {"dfltroot", 'r', 0, N_("Use compiled-in rootdev."), 0, 0},
+ {"single", 's', 0, N_("Boot into single mode."), 0, 0},
+ {"verbose", 'v', 0, N_("Boot with verbose messages."), 0, 0},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static const grub_uint32_t freebsd_flags[] =
+{
+ FREEBSD_RB_DUAL, FREEBSD_RB_SERIAL, FREEBSD_RB_ASKNAME,
+ FREEBSD_RB_CDROM, FREEBSD_RB_CONFIG, FREEBSD_RB_KDB,
+ FREEBSD_RB_GDB, FREEBSD_RB_MUTE, FREEBSD_RB_NOINTR,
+ FREEBSD_RB_PAUSE, FREEBSD_RB_QUIET, FREEBSD_RB_DFLTROOT,
+ FREEBSD_RB_SINGLE, FREEBSD_RB_VERBOSE, 0
+};
+
+static const struct grub_arg_option openbsd_opts[] =
+ {
+ {"askname", 'a', 0, N_("Ask for file name to reboot from."), 0, 0},
+ {"halt", 'b', 0, N_("Don't reboot, just halt."), 0, 0},
+ {"config", 'c', 0, N_("Change configured devices."), 0, 0},
+ {"single", 's', 0, N_("Boot into single mode."), 0, 0},
+ {"kdb", 'd', 0, N_("Enter in KDB on boot."), 0, 0},
+ {"root", 'r', 0, N_("Set root device."), "wdXY", ARG_TYPE_STRING},
+ {"serial", 'h', GRUB_ARG_OPTION_OPTIONAL,
+ N_("Use serial console."), N_("comUNIT[,SPEED]"), ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static const grub_uint32_t openbsd_flags[] =
+{
+ OPENBSD_RB_ASKNAME, OPENBSD_RB_HALT, OPENBSD_RB_CONFIG,
+ OPENBSD_RB_SINGLE, OPENBSD_RB_KDB, 0
+};
+
+#define OPENBSD_ROOT_ARG (ARRAY_SIZE (openbsd_flags) - 1)
+#define OPENBSD_SERIAL_ARG (ARRAY_SIZE (openbsd_flags))
+
+static const struct grub_arg_option netbsd_opts[] =
+ {
+ {"no-smp", '1', 0, N_("Disable SMP."), 0, 0},
+ {"no-acpi", '2', 0, N_("Disable ACPI."), 0, 0},
+ {"askname", 'a', 0, N_("Ask for file name to reboot from."), 0, 0},
+ {"halt", 'b', 0, N_("Don't reboot, just halt."), 0, 0},
+ {"config", 'c', 0, N_("Change configured devices."), 0, 0},
+ {"kdb", 'd', 0, N_("Enter in KDB on boot."), 0, 0},
+ {"miniroot", 'm', 0, "", 0, 0},
+ {"quiet", 'q', 0, N_("Don't display boot diagnostic messages."), 0, 0},
+ {"single", 's', 0, N_("Boot into single mode."), 0, 0},
+ {"verbose", 'v', 0, N_("Boot with verbose messages."), 0, 0},
+ {"debug", 'x', 0, N_("Boot with debug messages."), 0, 0},
+ {"silent", 'z', 0, N_("Supress normal output (warnings remain)."), 0, 0},
+ {"root", 'r', 0, N_("Set root device."), N_("DEVICE"), ARG_TYPE_STRING},
+ {"serial", 'h', GRUB_ARG_OPTION_OPTIONAL,
+ N_("Use serial console."), N_("[ADDR|comUNIT][,SPEED]"), ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static const grub_uint32_t netbsd_flags[] =
+{
+ NETBSD_AB_NOSMP, NETBSD_AB_NOACPI, NETBSD_RB_ASKNAME,
+ NETBSD_RB_HALT, NETBSD_RB_USERCONFIG, NETBSD_RB_KDB,
+ NETBSD_RB_MINIROOT, NETBSD_AB_QUIET, NETBSD_RB_SINGLE,
+ NETBSD_AB_VERBOSE, NETBSD_AB_DEBUG, NETBSD_AB_SILENT, 0
+};
+
+#define NETBSD_ROOT_ARG (ARRAY_SIZE (netbsd_flags) - 1)
+#define NETBSD_SERIAL_ARG (ARRAY_SIZE (netbsd_flags))
+
+static void
+grub_bsd_get_device (grub_uint32_t * biosdev,
+ grub_uint32_t * unit,
+ grub_uint32_t * slice, grub_uint32_t * part)
+{
+ grub_device_t dev;
+
+#ifdef GRUB_MACHINE_PCBIOS
+ *biosdev = grub_get_root_biosnumber () & 0xff;
+#else
+ *biosdev = 0xff;
+#endif
+ *unit = (*biosdev & 0x7f);
+ *slice = 0xff;
+ *part = 0xff;
+ dev = grub_device_open (0);
+ if (dev && dev->disk && dev->disk->partition)
+ {
+ if (dev->disk->partition->parent)
+ {
+ *part = dev->disk->partition->number;
+ *slice = dev->disk->partition->parent->number + 1;
+ }
+ else
+ *slice = dev->disk->partition->number + 1;
+ }
+ if (dev)
+ grub_device_close (dev);
+}
+
+grub_err_t
+grub_bsd_add_meta (grub_uint32_t type, void *data, grub_uint32_t len)
+{
+ struct bsd_tag *newtag;
+
+ newtag = grub_malloc (len + sizeof (struct bsd_tag));
+ if (!newtag)
+ return grub_errno;
+ newtag->len = len;
+ newtag->type = type;
+ newtag->next = NULL;
+ if (len)
+ grub_memcpy (newtag->data, data, len);
+
+ if (kernel_type == KERNEL_TYPE_FREEBSD
+ && type == (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_SMAP))
+ {
+ struct bsd_tag *p;
+ for (p = tags;
+ p->type != (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_KERNEND);
+ p = p->next);
+
+ if (p)
+ {
+ newtag->next = p->next;
+ p->next = newtag;
+ if (newtag->next == NULL)
+ tags_last = newtag;
+ return GRUB_ERR_NONE;
+ }
+ }
+
+ if (tags_last)
+ tags_last->next = newtag;
+ else
+ tags = newtag;
+ tags_last = newtag;
+
+ return GRUB_ERR_NONE;
+}
+
+struct grub_e820_mmap
+{
+ grub_uint64_t addr;
+ grub_uint64_t size;
+ grub_uint32_t type;
+} __attribute__((packed));
+#define GRUB_E820_RAM 1
+#define GRUB_E820_RESERVED 2
+#define GRUB_E820_ACPI 3
+#define GRUB_E820_NVS 4
+#define GRUB_E820_BADRAM 5
+
+static void
+generate_e820_mmap (grub_size_t *len, grub_size_t *cnt, void *buf)
+{
+ int count = 0;
+ struct grub_e820_mmap *mmap = buf;
+ struct grub_e820_mmap prev, cur;
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+ cur.addr = addr;
+ cur.size = size;
+ switch (type)
+ {
+ case GRUB_MEMORY_AVAILABLE:
+ cur.type = GRUB_E820_RAM;
+ break;
+
+ case GRUB_MEMORY_ACPI:
+ cur.type = GRUB_E820_ACPI;
+ break;
+
+ case GRUB_MEMORY_NVS:
+ cur.type = GRUB_E820_NVS;
+ break;
+
+ default:
+ case GRUB_MEMORY_CODE:
+ case GRUB_MEMORY_RESERVED:
+ cur.type = GRUB_E820_RESERVED;
+ break;
+ }
+
+ /* Merge regions if possible. */
+ if (count && cur.type == prev.type && cur.addr == prev.addr + prev.size)
+ {
+ prev.size += cur.size;
+ if (mmap)
+ mmap[-1] = prev;
+ }
+ else
+ {
+ if (mmap)
+ *mmap++ = cur;
+ prev = cur;
+ count++;
+ }
+
+ if (kernel_type == KERNEL_TYPE_OPENBSD && prev.addr < 0x100000
+ && prev.addr + prev.size > 0x100000)
+ {
+ cur.addr = 0x100000;
+ cur.size = prev.addr + prev.size - 0x100000;
+ cur.type = prev.type;
+ prev.size = 0x100000 - prev.addr;
+ if (mmap)
+ {
+ mmap[-1] = prev;
+ mmap[0] = cur;
+ mmap++;
+ }
+ prev = cur;
+ count++;
+ }
+
+ return 0;
+ }
+
+ grub_mmap_iterate (hook);
+
+ if (len)
+ *len = count * sizeof (struct grub_e820_mmap);
+ *cnt = count;
+
+ return;
+}
+
+static grub_err_t
+grub_bsd_add_mmap (void)
+{
+ grub_size_t len, cnt;
+ void *buf = NULL, *buf0;
+
+ generate_e820_mmap (&len, &cnt, buf);
+
+ if (kernel_type == KERNEL_TYPE_NETBSD)
+ len += sizeof (grub_uint32_t);
+
+ if (kernel_type == KERNEL_TYPE_OPENBSD)
+ len += sizeof (struct grub_e820_mmap);
+
+ buf = grub_malloc (len);
+ if (!buf)
+ return grub_errno;
+
+ buf0 = buf;
+ if (kernel_type == KERNEL_TYPE_NETBSD)
+ {
+ *(grub_uint32_t *) buf = cnt;
+ buf = ((grub_uint32_t *) buf + 1);
+ }
+
+ generate_e820_mmap (NULL, &cnt, buf);
+
+ if (kernel_type == KERNEL_TYPE_OPENBSD)
+ grub_memset ((grub_uint8_t *) buf + len - sizeof (struct grub_e820_mmap), 0,
+ sizeof (struct grub_e820_mmap));
+
+ grub_dprintf ("bsd", "%u entries in smap\n", (unsigned) cnt);
+ if (kernel_type == KERNEL_TYPE_NETBSD)
+ grub_bsd_add_meta (NETBSD_BTINFO_MEMMAP, buf0, len);
+ else if (kernel_type == KERNEL_TYPE_OPENBSD)
+ grub_bsd_add_meta (OPENBSD_BOOTARG_MMAP, buf0, len);
+ else
+ grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_SMAP, buf0, len);
+
+ grub_free (buf0);
+
+ return grub_errno;
+}
+
+grub_err_t
+grub_freebsd_add_meta_module (char *filename, char *type, int argc, char **argv,
+ grub_addr_t addr, grub_uint32_t size)
+{
+ char *name;
+ name = grub_strrchr (filename, '/');
+ if (name)
+ name++;
+ else
+ name = filename;
+ if (grub_strcmp (type, "/boot/zfs/zpool.cache") == 0)
+ name = "/boot/zfs/zpool.cache";
+
+ if (grub_bsd_add_meta (FREEBSD_MODINFO_NAME, name, grub_strlen (name) + 1))
+ return grub_errno;
+
+ if (is_64bit)
+ {
+ grub_uint64_t addr64 = addr, size64 = size;
+ if (grub_bsd_add_meta (FREEBSD_MODINFO_TYPE, type, grub_strlen (type) + 1)
+ || grub_bsd_add_meta (FREEBSD_MODINFO_ADDR, &addr64, sizeof (addr64))
+ || grub_bsd_add_meta (FREEBSD_MODINFO_SIZE, &size64, sizeof (size64)))
+ return grub_errno;
+ }
+ else
+ {
+ if (grub_bsd_add_meta (FREEBSD_MODINFO_TYPE, type, grub_strlen (type) + 1)
+ || grub_bsd_add_meta (FREEBSD_MODINFO_ADDR, &addr, sizeof (addr))
+ || grub_bsd_add_meta (FREEBSD_MODINFO_SIZE, &size, sizeof (size)))
+ return grub_errno;
+ }
+
+ if (argc)
+ {
+ int i, n;
+
+ n = 0;
+ for (i = 0; i < argc; i++)
+ {
+ n += grub_strlen (argv[i]) + 1;
+ }
+
+ if (n)
+ {
+ char cmdline[n], *p;
+
+ p = cmdline;
+ for (i = 0; i < argc; i++)
+ {
+ grub_strcpy (p, argv[i]);
+ p += grub_strlen (argv[i]);
+ *(p++) = ' ';
+ }
+ *p = 0;
+
+ if (grub_bsd_add_meta (FREEBSD_MODINFO_ARGS, cmdline, n))
+ return grub_errno;
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_freebsd_list_modules (void)
+{
+ struct bsd_tag *tag;
+
+ grub_printf (" %-18s %-18s%14s%14s\n", "name", "type", "addr", "size");
+
+ for (tag = tags; tag; tag = tag->next)
+ {
+ switch (tag->type)
+ {
+ case FREEBSD_MODINFO_NAME:
+ case FREEBSD_MODINFO_TYPE:
+ grub_printf (" %-18s", (char *) tag->data);
+ break;
+ case FREEBSD_MODINFO_ADDR:
+ {
+ grub_uint32_t addr;
+
+ addr = *((grub_uint32_t *) tag->data);
+ grub_printf (" 0x%08x", addr);
+ break;
+ }
+ case FREEBSD_MODINFO_SIZE:
+ {
+ grub_uint32_t len;
+
+ len = *((grub_uint32_t *) tag->data);
+ grub_printf (" 0x%08x\n", len);
+ }
+ }
+ }
+}
+
+static grub_err_t
+grub_netbsd_add_meta_module (char *filename, grub_uint32_t type,
+ grub_addr_t addr, grub_uint32_t size)
+{
+ char *name;
+ struct netbsd_module *mod;
+ name = grub_strrchr (filename, '/');
+
+ if (name)
+ name++;
+ else
+ name = filename;
+
+ mod = grub_zalloc (sizeof (*mod));
+ if (!mod)
+ return grub_errno;
+
+ grub_strncpy (mod->mod.name, name, sizeof (mod->mod.name) - 1);
+ mod->mod.addr = addr;
+ mod->mod.type = type;
+ mod->mod.size = size;
+
+ if (netbsd_mods_last)
+ netbsd_mods_last->next = mod;
+ else
+ netbsd_mods = mod;
+ netbsd_mods_last = mod;
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_netbsd_list_modules (void)
+{
+ struct netbsd_module *mod;
+
+ grub_printf (" %-18s%14s%14s%14s\n", "name", "type", "addr", "size");
+
+ for (mod = netbsd_mods; mod; mod = mod->next)
+ grub_printf (" %-18s 0x%08x 0x%08x 0x%08x", mod->mod.name,
+ mod->mod.type, mod->mod.addr, mod->mod.size);
+}
+
+/* This function would be here but it's under different license. */
+#include "bsd_pagetable.c"
+
+static grub_uint32_t freebsd_bootdev, freebsd_biosdev;
+
+static grub_err_t
+grub_freebsd_boot (void)
+{
+ struct grub_freebsd_bootinfo bi;
+ grub_uint8_t *p, *p0;
+ grub_addr_t p_target;
+ grub_size_t p_size = 0;
+ grub_err_t err;
+ grub_size_t tag_buf_len = 0;
+
+ auto int iterate_env (struct grub_env_var *var);
+ int iterate_env (struct grub_env_var *var)
+ {
+ if ((!grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1)) && (var->name[sizeof("kFreeBSD.") - 1]))
+ {
+ grub_strcpy ((char *) p, &var->name[sizeof("kFreeBSD.") - 1]);
+ p += grub_strlen ((char *) p);
+ *(p++) = '=';
+ grub_strcpy ((char *) p, var->value);
+ p += grub_strlen ((char *) p) + 1;
+ }
+
+ return 0;
+ }
+
+ auto int iterate_env_count (struct grub_env_var *var);
+ int iterate_env_count (struct grub_env_var *var)
+ {
+ if ((!grub_memcmp (var->name, "kFreeBSD.", sizeof("kFreeBSD.") - 1)) && (var->name[sizeof("kFreeBSD.") - 1]))
+ {
+ p_size += grub_strlen (&var->name[sizeof("kFreeBSD.") - 1]);
+ p_size++;
+ p_size += grub_strlen (var->value) + 1;
+ }
+
+ return 0;
+ }
+
+ grub_memset (&bi, 0, sizeof (bi));
+ bi.version = FREEBSD_BOOTINFO_VERSION;
+ bi.length = sizeof (bi);
+
+ bi.boot_device = freebsd_biosdev;
+
+ p_size = 0;
+ grub_env_iterate (iterate_env_count);
+
+ if (p_size)
+ p_size = ALIGN_PAGE (kern_end + p_size + 1) - kern_end;
+
+ if (is_elf_kernel)
+ {
+ struct bsd_tag *tag;
+
+ err = grub_bsd_add_mmap ();
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_END, 0, 0);
+ if (err)
+ return err;
+
+ tag_buf_len = 0;
+ for (tag = tags; tag; tag = tag->next)
+ tag_buf_len = ALIGN_VAR (tag_buf_len
+ + sizeof (struct freebsd_tag_header)
+ + tag->len);
+ p_size = ALIGN_PAGE (kern_end + p_size + tag_buf_len) - kern_end;
+ }
+
+ if (is_64bit)
+ p_size += 4096 * 3;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ kern_end, p_size);
+ if (err)
+ return err;
+ p = get_virtual_current_address (ch);
+ }
+ p_target = kern_end;
+ p0 = p;
+ kern_end += p_size;
+
+ grub_env_iterate (iterate_env);
+
+ if (p != p0)
+ {
+ *(p++) = 0;
+
+ bi.environment = p_target;
+ }
+
+ if (is_elf_kernel)
+ {
+ grub_uint8_t *p_tag = p;
+ struct bsd_tag *tag;
+
+ for (tag = tags; tag; tag = tag->next)
+ {
+ struct freebsd_tag_header *head
+ = (struct freebsd_tag_header *) p_tag;
+ head->type = tag->type;
+ head->len = tag->len;
+ p_tag += sizeof (struct freebsd_tag_header);
+ switch (tag->type)
+ {
+ case FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_HOWTO:
+ if (is_64bit)
+ *(grub_uint64_t *) p_tag = bootflags;
+ else
+ *(grub_uint32_t *) p_tag = bootflags;
+ break;
+
+ case FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_ENVP:
+ if (is_64bit)
+ *(grub_uint64_t *) p_tag = bi.environment;
+ else
+ *(grub_uint32_t *) p_tag = bi.environment;
+ break;
+
+ case FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_KERNEND:
+ if (is_64bit)
+ *(grub_uint64_t *) p_tag = kern_end;
+ else
+ *(grub_uint32_t *) p_tag = kern_end;
+ break;
+
+ default:
+ grub_memcpy (p_tag, tag->data, tag->len);
+ break;
+ }
+ p_tag += tag->len;
+ p_tag = ALIGN_VAR (p_tag - p) + p;
+ }
+
+ bi.tags = (p - p0) + p_target;
+
+ p = (ALIGN_PAGE ((p_tag - p0) + p_target) - p_target) + p0;
+ }
+
+ bi.kern_end = kern_end;
+
+ grub_video_set_mode ("text", 0, 0);
+
+ if (is_64bit)
+ {
+ struct grub_relocator64_state state;
+ grub_uint8_t *pagetable;
+ grub_uint32_t *stack;
+ grub_addr_t stack_target;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ 0x10000, 0x90000,
+ 3 * sizeof (grub_uint32_t)
+ + sizeof (bi), 4,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ if (err)
+ return err;
+ stack = get_virtual_current_address (ch);
+ stack_target = get_physical_target_address (ch);
+ }
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#endif
+
+ pagetable = p;
+ fill_bsd64_pagetable (pagetable, (pagetable - p0) + p_target);
+
+ state.cr3 = (pagetable - p0) + p_target;
+ state.rsp = stack_target;
+ state.rip = (((grub_uint64_t) entry_hi) << 32) | entry;
+
+ stack[0] = entry;
+ stack[1] = bi.tags;
+ stack[2] = kern_end;
+ return grub_relocator64_boot (relocator, state, 0, 0x40000000);
+ }
+ else
+ {
+ struct grub_relocator32_state state;
+ grub_uint32_t *stack;
+ grub_addr_t stack_target;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ 0x10000, 0x90000,
+ 9 * sizeof (grub_uint32_t)
+ + sizeof (bi), 4,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ if (err)
+ return err;
+ stack = get_virtual_current_address (ch);
+ stack_target = get_physical_target_address (ch);
+ }
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#endif
+
+ grub_memcpy (&stack[9], &bi, sizeof (bi));
+ state.eip = entry;
+ state.esp = stack_target;
+ state.ebp = stack_target;
+ stack[0] = entry; /* "Return" address. */
+ stack[1] = bootflags | FREEBSD_RB_BOOTINFO;
+ stack[2] = freebsd_bootdev;
+ stack[3] = 0;
+ stack[4] = 0;
+ stack[5] = 0;
+ stack[6] = stack_target + 9 * sizeof (grub_uint32_t);
+ stack[7] = bi.tags;
+ stack[8] = kern_end;
+ return grub_relocator32_boot (relocator, state);
+ }
+
+ /* Not reached. */
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_openbsd_boot (void)
+{
+ grub_uint32_t *stack;
+ struct grub_relocator32_state state;
+ void *curarg, *buf0, *arg0;
+ grub_addr_t buf_target;
+ grub_err_t err;
+ grub_size_t tag_buf_len;
+
+ err = grub_bsd_add_mmap ();
+ if (err)
+ return err;
+
+ {
+ struct bsd_tag *tag;
+ tag_buf_len = 0;
+ for (tag = tags; tag; tag = tag->next)
+ tag_buf_len = ALIGN_VAR (tag_buf_len
+ + sizeof (struct grub_openbsd_bootargs)
+ + tag->len);
+ }
+
+ buf_target = GRUB_BSD_TEMP_BUFFER - 9 * sizeof (grub_uint32_t);
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch, buf_target,
+ tag_buf_len
+ + sizeof (struct grub_openbsd_bootargs)
+ + 9 * sizeof (grub_uint32_t));
+ if (err)
+ return err;
+ buf0 = get_virtual_current_address (ch);
+ }
+
+ stack = (grub_uint32_t *) buf0;
+ arg0 = curarg = stack + 9;
+
+ {
+ struct bsd_tag *tag;
+ struct grub_openbsd_bootargs *head;
+
+ for (tag = tags; tag; tag = tag->next)
+ {
+ head = curarg;
+ head->ba_type = tag->type;
+ head->ba_size = tag->len + sizeof (*head);
+ curarg = head + 1;
+ grub_memcpy (curarg, tag->data, tag->len);
+ curarg = (grub_uint8_t *) curarg + tag->len;
+ head->ba_next = (grub_uint8_t *) curarg - (grub_uint8_t *) buf0
+ + buf_target;
+ }
+ head = curarg;
+ head->ba_type = OPENBSD_BOOTARG_END;
+ head->ba_size = 0;
+ head->ba_next = 0;
+ }
+
+ grub_video_set_mode ("text", 0, 0);
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#endif
+
+ state.eip = entry;
+ state.ebp = state.esp
+ = ((grub_uint8_t *) stack - (grub_uint8_t *) buf0) + buf_target;
+ stack[0] = entry;
+ stack[1] = bootflags;
+ stack[2] = openbsd_root;
+ stack[3] = OPENBSD_BOOTARG_APIVER;
+ stack[4] = 0;
+ stack[5] = grub_mmap_get_upper () >> 10;
+ stack[6] = grub_mmap_get_lower () >> 10;
+ stack[7] = (grub_uint8_t *) curarg - (grub_uint8_t *) arg0;
+ stack[8] = ((grub_uint8_t *) arg0 - (grub_uint8_t *) buf0) + buf_target;
+
+ return grub_relocator32_boot (relocator, state);
+}
+
+static grub_err_t
+grub_netbsd_setup_video (void)
+{
+ struct grub_video_mode_info mode_info;
+ void *framebuffer;
+ const char *modevar;
+ struct grub_netbsd_btinfo_framebuf params;
+ grub_err_t err;
+ grub_video_driver_id_t driv_id;
+
+ modevar = grub_env_get ("gfxpayload");
+
+ /* Now all graphical modes are acceptable.
+ May change in future if we have modes without framebuffer. */
+ if (modevar && *modevar != 0)
+ {
+ char *tmp;
+ tmp = grub_xasprintf ("%s;" NETBSD_DEFAULT_VIDEO_MODE, modevar);
+ if (! tmp)
+ return grub_errno;
+ err = grub_video_set_mode (tmp, 0, 0);
+ grub_free (tmp);
+ }
+ else
+ err = grub_video_set_mode (NETBSD_DEFAULT_VIDEO_MODE, 0, 0);
+
+ if (err)
+ return err;
+
+ driv_id = grub_video_get_driver_id ();
+ if (driv_id == GRUB_VIDEO_DRIVER_NONE)
+ return GRUB_ERR_NONE;
+
+ err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+
+ if (err)
+ return err;
+
+ params.width = mode_info.width;
+ params.height = mode_info.height;
+ params.bpp = mode_info.bpp;
+ params.pitch = mode_info.pitch;
+ params.flags = 0;
+
+ params.fbaddr = (grub_addr_t) framebuffer;
+
+ params.red_mask_size = mode_info.red_mask_size;
+ params.red_field_pos = mode_info.red_field_pos;
+ params.green_mask_size = mode_info.green_mask_size;
+ params.green_field_pos = mode_info.green_field_pos;
+ params.blue_mask_size = mode_info.blue_mask_size;
+ params.blue_field_pos = mode_info.blue_field_pos;
+
+#ifdef GRUB_MACHINE_PCBIOS
+ /* VESA packed modes may come with zeroed mask sizes, which need
+ to be set here according to DAC Palette width. If we don't,
+ this results in Linux displaying a black screen. */
+ if (mode_info.bpp <= 8 && driv_id == GRUB_VIDEO_DRIVER_VBE)
+ {
+ struct grub_vbe_info_block controller_info;
+ int status;
+ int width = 8;
+
+ status = grub_vbe_bios_get_controller_info (&controller_info);
+
+ if (status == GRUB_VBE_STATUS_OK &&
+ (controller_info.capabilities & GRUB_VBE_CAPABILITY_DACWIDTH))
+ status = grub_vbe_bios_set_dac_palette_width (&width);
+
+ if (status != GRUB_VBE_STATUS_OK)
+ /* 6 is default after mode reset. */
+ width = 6;
+
+ params.red_mask_size = params.green_mask_size
+ = params.blue_mask_size = width;
+ }
+#endif
+
+ err = grub_bsd_add_meta (NETBSD_BTINFO_FRAMEBUF, &params, sizeof (params));
+ return err;
+}
+
+static grub_err_t
+grub_netbsd_add_modules (void)
+{
+ struct netbsd_module *mod;
+ unsigned modcnt = 0;
+ struct grub_netbsd_btinfo_modules *mods;
+ unsigned i;
+ grub_err_t err;
+
+ for (mod = netbsd_mods; mod; mod = mod->next)
+ modcnt++;
+
+ mods = grub_malloc (sizeof (*mods) + sizeof (mods->mods[0]) * modcnt);
+ if (!mods)
+ return grub_errno;
+
+ mods->num = modcnt;
+ mods->last_addr = kern_end;
+ for (mod = netbsd_mods, i = 0; mod; i++, mod = mod->next)
+ mods->mods[i] = mod->mod;
+
+ err = grub_bsd_add_meta (NETBSD_BTINFO_MODULES, mods,
+ sizeof (*mods) + sizeof (mods->mods[0]) * modcnt);
+ grub_free (mods);
+ return err;
+}
+
+static grub_err_t
+grub_netbsd_boot (void)
+{
+ struct grub_netbsd_bootinfo *bootinfo;
+ void *curarg, *arg0;
+ grub_addr_t arg_target, stack_target;
+ grub_uint32_t *stack;
+ grub_err_t err;
+ struct grub_relocator32_state state;
+ grub_size_t tag_buf_len = 0;
+ int tag_count = 0;
+
+ err = grub_bsd_add_mmap ();
+ if (err)
+ return err;
+
+ err = grub_netbsd_setup_video ();
+ if (err)
+ {
+ grub_print_error ();
+ grub_printf ("Booting however\n");
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ err = grub_netbsd_add_modules ();
+ if (err)
+ return err;
+
+ {
+ struct bsd_tag *tag;
+ tag_buf_len = 0;
+ for (tag = tags; tag; tag = tag->next)
+ {
+ tag_buf_len = ALIGN_VAR (tag_buf_len
+ + sizeof (struct grub_netbsd_btinfo_common)
+ + tag->len);
+ tag_count++;
+ }
+ }
+
+ arg_target = kern_end;
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ arg_target, tag_buf_len
+ + sizeof (struct grub_netbsd_bootinfo)
+ + tag_count * sizeof (grub_uint32_t));
+ if (err)
+ return err;
+ curarg = get_virtual_current_address (ch);
+ }
+
+ arg0 = curarg;
+ bootinfo = (void *) ((grub_uint8_t *) arg0 + tag_buf_len);
+
+ {
+ struct bsd_tag *tag;
+ unsigned i;
+
+ bootinfo->bi_count = tag_count;
+ for (tag = tags, i = 0; tag; i++, tag = tag->next)
+ {
+ struct grub_netbsd_btinfo_common *head = curarg;
+ bootinfo->bi_data[i] = ((grub_uint8_t *) curarg - (grub_uint8_t *) arg0)
+ + arg_target;
+ head->type = tag->type;
+ head->len = tag->len + sizeof (*head);
+ curarg = head + 1;
+ grub_memcpy (curarg, tag->data, tag->len);
+ curarg = (grub_uint8_t *) curarg + tag->len;
+ }
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x10000, 0x90000,
+ 7 * sizeof (grub_uint32_t), 4,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ if (err)
+ return err;
+ stack = get_virtual_current_address (ch);
+ stack_target = get_physical_target_address (ch);
+ }
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#endif
+
+ state.eip = entry;
+ state.esp = stack_target;
+ state.ebp = stack_target;
+ stack[0] = entry;
+ stack[1] = bootflags;
+ stack[2] = 0;
+ stack[3] = ((grub_uint8_t *) bootinfo - (grub_uint8_t *) arg0) + arg_target;
+ stack[4] = 0;
+ stack[5] = grub_mmap_get_upper () >> 10;
+ stack[6] = grub_mmap_get_lower () >> 10;
+
+ return grub_relocator32_boot (relocator, state);
+}
+
+static grub_err_t
+grub_bsd_unload (void)
+{
+ struct bsd_tag *tag, *next;
+ for (tag = tags; tag; tag = next)
+ {
+ next = tag->next;
+ grub_free (tag);
+ }
+ tags = NULL;
+ tags_last = NULL;
+
+ kernel_type = KERNEL_TYPE_NONE;
+ grub_dl_unref (my_mod);
+
+ grub_relocator_unload (relocator);
+ relocator = NULL;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_bsd_load_aout (grub_file_t file)
+{
+ grub_addr_t load_addr, load_end;
+ int ofs, align_page;
+ union grub_aout_header ah;
+ grub_err_t err;
+ grub_size_t bss_size;
+
+ if ((grub_file_seek (file, 0)) == (grub_off_t) - 1)
+ return grub_errno;
+
+ if (grub_file_read (file, &ah, sizeof (ah)) != sizeof (ah))
+ return grub_error (GRUB_ERR_READ_ERROR, "cannot read the a.out header");
+
+ if (grub_aout_get_type (&ah) != AOUT_TYPE_AOUT32)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid a.out header");
+
+ entry = ah.aout32.a_entry & 0xFFFFFF;
+
+ if (AOUT_GETMAGIC (ah.aout32) == AOUT32_ZMAGIC)
+ {
+ load_addr = entry;
+ ofs = 0x1000;
+ align_page = 0;
+ }
+ else
+ {
+ load_addr = entry & 0xF00000;
+ ofs = sizeof (struct grub_aout32_header);
+ align_page = 1;
+ }
+
+ if (load_addr < 0x100000)
+ return grub_error (GRUB_ERR_BAD_OS, "load address below 1M");
+
+ kern_start = load_addr;
+ load_end = kern_end = load_addr + ah.aout32.a_text + ah.aout32.a_data;
+ if (align_page)
+ kern_end = ALIGN_PAGE (kern_end);
+
+ if (ah.aout32.a_bss)
+ {
+ kern_end += ah.aout32.a_bss;
+ if (align_page)
+ kern_end = ALIGN_PAGE (kern_end);
+
+ bss_size = kern_end - load_end;
+ }
+ else
+ bss_size = 0;
+
+ {
+ grub_relocator_chunk_t ch;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ kern_start, kern_end - kern_start);
+ if (err)
+ return err;
+ kern_chunk_src = get_virtual_current_address (ch);
+ }
+
+ return grub_aout_load (file, ofs, kern_chunk_src,
+ ah.aout32.a_text + ah.aout32.a_data,
+ bss_size);
+}
+
+static int NESTED_FUNC_ATTR
+grub_bsd_elf32_size_hook (grub_elf_t elf __attribute__ ((unused)),
+ Elf32_Phdr *phdr, void *arg __attribute__ ((unused)))
+{
+ Elf32_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ return 0;
+
+ paddr = phdr->p_paddr & 0xFFFFFF;
+
+ if (paddr < kern_start)
+ kern_start = paddr;
+
+ if (paddr + phdr->p_memsz > kern_end)
+ kern_end = paddr + phdr->p_memsz;
+
+ return 0;
+}
+
+static grub_err_t
+grub_bsd_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr, int *do_load)
+{
+ Elf32_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ {
+ *do_load = 0;
+ return 0;
+ }
+
+ *do_load = 1;
+ phdr->p_paddr &= 0xFFFFFF;
+ paddr = phdr->p_paddr;
+
+ *addr = (grub_addr_t) (paddr - kern_start + (grub_uint8_t *) kern_chunk_src);
+
+ return GRUB_ERR_NONE;
+}
+
+static int NESTED_FUNC_ATTR
+grub_bsd_elf64_size_hook (grub_elf_t elf __attribute__ ((unused)),
+ Elf64_Phdr *phdr, void *arg __attribute__ ((unused)))
+{
+ Elf64_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ return 0;
+
+ paddr = phdr->p_paddr & 0xffffff;
+
+ if (paddr < kern_start)
+ kern_start = paddr;
+
+ if (paddr + phdr->p_memsz > kern_end)
+ kern_end = paddr + phdr->p_memsz;
+
+ return 0;
+}
+
+static grub_err_t
+grub_bsd_elf64_hook (Elf64_Phdr * phdr, grub_addr_t * addr, int *do_load)
+{
+ Elf64_Addr paddr;
+
+ if (phdr->p_type != PT_LOAD
+ && phdr->p_type != PT_DYNAMIC)
+ {
+ *do_load = 0;
+ return 0;
+ }
+
+ *do_load = 1;
+ paddr = phdr->p_paddr & 0xffffff;
+
+ *addr = (grub_addr_t) (paddr - kern_start + (grub_uint8_t *) kern_chunk_src);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_bsd_load_elf (grub_elf_t elf)
+{
+ grub_err_t err;
+
+ kern_end = 0;
+ kern_start = ~0;
+
+ if (grub_elf_is_elf32 (elf))
+ {
+ grub_relocator_chunk_t ch;
+
+ entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFF;
+ err = grub_elf32_phdr_iterate (elf, grub_bsd_elf32_size_hook, NULL);
+ if (err)
+ return err;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ kern_start, kern_end - kern_start);
+ if (err)
+ return err;
+
+ kern_chunk_src = get_virtual_current_address (ch);
+
+ err = grub_elf32_load (elf, grub_bsd_elf32_hook, 0, 0);
+ if (err)
+ return err;
+ if (kernel_type != KERNEL_TYPE_OPENBSD)
+ return GRUB_ERR_NONE;
+ return grub_openbsd_find_ramdisk32 (elf->file, kern_start,
+ kern_chunk_src, &openbsd_ramdisk);
+ }
+ else if (grub_elf_is_elf64 (elf))
+ {
+ is_64bit = 1;
+
+ if (! grub_cpuid_has_longmode)
+ return grub_error (GRUB_ERR_BAD_OS, "your CPU does not implement AMD64 architecture");
+
+ /* FreeBSD has 64-bit entry point. */
+ if (kernel_type == KERNEL_TYPE_FREEBSD)
+ {
+ entry = elf->ehdr.ehdr64.e_entry & 0xffffffff;
+ entry_hi = (elf->ehdr.ehdr64.e_entry >> 32) & 0xffffffff;
+ }
+ else
+ {
+ entry = elf->ehdr.ehdr64.e_entry & 0x0fffffff;
+ entry_hi = 0;
+ }
+
+ err = grub_elf64_phdr_iterate (elf, grub_bsd_elf64_size_hook, NULL);
+ if (err)
+ return err;
+
+ grub_dprintf ("bsd", "kern_start = %lx, kern_end = %lx\n",
+ (unsigned long) kern_start, (unsigned long) kern_end);
+ {
+ grub_relocator_chunk_t ch;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start,
+ kern_end - kern_start);
+ if (err)
+ return err;
+ kern_chunk_src = get_virtual_current_address (ch);
+ }
+
+ err = grub_elf64_load (elf, grub_bsd_elf64_hook, 0, 0);
+ if (err)
+ return err;
+ if (kernel_type != KERNEL_TYPE_OPENBSD)
+ return GRUB_ERR_NONE;
+ return grub_openbsd_find_ramdisk64 (elf->file, kern_start,
+ kern_chunk_src, &openbsd_ramdisk);
+ }
+ else
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+}
+
+static grub_err_t
+grub_bsd_load (int argc, char *argv[])
+{
+ grub_file_t file;
+ grub_elf_t elf;
+
+ grub_dl_ref (my_mod);
+
+ grub_loader_unset ();
+
+ grub_memset (&openbsd_ramdisk, 0, sizeof (openbsd_ramdisk));
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0]);
+ if (!file)
+ goto fail;
+
+ relocator = grub_relocator_new ();
+
+ elf = grub_elf_file (file);
+ if (elf)
+ {
+ is_elf_kernel = 1;
+ grub_bsd_load_elf (elf);
+ grub_elf_close (elf);
+ }
+ else
+ {
+ is_elf_kernel = 0;
+ grub_errno = 0;
+ grub_bsd_load_aout (file);
+ grub_file_close (file);
+ }
+
+ kern_end = ALIGN_PAGE (kern_end);
+
+fail:
+
+ if (grub_errno != GRUB_ERR_NONE)
+ grub_dl_unref (my_mod);
+
+ return grub_errno;
+}
+
+static grub_uint32_t
+grub_bsd_parse_flags (const struct grub_arg_list *state,
+ const grub_uint32_t * flags)
+{
+ grub_uint32_t result = 0;
+ unsigned i;
+
+ for (i = 0; flags[i]; i++)
+ if (state[i].set)
+ result |= flags[i];
+
+ return result;
+}
+
+static grub_err_t
+grub_cmd_freebsd (grub_extcmd_context_t ctxt, int argc, char *argv[])
+{
+ kernel_type = KERNEL_TYPE_FREEBSD;
+ bootflags = grub_bsd_parse_flags (ctxt->state, freebsd_flags);
+
+ if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
+ {
+ grub_uint32_t unit, slice, part;
+
+ kern_end = ALIGN_PAGE (kern_end);
+ if (is_elf_kernel)
+ {
+ grub_err_t err;
+ grub_uint64_t data = 0;
+ grub_file_t file;
+ int len = is_64bit ? 8 : 4;
+
+ err = grub_freebsd_add_meta_module (argv[0], is_64bit
+ ? FREEBSD_MODTYPE_KERNEL64
+ : FREEBSD_MODTYPE_KERNEL,
+ argc - 1, argv + 1,
+ kern_start,
+ kern_end - kern_start);
+ if (err)
+ return err;
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ return grub_errno;
+
+ if (is_64bit)
+ err = grub_freebsd_load_elf_meta64 (relocator, file, &kern_end);
+ else
+ err = grub_freebsd_load_elf_meta32 (relocator, file, &kern_end);
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_HOWTO, &data, 4);
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_ENVP, &data, len);
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_KERNEND, &data, len);
+ if (err)
+ return err;
+ }
+ grub_bsd_get_device (&freebsd_biosdev, &unit, &slice, &part);
+ freebsd_bootdev = (FREEBSD_B_DEVMAGIC + ((slice + 1) << FREEBSD_B_SLICESHIFT) +
+ (unit << FREEBSD_B_UNITSHIFT) + (part << FREEBSD_B_PARTSHIFT));
+
+ grub_loader_set (grub_freebsd_boot, grub_bsd_unload, 0);
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_openbsd (grub_extcmd_context_t ctxt, int argc, char *argv[])
+{
+ grub_uint32_t bootdev;
+
+ kernel_type = KERNEL_TYPE_OPENBSD;
+ bootflags = grub_bsd_parse_flags (ctxt->state, openbsd_flags);
+
+ if (ctxt->state[OPENBSD_ROOT_ARG].set)
+ {
+ const char *arg = ctxt->state[OPENBSD_ROOT_ARG].arg;
+ int unit, part;
+ if (*(arg++) != 'w' || *(arg++) != 'd')
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only device specifications of form "
+ "wd<number><lowercase letter> are supported");
+
+ unit = grub_strtoul (arg, (char **) &arg, 10);
+ if (! (arg && *arg >= 'a' && *arg <= 'z'))
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only device specifications of form "
+ "wd<number><lowercase letter> are supported");
+
+ part = *arg - 'a';
+
+ bootdev = (OPENBSD_B_DEVMAGIC + (unit << OPENBSD_B_UNITSHIFT) +
+ (part << OPENBSD_B_PARTSHIFT));
+ }
+ else
+ bootdev = 0;
+
+ if (ctxt->state[OPENBSD_SERIAL_ARG].set)
+ {
+ struct grub_openbsd_bootarg_console serial;
+ char *ptr;
+ unsigned port = 0;
+ unsigned speed = 9600;
+
+ grub_memset (&serial, 0, sizeof (serial));
+
+ if (ctxt->state[OPENBSD_SERIAL_ARG].arg)
+ {
+ ptr = ctxt->state[OPENBSD_SERIAL_ARG].arg;
+ if (grub_memcmp (ptr, "com", sizeof ("com") - 1) != 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only com0-com3 are supported");
+ ptr += sizeof ("com") - 1;
+ port = grub_strtoul (ptr, &ptr, 0);
+ if (port >= 4)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only com0-com3 are supported");
+ if (*ptr == ',')
+ {
+ ptr++;
+ speed = grub_strtoul (ptr, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+ }
+ }
+
+ serial.device = (GRUB_OPENBSD_COM_MAJOR << 8) | port;
+ serial.speed = speed;
+
+ grub_bsd_add_meta (OPENBSD_BOOTARG_CONSOLE, &serial, sizeof (serial));
+ bootflags |= OPENBSD_RB_SERCONS;
+ }
+ else
+ {
+ struct grub_openbsd_bootarg_console serial;
+
+ grub_memset (&serial, 0, sizeof (serial));
+ serial.device = (GRUB_OPENBSD_VGA_MAJOR << 8);
+ grub_bsd_add_meta (OPENBSD_BOOTARG_CONSOLE, &serial, sizeof (serial));
+ bootflags &= ~OPENBSD_RB_SERCONS;
+ }
+
+ if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
+ {
+ grub_loader_set (grub_openbsd_boot, grub_bsd_unload, 0);
+ openbsd_root = bootdev;
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_netbsd (grub_extcmd_context_t ctxt, int argc, char *argv[])
+{
+ grub_err_t err;
+ kernel_type = KERNEL_TYPE_NETBSD;
+ bootflags = grub_bsd_parse_flags (ctxt->state, netbsd_flags);
+
+ if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE)
+ {
+ if (is_elf_kernel)
+ {
+ grub_file_t file;
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ return grub_errno;
+
+ if (is_64bit)
+ err = grub_netbsd_load_elf_meta64 (relocator, file, &kern_end);
+ else
+ err = grub_netbsd_load_elf_meta32 (relocator, file, &kern_end);
+ if (err)
+ return err;
+ }
+
+ {
+ char bootpath[GRUB_NETBSD_MAX_BOOTPATH_LEN];
+ char *name;
+ name = grub_strrchr (argv[0], '/');
+ if (name)
+ name++;
+ else
+ name = argv[0];
+ grub_memset (bootpath, 0, sizeof (bootpath));
+ grub_strncpy (bootpath, name, sizeof (bootpath) - 1);
+ grub_bsd_add_meta (NETBSD_BTINFO_BOOTPATH, bootpath, sizeof (bootpath));
+ }
+
+ if (ctxt->state[NETBSD_ROOT_ARG].set)
+ {
+ char root[GRUB_NETBSD_MAX_ROOTDEVICE_LEN];
+ grub_memset (root, 0, sizeof (root));
+ grub_strncpy (root, ctxt->state[NETBSD_ROOT_ARG].arg,
+ sizeof (root) - 1);
+ grub_bsd_add_meta (NETBSD_BTINFO_ROOTDEVICE, root, sizeof (root));
+ }
+ if (ctxt->state[NETBSD_SERIAL_ARG].set)
+ {
+ struct grub_netbsd_btinfo_serial serial;
+ char *ptr;
+
+ grub_memset (&serial, 0, sizeof (serial));
+ grub_strcpy (serial.devname, "com");
+
+ serial.addr = grub_ns8250_hw_get_port (0);
+ serial.speed = 9600;
+
+ if (ctxt->state[NETBSD_SERIAL_ARG].arg)
+ {
+ ptr = ctxt->state[NETBSD_SERIAL_ARG].arg;
+ if (grub_memcmp (ptr, "com", sizeof ("com") - 1) == 0)
+ {
+ ptr += sizeof ("com") - 1;
+ serial.addr
+ = grub_ns8250_hw_get_port (grub_strtoul (ptr, &ptr, 0));
+ }
+ else
+ serial.addr = grub_strtoul (ptr, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+
+ if (*ptr == ',')
+ {
+ ptr++;
+ serial.speed = grub_strtoul (ptr, &ptr, 0);
+ if (grub_errno)
+ return grub_errno;
+ }
+ }
+
+ grub_bsd_add_meta (NETBSD_BTINFO_CONSOLE, &serial, sizeof (serial));
+ }
+ else
+ {
+ struct grub_netbsd_btinfo_serial cons;
+
+ grub_memset (&cons, 0, sizeof (cons));
+ grub_strcpy (cons.devname, "pc");
+
+ grub_bsd_add_meta (NETBSD_BTINFO_CONSOLE, &cons, sizeof (cons));
+ }
+
+ grub_loader_set (grub_netbsd_boot, grub_bsd_unload, 0);
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_freebsd_loadenv (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ char *buf = 0, *curr, *next;
+ int len;
+
+ if (! grub_loader_is_loaded ())
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "you need to load the kernel first");
+
+ if (kernel_type != KERNEL_TYPE_FREEBSD)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only FreeBSD supports environment");
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no filename");
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0]);
+ if ((!file) || (!file->size))
+ goto fail;
+
+ len = file->size;
+ buf = grub_malloc (len + 1);
+ if (!buf)
+ goto fail;
+
+ if (grub_file_read (file, buf, len) != len)
+ goto fail;
+
+ buf[len] = 0;
+
+ next = buf;
+ while (next)
+ {
+ char *p;
+
+ curr = next;
+ next = grub_strchr (curr, '\n');
+ if (next)
+ {
+
+ p = next - 1;
+ while (p > curr)
+ {
+ if ((*p != '\r') && (*p != ' ') && (*p != '\t'))
+ break;
+ p--;
+ }
+
+ if ((p > curr) && (*p == '"'))
+ p--;
+
+ *(p + 1) = 0;
+ next++;
+ }
+
+ if (*curr == '#')
+ continue;
+
+ p = grub_strchr (curr, '=');
+ if (!p)
+ continue;
+
+ *(p++) = 0;
+
+ if (*curr)
+ {
+ char *name;
+
+ if (*p == '"')
+ p++;
+
+ name = grub_xasprintf ("kFreeBSD.%s", curr);
+ if (!name)
+ goto fail;
+ if (grub_env_set (name, p))
+ {
+ grub_free (name);
+ goto fail;
+ }
+ grub_free (name);
+ }
+ }
+
+fail:
+ grub_free (buf);
+
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ int modargc;
+ char **modargv;
+ char *type;
+ grub_err_t err;
+ void *src;
+
+ if (kernel_type != KERNEL_TYPE_FREEBSD)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no FreeBSD loaded");
+
+ if (!is_elf_kernel)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only ELF kernel supports module");
+
+ /* List the current modules if no parameter. */
+ if (!argc)
+ {
+ grub_freebsd_list_modules ();
+ return 0;
+ }
+
+ file = grub_file_open (argv[0]);
+ if ((!file) || (!file->size))
+ goto fail;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_end,
+ file->size);
+ if (err)
+ goto fail;
+ src = get_virtual_current_address (ch);
+ }
+
+
+ grub_file_read (file, src, file->size);
+ if (grub_errno)
+ goto fail;
+
+ modargc = argc - 1;
+ modargv = argv + 1;
+
+ if (modargc && (! grub_memcmp (modargv[0], "type=", 5)))
+ {
+ type = &modargv[0][5];
+ modargc--;
+ modargv++;
+ }
+ else
+ type = FREEBSD_MODTYPE_RAW;
+
+ err = grub_freebsd_add_meta_module (argv[0], type, modargc, modargv,
+ kern_end, file->size);
+ if (err)
+ goto fail;
+
+ kern_end = ALIGN_PAGE (kern_end + file->size);
+
+fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_netbsd_module_load (char *filename, grub_uint32_t type)
+{
+ grub_file_t file = 0;
+ void *src;
+ grub_err_t err;
+
+ file = grub_file_open (filename);
+ if ((!file) || (!file->size))
+ goto fail;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_end,
+ file->size);
+ if (err)
+ goto fail;
+
+ src = get_virtual_current_address (ch);
+ }
+
+ grub_file_read (file, src, file->size);
+ if (grub_errno)
+ goto fail;
+
+ err = grub_netbsd_add_meta_module (filename, type, kern_end, file->size);
+
+ if (err)
+ goto fail;
+
+ kern_end = ALIGN_PAGE (kern_end + file->size);
+
+fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_netbsd_module (grub_command_t cmd,
+ int argc, char *argv[])
+{
+ grub_uint32_t type;
+
+ if (kernel_type != KERNEL_TYPE_NETBSD)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no NetBSD loaded");
+
+ if (!is_elf_kernel)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only ELF kernel supports module");
+
+ /* List the current modules if no parameter. */
+ if (!argc)
+ {
+ grub_netbsd_list_modules ();
+ return 0;
+ }
+
+ if (grub_strcmp (cmd->name, "knetbsd_module_elf") == 0)
+ type = GRUB_NETBSD_MODULE_ELF;
+ else
+ type = GRUB_NETBSD_MODULE_RAW;
+
+ return grub_netbsd_module_load (argv[0], type);
+}
+
+static grub_err_t
+grub_cmd_freebsd_module_elf (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_err_t err;
+
+ if (! grub_loader_is_loaded ())
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "you need to load the kernel first");
+
+ if (kernel_type != KERNEL_TYPE_FREEBSD)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only FreeBSD supports module");
+
+ if (! is_elf_kernel)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "only ELF kernel supports module");
+
+ /* List the current modules if no parameter. */
+ if (! argc)
+ {
+ grub_freebsd_list_modules ();
+ return 0;
+ }
+
+ file = grub_file_open (argv[0]);
+ if (!file)
+ return grub_errno;
+ if (!file->size)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+
+ if (is_64bit)
+ err = grub_freebsd_load_elfmodule_obj64 (relocator, file,
+ argc, argv, &kern_end);
+ else
+ err = grub_freebsd_load_elfmodule32 (relocator, file,
+ argc, argv, &kern_end);
+ grub_file_close (file);
+
+ return err;
+}
+
+static grub_err_t
+grub_cmd_openbsd_ramdisk (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ grub_size_t size;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ if (kernel_type != KERNEL_TYPE_OPENBSD)
+ return grub_error (GRUB_ERR_BAD_OS, "no kOpenBSD loaded");
+
+ if (!openbsd_ramdisk.max_size)
+ return grub_error (GRUB_ERR_BAD_OS, "your kOpenBSD doesn't support ramdisk");
+
+ file = grub_file_open (args[0]);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "couldn't load ramdisk");
+
+ size = grub_file_size (file);
+
+ if (size > openbsd_ramdisk.max_size)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_BAD_OS, "your kOpenBSD supports ramdisk only"
+ " up to %u bytes, however you supplied a %u bytes one",
+ openbsd_ramdisk.max_size, size);
+ }
+
+ if (grub_file_read (file, openbsd_ramdisk.target, size)
+ != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
+ }
+ grub_memset (openbsd_ramdisk.target + size, 0,
+ openbsd_ramdisk.max_size - size);
+ *openbsd_ramdisk.size = ALIGN_UP (size, 512);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_extcmd_t cmd_freebsd, cmd_openbsd, cmd_netbsd;
+static grub_command_t cmd_freebsd_loadenv, cmd_freebsd_module;
+static grub_command_t cmd_netbsd_module, cmd_freebsd_module_elf;
+static grub_command_t cmd_netbsd_module_elf, cmd_openbsd_ramdisk;
+
+GRUB_MOD_INIT (bsd)
+{
+ /* Net and OpenBSD kernels are often compressed. */
+ grub_dl_load ("gzio");
+
+ cmd_freebsd = grub_register_extcmd ("kfreebsd", grub_cmd_freebsd, 0,
+ N_("FILE"), N_("Load kernel of FreeBSD."),
+ freebsd_opts);
+ cmd_openbsd = grub_register_extcmd ("kopenbsd", grub_cmd_openbsd, 0,
+ N_("FILE"), N_("Load kernel of OpenBSD."),
+ openbsd_opts);
+ cmd_netbsd = grub_register_extcmd ("knetbsd", grub_cmd_netbsd, 0,
+ N_("FILE"), N_("Load kernel of NetBSD."),
+ netbsd_opts);
+ cmd_freebsd_loadenv =
+ grub_register_command ("kfreebsd_loadenv", grub_cmd_freebsd_loadenv,
+ 0, N_("Load FreeBSD env."));
+ cmd_freebsd_module =
+ grub_register_command ("kfreebsd_module", grub_cmd_freebsd_module,
+ 0, N_("Load FreeBSD kernel module."));
+ cmd_netbsd_module =
+ grub_register_command ("knetbsd_module", grub_cmd_netbsd_module,
+ 0, N_("Load NetBSD kernel module."));
+ cmd_netbsd_module_elf =
+ grub_register_command ("knetbsd_module_elf", grub_cmd_netbsd_module,
+ 0, N_("Load NetBSD kernel module (ELF)."));
+ cmd_freebsd_module_elf =
+ grub_register_command ("kfreebsd_module_elf", grub_cmd_freebsd_module_elf,
+ 0, N_("Load FreeBSD kernel module (ELF)."));
+
+ cmd_openbsd_ramdisk = grub_register_command ("kopenbsd_ramdisk",
+ grub_cmd_openbsd_ramdisk, 0,
+ "Load kOpenBSD ramdisk. ");
+
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI (bsd)
+{
+ grub_unregister_extcmd (cmd_freebsd);
+ grub_unregister_extcmd (cmd_openbsd);
+ grub_unregister_extcmd (cmd_netbsd);
+
+ grub_unregister_command (cmd_freebsd_loadenv);
+ grub_unregister_command (cmd_freebsd_module);
+ grub_unregister_command (cmd_netbsd_module);
+ grub_unregister_command (cmd_freebsd_module_elf);
+ grub_unregister_command (cmd_netbsd_module_elf);
+ grub_unregister_command (cmd_openbsd_ramdisk);
+
+ grub_bsd_unload ();
+}
diff --git a/grub-core/loader/i386/bsd32.c b/grub-core/loader/i386/bsd32.c
new file mode 100644
index 0000000..26704c4
--- /dev/null
+++ b/grub-core/loader/i386/bsd32.c
@@ -0,0 +1,6 @@
+#define SUFFIX(x) x ## 32
+#define GRUB_TARGET_WORDSIZE 32
+#define OBJSYM 0
+#include <grub/types.h>
+typedef grub_uint32_t grub_freebsd_addr_t;
+#include "bsdXX.c"
diff --git a/grub-core/loader/i386/bsd64.c b/grub-core/loader/i386/bsd64.c
new file mode 100644
index 0000000..f8aad1c
--- /dev/null
+++ b/grub-core/loader/i386/bsd64.c
@@ -0,0 +1,6 @@
+#define SUFFIX(x) x ## 64
+#define GRUB_TARGET_WORDSIZE 64
+#define OBJSYM 1
+#include <grub/types.h>
+typedef grub_uint64_t grub_freebsd_addr_t;
+#include "bsdXX.c"
diff --git a/grub-core/loader/i386/bsdXX.c b/grub-core/loader/i386/bsdXX.c
new file mode 100644
index 0000000..92d2675
--- /dev/null
+++ b/grub-core/loader/i386/bsdXX.c
@@ -0,0 +1,621 @@
+#include <grub/loader.h>
+#include <grub/i386/bsd.h>
+#include <grub/mm.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/i386/relocator.h>
+
+#define ALIGN_PAGE(a) ALIGN_UP (a, 4096)
+
+static inline grub_err_t
+load (grub_file_t file, void *where, grub_off_t off, grub_size_t size)
+{
+ if (grub_file_seek (file, off) == (grub_off_t) -1)
+ return grub_errno;
+ if (grub_file_read (file, where, size) != (grub_ssize_t) size)
+ {
+ if (grub_errno)
+ return grub_errno;
+ else
+ return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
+ }
+ return GRUB_ERR_NONE;
+}
+
+static inline grub_err_t
+read_headers (grub_file_t file, Elf_Ehdr *e, char **shdr)
+{
+ if (grub_file_seek (file, 0) == (grub_off_t) -1)
+ return grub_errno;
+
+ if (grub_file_read (file, (char *) e, sizeof (*e)) != sizeof (*e))
+ {
+ if (grub_errno)
+ return grub_errno;
+ else
+ return grub_error (GRUB_ERR_BAD_OS, "file is too short");
+ }
+
+ if (e->e_ident[EI_MAG0] != ELFMAG0
+ || e->e_ident[EI_MAG1] != ELFMAG1
+ || e->e_ident[EI_MAG2] != ELFMAG2
+ || e->e_ident[EI_MAG3] != ELFMAG3
+ || e->e_ident[EI_VERSION] != EV_CURRENT
+ || e->e_version != EV_CURRENT)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
+
+ if (e->e_ident[EI_CLASS] != SUFFIX (ELFCLASS))
+ return grub_error (GRUB_ERR_BAD_OS, "invalid arch dependent ELF magic");
+
+ *shdr = grub_malloc (e->e_shnum * e->e_shentsize);
+ if (! *shdr)
+ return grub_errno;
+
+ if (grub_file_seek (file, e->e_shoff) == (grub_off_t) -1)
+ return grub_errno;
+
+ if (grub_file_read (file, *shdr, e->e_shnum * e->e_shentsize)
+ != e->e_shnum * e->e_shentsize)
+ {
+ if (grub_errno)
+ return grub_errno;
+ else
+ return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/* On i386 FreeBSD uses "elf module" approarch for 32-bit variant
+ and "elf obj module" for 64-bit variant. However it may differ on other
+ platforms. So I keep both versions. */
+#if OBJSYM
+grub_err_t
+SUFFIX (grub_freebsd_load_elfmodule_obj) (struct grub_relocator *relocator,
+ grub_file_t file, int argc,
+ char *argv[], grub_addr_t *kern_end)
+{
+ Elf_Ehdr e;
+ Elf_Shdr *s;
+ char *shdr = 0;
+ grub_addr_t curload, module;
+ grub_err_t err;
+ grub_size_t chunk_size = 0;
+ void *chunk_src;
+
+ err = read_headers (file, &e, &shdr);
+ if (err)
+ return err;
+
+ curload = module = ALIGN_PAGE (*kern_end);
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ if (s->sh_size == 0)
+ continue;
+
+ if (s->sh_addralign)
+ chunk_size = ALIGN_UP (chunk_size + *kern_end, s->sh_addralign)
+ - *kern_end;
+
+ chunk_size += s->sh_size;
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ module, chunk_size);
+ if (err)
+ return err;
+ chunk_src = get_virtual_current_address (ch);
+ }
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ if (s->sh_size == 0)
+ continue;
+
+ if (s->sh_addralign)
+ curload = ALIGN_UP (curload, s->sh_addralign);
+ s->sh_addr = curload;
+
+ grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n",
+ (unsigned) curload, (int) s->sh_size,
+ (int) s->sh_addralign);
+
+ switch (s->sh_type)
+ {
+ default:
+ case SHT_PROGBITS:
+ err = load (file, (grub_uint8_t *) chunk_src + curload - *kern_end,
+ s->sh_offset, s->sh_size);
+ if (err)
+ return err;
+ break;
+ case SHT_NOBITS:
+ grub_memset ((grub_uint8_t *) chunk_src + curload - *kern_end, 0,
+ s->sh_size);
+ break;
+ }
+ curload += s->sh_size;
+ }
+
+ *kern_end = ALIGN_PAGE (curload);
+
+ err = grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE_OBJ,
+ argc - 1, argv + 1, module,
+ curload - module);
+ if (! err)
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA
+ | FREEBSD_MODINFOMD_ELFHDR,
+ &e, sizeof (e));
+ if (! err)
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA
+ | FREEBSD_MODINFOMD_SHDR,
+ shdr, e.e_shnum * e.e_shentsize);
+
+ return err;
+}
+
+#else
+
+grub_err_t
+SUFFIX (grub_freebsd_load_elfmodule) (struct grub_relocator *relocator,
+ grub_file_t file, int argc, char *argv[],
+ grub_addr_t *kern_end)
+{
+ Elf_Ehdr e;
+ Elf_Shdr *s;
+ char *shdr = 0;
+ grub_addr_t curload, module;
+ grub_err_t err;
+ grub_size_t chunk_size = 0;
+ void *chunk_src;
+
+ err = read_headers (file, &e, &shdr);
+ if (err)
+ return err;
+
+ curload = module = ALIGN_PAGE (*kern_end);
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ if (s->sh_size == 0)
+ continue;
+
+ if (! (s->sh_flags & SHF_ALLOC))
+ continue;
+ if (chunk_size < s->sh_addr + s->sh_size)
+ chunk_size = s->sh_addr + s->sh_size;
+ }
+
+ if (chunk_size < sizeof (e))
+ chunk_size = sizeof (e);
+ chunk_size += e.e_phnum * e.e_phentsize;
+ chunk_size += e.e_shnum * e.e_shentsize;
+
+ {
+ grub_relocator_chunk_t ch;
+
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ module, chunk_size);
+ if (err)
+ return err;
+
+ chunk_src = get_virtual_current_address (ch);
+ }
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ if (s->sh_size == 0)
+ continue;
+
+ if (! (s->sh_flags & SHF_ALLOC))
+ continue;
+
+ grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n",
+ (unsigned) curload, (int) s->sh_size,
+ (int) s->sh_addralign);
+
+ switch (s->sh_type)
+ {
+ default:
+ case SHT_PROGBITS:
+ err = load (file, (grub_uint8_t *) chunk_src + module
+ + s->sh_addr - *kern_end,
+ s->sh_offset, s->sh_size);
+ if (err)
+ return err;
+ break;
+ case SHT_NOBITS:
+ grub_memset ((grub_uint8_t *) chunk_src + module
+ + s->sh_addr - *kern_end, 0, s->sh_size);
+ break;
+ }
+ if (curload < module + s->sh_addr + s->sh_size)
+ curload = module + s->sh_addr + s->sh_size;
+ }
+
+ load (file, UINT_TO_PTR (module), 0, sizeof (e));
+ if (curload < module + sizeof (e))
+ curload = module + sizeof (e);
+
+ load (file, UINT_TO_PTR (curload), e.e_shoff,
+ e.e_shnum * e.e_shentsize);
+ e.e_shoff = curload - module;
+ curload += e.e_shnum * e.e_shentsize;
+
+ load (file, UINT_TO_PTR (curload), e.e_phoff,
+ e.e_phnum * e.e_phentsize);
+ e.e_phoff = curload - module;
+ curload += e.e_phnum * e.e_phentsize;
+
+ *kern_end = curload;
+
+ grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE,
+ argc - 1, argv + 1, module,
+ curload - module);
+ return SUFFIX (grub_freebsd_load_elf_meta) (relocator, file, kern_end);
+}
+
+#endif
+
+grub_err_t
+SUFFIX (grub_freebsd_load_elf_meta) (struct grub_relocator *relocator,
+ grub_file_t file, grub_addr_t *kern_end)
+{
+ grub_err_t err;
+ Elf_Ehdr e;
+ Elf_Shdr *s;
+ char *shdr = 0;
+ unsigned symoff, stroff, symsize, strsize;
+ grub_freebsd_addr_t symstart, symend, symentsize, dynamic;
+ Elf_Sym *sym;
+ void *sym_chunk;
+ grub_uint8_t *curload;
+ grub_freebsd_addr_t symtarget;
+ const char *str;
+ unsigned i;
+ grub_size_t chunk_size;
+
+ err = read_headers (file, &e, &shdr);
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_ELFHDR, &e,
+ sizeof (e));
+ if (err)
+ return err;
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+ if (s >= (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize))
+ return grub_error (GRUB_ERR_BAD_OS, "no symbol table");
+ symoff = s->sh_offset;
+ symsize = s->sh_size;
+ symentsize = s->sh_entsize;
+ s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
+ stroff = s->sh_offset;
+ strsize = s->sh_size;
+
+ chunk_size = ALIGN_UP (symsize + strsize, sizeof (grub_freebsd_addr_t))
+ + 2 * sizeof (grub_freebsd_addr_t);
+
+ symtarget = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t));
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ symtarget, chunk_size);
+ if (err)
+ return err;
+ sym_chunk = get_virtual_current_address (ch);
+ }
+
+ symstart = symtarget;
+ symend = symstart + chunk_size;
+
+ curload = sym_chunk;
+ *((grub_freebsd_addr_t *) curload) = symsize;
+ curload += sizeof (grub_freebsd_addr_t);
+
+ if (grub_file_seek (file, symoff) == (grub_off_t) -1)
+ return grub_errno;
+ sym = (Elf_Sym *) curload;
+ if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize)
+ {
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+ curload += symsize;
+
+ *((grub_freebsd_addr_t *) curload) = strsize;
+ curload += sizeof (grub_freebsd_addr_t);
+ if (grub_file_seek (file, stroff) == (grub_off_t) -1)
+ return grub_errno;
+ str = (char *) curload;
+ if (grub_file_read (file, curload, strsize) != (grub_ssize_t) strsize)
+ {
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+
+ for (i = 0;
+ i * symentsize < symsize;
+ i++, sym = (Elf_Sym *) ((char *) sym + symentsize))
+ {
+ const char *name = str + sym->st_name;
+ if (grub_strcmp (name, "_DYNAMIC") == 0)
+ break;
+ }
+
+ if (i * symentsize < symsize)
+ {
+ dynamic = sym->st_value;
+ grub_dprintf ("bsd", "dynamic = %llx\n", (unsigned long long) dynamic);
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_DYNAMIC, &dynamic,
+ sizeof (dynamic));
+ if (err)
+ return err;
+ }
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_SSYM, &symstart,
+ sizeof (symstart));
+ if (err)
+ return err;
+
+ err = grub_bsd_add_meta (FREEBSD_MODINFO_METADATA |
+ FREEBSD_MODINFOMD_ESYM, &symend,
+ sizeof (symend));
+ if (err)
+ return err;
+
+ *kern_end = ALIGN_PAGE (symend);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+SUFFIX (grub_netbsd_load_elf_meta) (struct grub_relocator *relocator,
+ grub_file_t file, grub_addr_t *kern_end)
+{
+ grub_err_t err;
+ Elf_Ehdr e;
+ Elf_Shdr *s, *symsh, *strsh;
+ char *shdr = NULL;
+ unsigned symsize, strsize;
+ void *sym_chunk;
+ grub_uint8_t *curload;
+ grub_size_t chunk_size;
+ Elf_Ehdr *e2;
+ struct grub_netbsd_btinfo_symtab symtab;
+ grub_addr_t symtarget;
+
+ err = read_headers (file, &e, &shdr);
+ if (err)
+ return err;
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+ if (s >= (Elf_Shdr *) ((char *) shdr
+ + e.e_shnum * e.e_shentsize))
+ return GRUB_ERR_NONE;
+ symsize = s->sh_size;
+ symsh = s;
+ s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
+ strsize = s->sh_size;
+ strsh = s;
+
+ chunk_size = ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t))
+ + ALIGN_UP (strsize, sizeof (grub_freebsd_addr_t))
+ + sizeof (e) + e.e_shnum * e.e_shentsize;
+
+ symtarget = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t));
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ symtarget, chunk_size);
+ if (err)
+ return err;
+ sym_chunk = get_virtual_current_address (ch);
+ }
+
+ symtab.nsyms = 1;
+ symtab.ssyms = symtarget;
+ symtab.esyms = symtarget + chunk_size;
+
+ curload = sym_chunk;
+
+ e2 = (Elf_Ehdr *) curload;
+ grub_memcpy (curload, &e, sizeof (e));
+ e2->e_phoff = 0;
+ e2->e_phnum = 0;
+ e2->e_phentsize = 0;
+ e2->e_shstrndx = 0;
+ e2->e_shoff = sizeof (e);
+
+ curload += sizeof (e);
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ {
+ Elf_Shdr *s2;
+ s2 = (Elf_Shdr *) curload;
+ grub_memcpy (curload, s, e.e_shentsize);
+ if (s == symsh)
+ s2->sh_offset = sizeof (e) + e.e_shnum * e.e_shentsize;
+ else if (s == strsh)
+ s2->sh_offset = ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t))
+ + sizeof (e) + e.e_shnum * e.e_shentsize;
+ else
+ s2->sh_offset = 0;
+ s2->sh_addr = s2->sh_offset;
+ curload += e.e_shentsize;
+ }
+
+ if (grub_file_seek (file, symsh->sh_offset) == (grub_off_t) -1)
+ return grub_errno;
+ if (grub_file_read (file, curload, symsize) != (grub_ssize_t) symsize)
+ {
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+ curload += ALIGN_UP (symsize, sizeof (grub_freebsd_addr_t));
+
+ if (grub_file_seek (file, strsh->sh_offset) == (grub_off_t) -1)
+ return grub_errno;
+ if (grub_file_read (file, curload, strsize) != (grub_ssize_t) strsize)
+ {
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+
+ err = grub_bsd_add_meta (NETBSD_BTINFO_SYMTAB,
+ &symtab,
+ sizeof (symtab));
+ if (err)
+ return err;
+
+ *kern_end = ALIGN_PAGE (symtarget + chunk_size);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+SUFFIX(grub_openbsd_find_ramdisk) (grub_file_t file,
+ grub_addr_t kern_start,
+ void *kern_chunk_src,
+ struct grub_openbsd_ramdisk_descriptor *desc)
+{
+ unsigned symoff, stroff, symsize, strsize, symentsize;
+
+ {
+ grub_err_t err;
+ Elf_Ehdr e;
+ Elf_Shdr *s;
+ char *shdr = NULL;
+
+ err = read_headers (file, &e, &shdr);
+ if (err)
+ return err;
+
+ for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
+ + e.e_shnum * e.e_shentsize);
+ s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+ if (s >= (Elf_Shdr *) ((char *) shdr + e.e_shnum * e.e_shentsize))
+ {
+ grub_free (shdr);
+ return GRUB_ERR_NONE;
+ }
+
+ symsize = s->sh_size;
+ symentsize = s->sh_entsize;
+ symoff = s->sh_offset;
+
+ s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
+ stroff = s->sh_offset;
+ strsize = s->sh_size;
+ grub_free (shdr);
+ }
+ {
+ Elf_Sym *syms, *sym, *imagesym = NULL, *sizesym = NULL;
+ unsigned i;
+ char *strs;
+
+ syms = grub_malloc (symsize);
+ if (!syms)
+ return grub_errno;
+
+ if (grub_file_seek (file, symoff) == (grub_off_t) -1)
+ {
+ grub_free (syms);
+ return grub_errno;
+ }
+ if (grub_file_read (file, syms, symsize) != (grub_ssize_t) symsize)
+ {
+ grub_free (syms);
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+
+ strs = grub_malloc (strsize);
+ if (!strs)
+ {
+ grub_free (syms);
+ return grub_errno;
+ }
+
+ if (grub_file_seek (file, stroff) == (grub_off_t) -1)
+ return grub_errno;
+ if (grub_file_read (file, strs, strsize) != (grub_ssize_t) strsize)
+ {
+ grub_free (syms);
+ grub_free (strs);
+ if (! grub_errno)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF");
+ return grub_errno;
+ }
+
+ for (i = 0, sym = syms; i < symsize / symentsize;
+ i++, sym = (Elf_Sym *) ((char *) sym + symentsize))
+ {
+ if (ELF_ST_TYPE (sym->st_info) != STT_OBJECT)
+ continue;
+ if (!sym->st_name)
+ continue;
+ if (grub_strcmp (strs + sym->st_name, "rd_root_image") == 0)
+ imagesym = sym;
+ if (grub_strcmp (strs + sym->st_name, "rd_root_size") == 0)
+ sizesym = sym;
+ if (imagesym && sizesym)
+ break;
+ }
+ if (!imagesym || !sizesym)
+ {
+ grub_free (syms);
+ grub_free (strs);
+ return GRUB_ERR_NONE;
+ }
+ if (sizeof (*desc->size) != sizesym->st_size)
+ {
+ grub_free (syms);
+ grub_free (strs);
+ return grub_error (GRUB_ERR_BAD_OS, "unexpected size of rd_root_size");
+ }
+ desc->max_size = imagesym->st_size;
+ desc->target = (imagesym->st_value & 0xFFFFFF) - kern_start
+ + (grub_uint8_t *) kern_chunk_src;
+ desc->size = (grub_uint32_t *) ((sizesym->st_value & 0xFFFFFF) - kern_start
+ + (grub_uint8_t *) kern_chunk_src);
+ grub_free (syms);
+ grub_free (strs);
+
+ return GRUB_ERR_NONE;
+ }
+}
diff --git a/grub-core/loader/i386/bsd_pagetable.c b/grub-core/loader/i386/bsd_pagetable.c
new file mode 100644
index 0000000..9ec5abf
--- /dev/null
+++ b/grub-core/loader/i386/bsd_pagetable.c
@@ -0,0 +1,92 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Based on the code from FreeBSD originally distributed under the
+ following terms: */
+
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+static void
+fill_bsd64_pagetable (grub_uint8_t *src, grub_addr_t target)
+{
+ grub_uint64_t *pt2, *pt3, *pt4;
+ grub_addr_t pt2t, pt3t;
+ int i;
+
+#define PG_V 0x001
+#define PG_RW 0x002
+#define PG_U 0x004
+#define PG_PS 0x080
+
+ pt4 = (grub_uint64_t *) src;
+ pt3 = (grub_uint64_t *) (src + 4096);
+ pt2 = (grub_uint64_t *) (src + 8192);
+
+ pt3t = target + 4096;
+ pt2t = target + 8192;
+
+ grub_memset (src, 0, 4096 * 3);
+
+ /*
+ * This is kinda brutal, but every single 1GB VM memory segment points to
+ * the same first 1GB of physical memory. But it is how BSD expects
+ * it to be.
+ */
+ for (i = 0; i < 512; i++)
+ {
+ /* Each slot of the level 4 pages points to the same level 3 page */
+ pt4[i] = (grub_addr_t) pt3t;
+ pt4[i] |= PG_V | PG_RW | PG_U;
+
+ /* Each slot of the level 3 pages points to the same level 2 page */
+ pt3[i] = (grub_addr_t) pt2t;
+ pt3[i] |= PG_V | PG_RW | PG_U;
+
+ /* The level 2 page slots are mapped with 2MB pages for 1GB. */
+ pt2[i] = i * (2 * 1024 * 1024);
+ pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
+ }
+}
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
new file mode 100644
index 0000000..f19f471
--- /dev/null
+++ b/grub-core/loader/i386/linux.c
@@ -0,0 +1,1016 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/memory.h>
+#include <grub/normal.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/term.h>
+#include <grub/cpu/linux.h>
+#include <grub/video.h>
+#include <grub/video_fb.h>
+#include <grub/command.h>
+#include <grub/i386/relocator.h>
+#include <grub/i18n.h>
+#include <grub/lib/cmdline.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/i386/pc/vesa_modes_table.h>
+#endif
+
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#define HAS_VGA_TEXT 0
+#define DEFAULT_VIDEO_MODE "auto"
+#elif defined (GRUB_MACHINE_IEEE1275)
+#include <grub/ieee1275/ieee1275.h>
+#define HAS_VGA_TEXT 0
+#define DEFAULT_VIDEO_MODE "text"
+#else
+#include <grub/i386/pc/vbe.h>
+#include <grub/i386/pc/console.h>
+#define HAS_VGA_TEXT 1
+#define DEFAULT_VIDEO_MODE "text"
+#endif
+
+#define GRUB_LINUX_CL_OFFSET 0x1000
+#define GRUB_LINUX_CL_END_OFFSET 0x2000
+
+static grub_dl_t my_mod;
+
+static grub_size_t linux_mem_size;
+static int loaded;
+static void *real_mode_mem;
+static grub_addr_t real_mode_target;
+static void *prot_mode_mem;
+static grub_addr_t prot_mode_target;
+static void *initrd_mem;
+static grub_addr_t initrd_mem_target;
+static grub_uint32_t real_mode_pages;
+static grub_uint32_t prot_mode_pages;
+static grub_uint32_t initrd_pages;
+static struct grub_relocator *relocator = NULL;
+static void *efi_mmap_buf;
+#ifdef GRUB_MACHINE_EFI
+static grub_efi_uintn_t efi_mmap_size;
+#else
+static const grub_size_t efi_mmap_size = 0;
+#endif
+
+/* FIXME */
+#if 0
+struct idt_descriptor
+{
+ grub_uint16_t limit;
+ void *base;
+} __attribute__ ((packed));
+
+static struct idt_descriptor idt_desc =
+ {
+ 0,
+ 0
+ };
+#endif
+
+static inline grub_size_t
+page_align (grub_size_t size)
+{
+ return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
+}
+
+#ifdef GRUB_MACHINE_EFI
+/* Find the optimal number of pages for the memory map. Is it better to
+ move this code to efi/mm.c? */
+static grub_efi_uintn_t
+find_efi_mmap_size (void)
+{
+ static grub_efi_uintn_t mmap_size = 0;
+
+ if (mmap_size != 0)
+ return mmap_size;
+
+ mmap_size = (1 << 12);
+ while (1)
+ {
+ int ret;
+ grub_efi_memory_descriptor_t *mmap;
+ grub_efi_uintn_t desc_size;
+
+ mmap = grub_malloc (mmap_size);
+ if (! mmap)
+ return 0;
+
+ ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
+ grub_free (mmap);
+
+ if (ret < 0)
+ grub_fatal ("cannot get memory map");
+ else if (ret > 0)
+ break;
+
+ mmap_size += (1 << 12);
+ }
+
+ /* Increase the size a bit for safety, because GRUB allocates more on
+ later, and EFI itself may allocate more. */
+ mmap_size += (1 << 12);
+
+ mmap_size = page_align (mmap_size);
+ return mmap_size;
+}
+
+#endif
+
+/* Find the optimal number of pages for the memory map. */
+static grub_size_t
+find_mmap_size (void)
+{
+ grub_size_t count = 0, mmap_size;
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr __attribute__ ((unused)),
+ grub_uint64_t size __attribute__ ((unused)),
+ grub_memory_type_t type __attribute__ ((unused)))
+ {
+ count++;
+ return 0;
+ }
+
+ grub_mmap_iterate (hook);
+
+ mmap_size = count * sizeof (struct grub_e820_mmap);
+
+ /* Increase the size a bit for safety, because GRUB allocates more on
+ later. */
+ mmap_size += (1 << 12);
+
+ return page_align (mmap_size);
+}
+
+static void
+free_pages (void)
+{
+ grub_relocator_unload (relocator);
+ relocator = NULL;
+ real_mode_mem = prot_mode_mem = initrd_mem = 0;
+ real_mode_target = prot_mode_target = initrd_mem_target = 0;
+}
+
+/* Allocate pages for the real mode code and the protected mode code
+ for linux as well as a memory map buffer. */
+static grub_err_t
+allocate_pages (grub_size_t prot_size)
+{
+ grub_size_t real_size, mmap_size;
+ grub_err_t err;
+
+ /* Make sure that each size is aligned to a page boundary. */
+ real_size = GRUB_LINUX_CL_END_OFFSET;
+ prot_size = page_align (prot_size);
+ mmap_size = find_mmap_size ();
+
+#ifdef GRUB_MACHINE_EFI
+ efi_mmap_size = find_efi_mmap_size ();
+#endif
+
+ grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
+ (unsigned) real_size, (unsigned) prot_size, (unsigned) mmap_size);
+
+ /* Calculate the number of pages; Combine the real mode code with
+ the memory map buffer for simplicity. */
+ real_mode_pages = ((real_size + mmap_size + efi_mmap_size) >> 12);
+ prot_mode_pages = (prot_size >> 12);
+
+ /* Initialize the memory pointers with NULL for convenience. */
+ free_pages ();
+
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ {
+ err = grub_errno;
+ goto fail;
+ }
+
+ /* FIXME: Should request low memory from the heap when this feature is
+ implemented. */
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+ /* We must put real mode code in the traditional space. */
+
+ if (type == GRUB_MEMORY_AVAILABLE
+ && addr <= 0x90000)
+ {
+ if (addr < 0x10000)
+ {
+ size += addr - 0x10000;
+ addr = 0x10000;
+ }
+
+ if (addr + size > 0x90000)
+ size = 0x90000 - addr;
+
+ if (real_size + mmap_size + efi_mmap_size > size)
+ return 0;
+
+ real_mode_target = ((addr + size) - (real_size + mmap_size + efi_mmap_size));
+ return 1;
+ }
+
+ return 0;
+ }
+ grub_mmap_iterate (hook);
+ if (! real_mode_target)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
+ goto fail;
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ real_mode_target,
+ (real_size + mmap_size
+ + efi_mmap_size));
+ if (err)
+ goto fail;
+ real_mode_mem = get_virtual_current_address (ch);
+ }
+ efi_mmap_buf = (grub_uint8_t *) real_mode_mem + real_size + mmap_size;
+
+ prot_mode_target = GRUB_LINUX_BZIMAGE_ADDR;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ prot_mode_target, prot_size);
+ if (err)
+ goto fail;
+ prot_mode_mem = get_virtual_current_address (ch);
+ }
+
+ grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, "
+ "prot_mode_mem = %lx, prot_mode_pages = %x\n",
+ (unsigned long) real_mode_mem, (unsigned) real_mode_pages,
+ (unsigned long) prot_mode_mem, (unsigned) prot_mode_pages);
+
+ return GRUB_ERR_NONE;
+
+ fail:
+ free_pages ();
+ return err;
+}
+
+static void
+grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num,
+ grub_uint64_t start, grub_uint64_t size,
+ grub_uint32_t type)
+{
+ int n = *e820_num;
+
+ if (n >= GRUB_E820_MAX_ENTRY)
+ grub_fatal ("Too many e820 memory map entries");
+
+ if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) &&
+ (e820_map[n - 1].type == type))
+ e820_map[n - 1].size += size;
+ else
+ {
+ e820_map[n].addr = start;
+ e820_map[n].size = size;
+ e820_map[n].type = type;
+ (*e820_num)++;
+ }
+}
+
+static grub_err_t
+grub_linux_setup_video (struct linux_kernel_params *params)
+{
+ struct grub_video_mode_info mode_info;
+ void *framebuffer;
+ grub_err_t err;
+ grub_video_driver_id_t driver_id;
+ char *gfxlfbvar = grub_env_get ("gfxpayloadforcelfb");
+
+ driver_id = grub_video_get_driver_id ();
+
+ if (driver_id == GRUB_VIDEO_DRIVER_NONE)
+ return 1;
+
+ err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+
+ if (err)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ return 1;
+ }
+
+ params->lfb_width = mode_info.width;
+ params->lfb_height = mode_info.height;
+ params->lfb_depth = mode_info.bpp;
+ params->lfb_line_len = mode_info.pitch;
+
+ params->lfb_base = (grub_size_t) framebuffer;
+ params->lfb_size = ALIGN_UP (params->lfb_line_len * params->lfb_height, 65536);
+
+ params->red_mask_size = mode_info.red_mask_size;
+ params->red_field_pos = mode_info.red_field_pos;
+ params->green_mask_size = mode_info.green_mask_size;
+ params->green_field_pos = mode_info.green_field_pos;
+ params->blue_mask_size = mode_info.blue_mask_size;
+ params->blue_field_pos = mode_info.blue_field_pos;
+ params->reserved_mask_size = mode_info.reserved_mask_size;
+ params->reserved_field_pos = mode_info.reserved_field_pos;
+
+ if (gfxlfbvar && (gfxlfbvar[0] == '1' || gfxlfbvar[0] == 'y'))
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
+ else
+ {
+ switch (driver_id)
+ {
+ case GRUB_VIDEO_DRIVER_VBE:
+ params->lfb_size >>= 16;
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_VESA;
+ break;
+
+ case GRUB_VIDEO_DRIVER_EFI_UGA:
+ case GRUB_VIDEO_DRIVER_EFI_GOP:
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_EFIFB;
+ break;
+
+ /* FIXME: check if better id is available. */
+ case GRUB_VIDEO_DRIVER_SM712:
+ case GRUB_VIDEO_DRIVER_VGA:
+ case GRUB_VIDEO_DRIVER_CIRRUS:
+ case GRUB_VIDEO_DRIVER_BOCHS:
+ /* Make gcc happy. */
+ case GRUB_VIDEO_DRIVER_SDL:
+ case GRUB_VIDEO_DRIVER_NONE:
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
+ break;
+ }
+ }
+
+#ifdef GRUB_MACHINE_PCBIOS
+ /* VESA packed modes may come with zeroed mask sizes, which need
+ to be set here according to DAC Palette width. If we don't,
+ this results in Linux displaying a black screen. */
+ if (driver_id == GRUB_VIDEO_DRIVER_VBE && mode_info.bpp <= 8)
+ {
+ struct grub_vbe_info_block controller_info;
+ int status;
+ int width = 8;
+
+ status = grub_vbe_bios_get_controller_info (&controller_info);
+
+ if (status == GRUB_VBE_STATUS_OK &&
+ (controller_info.capabilities & GRUB_VBE_CAPABILITY_DACWIDTH))
+ status = grub_vbe_bios_set_dac_palette_width (&width);
+
+ if (status != GRUB_VBE_STATUS_OK)
+ /* 6 is default after mode reset. */
+ width = 6;
+
+ params->red_mask_size = params->green_mask_size
+ = params->blue_mask_size = width;
+ params->reserved_mask_size = 0;
+ }
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ struct linux_kernel_params *params;
+ int e820_num;
+ grub_err_t err = 0;
+ char *modevar, *tmp;
+ struct grub_relocator32_state state;
+
+ params = real_mode_mem;
+
+#ifdef GRUB_MACHINE_IEEE1275
+ {
+ char *bootpath;
+ grub_ssize_t len;
+
+ bootpath = grub_env_get ("root");
+ if (bootpath)
+ grub_ieee1275_set_property (grub_ieee1275_chosen,
+ "bootpath", bootpath,
+ grub_strlen (bootpath) + 1,
+ &len);
+ }
+#endif
+
+ grub_dprintf ("linux", "code32_start = %x\n",
+ (unsigned) params->code32_start);
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+ switch (type)
+ {
+ case GRUB_MEMORY_AVAILABLE:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_RAM);
+ break;
+
+ case GRUB_MEMORY_ACPI:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_ACPI);
+ break;
+
+ case GRUB_MEMORY_NVS:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_NVS);
+ break;
+
+ case GRUB_MEMORY_BADRAM:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_BADRAM);
+ break;
+
+ default:
+ grub_e820_add_region (params->e820_map, &e820_num,
+ addr, size, GRUB_E820_RESERVED);
+ }
+ return 0;
+ }
+
+ e820_num = 0;
+ grub_mmap_iterate (hook);
+ params->mmap_size = e820_num;
+
+ modevar = grub_env_get ("gfxpayload");
+
+ /* Now all graphical modes are acceptable.
+ May change in future if we have modes without framebuffer. */
+ if (modevar && *modevar != 0)
+ {
+ tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
+ if (! tmp)
+ return grub_errno;
+ err = grub_video_set_mode (tmp, 0, 0);
+ grub_free (tmp);
+ }
+ else
+ err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
+
+ if (err)
+ {
+ grub_print_error ();
+ grub_printf ("Booting however\n");
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ if (grub_linux_setup_video (params))
+ {
+#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
+ params->have_vga = GRUB_VIDEO_LINUX_TYPE_TEXT;
+ params->video_mode = 0x3;
+#else
+ params->have_vga = 0;
+ params->video_mode = 0;
+ params->video_width = 0;
+ params->video_height = 0;
+#endif
+ }
+
+ /* Initialize these last, because terminal position could be affected by printfs above. */
+#ifndef GRUB_MACHINE_IEEE1275
+ if (params->have_vga == GRUB_VIDEO_LINUX_TYPE_TEXT)
+#endif
+ {
+ grub_term_output_t term;
+ int found = 0;
+ FOR_ACTIVE_TERM_OUTPUTS(term)
+ if (grub_strcmp (term->name, "vga_text") == 0
+ || grub_strcmp (term->name, "console") == 0
+ || grub_strcmp (term->name, "ofconsole") == 0)
+ {
+ grub_uint16_t pos = grub_term_getxy (term);
+ params->video_cursor_x = pos >> 8;
+ params->video_cursor_y = pos & 0xff;
+ params->video_width = grub_term_width (term);
+ params->video_height = grub_term_height (term);
+ found = 1;
+ break;
+ }
+ if (!found)
+ {
+ params->video_cursor_x = 0;
+ params->video_cursor_y = 0;
+ params->video_width = 80;
+ params->video_height = 25;
+ }
+ }
+
+#ifdef GRUB_MACHINE_IEEE1275
+ {
+ params->ofw_signature = GRUB_LINUX_OFW_SIGNATURE;
+ params->ofw_num_items = 1;
+ params->ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn;
+ params->ofw_idt = 0;
+ }
+#endif
+
+#ifdef GRUB_MACHINE_EFI
+ {
+ grub_efi_uintn_t efi_desc_size;
+ grub_size_t efi_mmap_target;
+ grub_efi_uint32_t efi_desc_version;
+ err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL,
+ &efi_desc_size, &efi_desc_version);
+ if (err)
+ return err;
+
+ /* Note that no boot services are available from here. */
+ efi_mmap_target = real_mode_target
+ + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem);
+ /* Pass EFI parameters. */
+ if (grub_le_to_cpu16 (params->version) >= 0x0206)
+ {
+ params->v0206.efi_mem_desc_size = efi_desc_size;
+ params->v0206.efi_mem_desc_version = efi_desc_version;
+ params->v0206.efi_mmap = efi_mmap_target;
+ params->v0206.efi_mmap_size = efi_mmap_size;
+#ifdef __x86_64__
+ params->v0206.efi_mmap_hi = (efi_mmap_target >> 32);
+#endif
+ }
+ else if (grub_le_to_cpu16 (params->version) >= 0x0204)
+ {
+ params->v0204.efi_mem_desc_size = efi_desc_size;
+ params->v0204.efi_mem_desc_version = efi_desc_version;
+ params->v0204.efi_mmap = efi_mmap_target;
+ params->v0204.efi_mmap_size = efi_mmap_size;
+ }
+ }
+#endif
+
+ /* FIXME. */
+ /* asm volatile ("lidt %0" : : "m" (idt_desc)); */
+ state.ebp = state.edi = state.ebx = 0;
+ state.esi = real_mode_target;
+ state.esp = real_mode_target;
+ state.eip = params->code32_start;
+ return grub_relocator32_boot (relocator, state);
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ struct linux_kernel_header lh;
+ struct linux_kernel_params *params;
+ grub_uint8_t setup_sects;
+ grub_size_t real_size, prot_size;
+ grub_ssize_t len;
+ int i;
+
+ grub_dl_ref (my_mod);
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
+ goto fail;
+ }
+
+ if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
+ goto fail;
+ }
+
+ if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
+ goto fail;
+ }
+
+ if (! (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "zImage doesn't support 32-bit boot"
+#ifdef GRUB_MACHINE_PCBIOS
+ " (try with `linux16')"
+#endif
+ );
+ goto fail;
+ }
+
+ /* FIXME: 2.03 is not always good enough (Linux 2.4 can be 2.03 and
+ still not support 32-bit boot. */
+ if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
+ || grub_le_to_cpu16 (lh.version) < 0x0203)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "version too old for 32-bit boot"
+#ifdef GRUB_MACHINE_PCBIOS
+ " (try with `linux16')"
+#endif
+ );
+ goto fail;
+ }
+
+ setup_sects = lh.setup_sects;
+
+ /* If SETUP_SECTS is not set, set it to the default (4). */
+ if (! setup_sects)
+ setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
+
+ real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
+ prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
+
+ if (allocate_pages (prot_size))
+ goto fail;
+
+ params = (struct linux_kernel_params *) real_mode_mem;
+ grub_memset (params, 0, GRUB_LINUX_CL_END_OFFSET);
+ grub_memcpy (&params->setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
+
+ params->ps_mouse = params->padding10 = 0;
+
+ len = 0x400 - sizeof (lh);
+ if (grub_file_read (file, (char *) real_mode_mem + sizeof (lh), len) != len)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ params->type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
+
+ /* These two are used (instead of cmd_line_ptr) by older versions of Linux,
+ and otherwise ignored. */
+ params->cl_magic = GRUB_LINUX_CL_MAGIC;
+ params->cl_offset = 0x1000;
+
+ params->cmd_line_ptr = real_mode_target + 0x1000;
+ params->ramdisk_image = 0;
+ params->ramdisk_size = 0;
+
+ params->heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET;
+ params->loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
+
+ /* These are not needed to be precise, because Linux uses these values
+ only to raise an error when the decompression code cannot find good
+ space. */
+ params->ext_mem = ((32 * 0x100000) >> 10);
+ params->alt_mem = ((32 * 0x100000) >> 10);
+
+ /* Ignored by Linux. */
+ params->video_page = 0;
+
+ /* Only used when `video_mode == 0x7', otherwise ignored. */
+ params->video_ega_bx = 0;
+
+ params->font_size = 16; /* XXX */
+
+#ifdef GRUB_MACHINE_EFI
+ if (grub_le_to_cpu16 (params->version) >= 0x0206)
+ {
+ params->v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
+ params->v0206.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
+#ifdef __x86_64__
+ params->v0206.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32);
+#endif
+ }
+ else if (grub_le_to_cpu16 (params->version) >= 0x0204)
+ {
+ params->v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204;
+ params->v0204.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
+ }
+#endif
+
+ /* The other parameters are filled when booting. */
+
+ grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
+
+ grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
+ (unsigned) real_size, (unsigned) prot_size);
+
+ /* Look for memory size and video mode specified on the command line. */
+ linux_mem_size = 0;
+ for (i = 1; i < argc; i++)
+#ifdef GRUB_MACHINE_PCBIOS
+ if (grub_memcmp (argv[i], "vga=", 4) == 0)
+ {
+ /* Video mode selection support. */
+ char *val = argv[i] + 4;
+ unsigned vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
+ struct grub_vesa_mode_table_entry *linux_mode;
+ grub_err_t err;
+ char *buf;
+
+ grub_dl_load ("vbe");
+
+ if (grub_strcmp (val, "normal") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
+ else if (grub_strcmp (val, "ext") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
+ else if (grub_strcmp (val, "ask") == 0)
+ {
+ grub_printf ("Legacy `ask' parameter no longer supported.\n");
+
+ /* We usually would never do this in a loader, but "vga=ask" means user
+ requested interaction, so it can't hurt to request keyboard input. */
+ grub_wait_after_message ();
+
+ goto fail;
+ }
+ else
+ vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
+
+ switch (vid_mode)
+ {
+ case 0:
+ case GRUB_LINUX_VID_MODE_NORMAL:
+ grub_env_set ("gfxpayload", "text");
+ grub_printf ("%s is deprecated. "
+ "Use set gfxpayload=text before "
+ "linux command instead.\n",
+ argv[i]);
+ break;
+
+ case 1:
+ case GRUB_LINUX_VID_MODE_EXTENDED:
+ /* FIXME: support 80x50 text. */
+ grub_env_set ("gfxpayload", "text");
+ grub_printf ("%s is deprecated. "
+ "Use set gfxpayload=text before "
+ "linux command instead.\n",
+ argv[i]);
+ break;
+ default:
+ /* Ignore invalid values. */
+ if (vid_mode < GRUB_VESA_MODE_TABLE_START ||
+ vid_mode > GRUB_VESA_MODE_TABLE_END)
+ {
+ grub_env_set ("gfxpayload", "text");
+ grub_printf ("%s is deprecated. Mode %d isn't recognized. "
+ "Use set gfxpayload=WIDTHxHEIGHT[xDEPTH] before "
+ "linux command instead.\n",
+ argv[i], vid_mode);
+ break;
+ }
+
+ linux_mode = &grub_vesa_mode_table[vid_mode
+ - GRUB_VESA_MODE_TABLE_START];
+
+ buf = grub_xasprintf ("%ux%ux%u,%ux%u",
+ linux_mode->width, linux_mode->height,
+ linux_mode->depth,
+ linux_mode->width, linux_mode->height);
+ if (! buf)
+ goto fail;
+
+ grub_printf ("%s is deprecated. "
+ "Use set gfxpayload=%s before "
+ "linux command instead.\n",
+ argv[i], buf);
+ err = grub_env_set ("gfxpayload", buf);
+ grub_free (buf);
+ if (err)
+ goto fail;
+ }
+ }
+ else
+#endif /* GRUB_MACHINE_PCBIOS */
+ if (grub_memcmp (argv[i], "mem=", 4) == 0)
+ {
+ char *val = argv[i] + 4;
+
+ linux_mem_size = grub_strtoul (val, &val, 0);
+
+ if (grub_errno)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ linux_mem_size = 0;
+ }
+ else
+ {
+ int shift = 0;
+
+ switch (grub_tolower (val[0]))
+ {
+ case 'g':
+ shift += 10;
+ case 'm':
+ shift += 10;
+ case 'k':
+ shift += 10;
+ default:
+ break;
+ }
+
+ /* Check an overflow. */
+ if (linux_mem_size > (~0UL >> shift))
+ linux_mem_size = 0;
+ else
+ linux_mem_size <<= shift;
+ }
+ }
+ else if (grub_memcmp (argv[i], "quiet", sizeof ("quiet") - 1) == 0)
+ {
+ params->loadflags |= GRUB_LINUX_FLAG_QUIET;
+ }
+
+ /* Create kernel command line. */
+ grub_memcpy ((char *)real_mode_mem + GRUB_LINUX_CL_OFFSET, LINUX_IMAGE,
+ sizeof (LINUX_IMAGE));
+ grub_create_loader_cmdline (argc, argv,
+ (char *)real_mode_mem + GRUB_LINUX_CL_OFFSET
+ + sizeof (LINUX_IMAGE) - 1,
+ GRUB_LINUX_CL_END_OFFSET - GRUB_LINUX_CL_OFFSET
+ - (sizeof (LINUX_IMAGE) - 1));
+
+ len = prot_size;
+ if (grub_file_read (file, prot_mode_mem, len) != len)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ grub_loader_set (grub_linux_boot, grub_linux_unload,
+ 0 /* set noreturn=0 in order to avoid grub_console_fini() */);
+ loaded = 1;
+ }
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_ssize_t size;
+ grub_addr_t addr_min, addr_max;
+ grub_addr_t addr;
+ grub_err_t err;
+ struct linux_kernel_header *lh;
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
+ goto fail;
+ }
+
+ if (! loaded)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
+ goto fail;
+ }
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ size = grub_file_size (file);
+ initrd_pages = (page_align (size) >> 12);
+
+ lh = (struct linux_kernel_header *) real_mode_mem;
+
+ /* Get the highest address available for the initrd. */
+ if (grub_le_to_cpu16 (lh->version) >= 0x0203)
+ {
+ addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);
+
+ /* XXX in reality, Linux specifies a bogus value, so
+ it is necessary to make sure that ADDR_MAX does not exceed
+ 0x3fffffff. */
+ if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
+ addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
+ }
+ else
+ addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
+
+ if (linux_mem_size != 0 && linux_mem_size < addr_max)
+ addr_max = linux_mem_size;
+
+ /* Linux 2.3.xx has a bug in the memory range check, so avoid
+ the last page.
+ Linux 2.2.xx has a bug in the memory range check, which is
+ worse than that of Linux 2.3.xx, so avoid the last 64kb. */
+ addr_max -= 0x10000;
+
+ /* Usually, the compression ratio is about 50%. */
+ addr_min = (grub_addr_t) prot_mode_target + ((prot_mode_pages * 3) << 12)
+ + page_align (size);
+
+ /* Put the initrd as high as possible, 4KiB aligned. */
+ addr = (addr_max - size) & ~0xFFF;
+
+ if (addr < addr_min)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big");
+ goto fail;
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ addr_min, addr, size, 0x1000,
+ GRUB_RELOCATOR_PREFERENCE_HIGH);
+ if (err)
+ return err;
+ initrd_mem = get_virtual_current_address (ch);
+ initrd_mem_target = get_physical_target_address (ch);
+ }
+
+ if (grub_file_read (file, initrd_mem, size) != size)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
+ (unsigned) addr, (unsigned) size);
+
+ lh->ramdisk_image = initrd_mem_target;
+ lh->ramdisk_size = size;
+ lh->root_dev = 0x0100; /* XXX */
+
+ fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_linux, cmd_initrd;
+
+GRUB_MOD_INIT(linux)
+{
+ cmd_linux = grub_register_command ("linux", grub_cmd_linux,
+ 0, N_("Load Linux."));
+ cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
+ 0, N_("Load initrd."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(linux)
+{
+ grub_unregister_command (cmd_linux);
+ grub_unregister_command (cmd_initrd);
+}
diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c
new file mode 100644
index 0000000..bef5342
--- /dev/null
+++ b/grub-core/loader/i386/multiboot_mbi.c
@@ -0,0 +1,731 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/memory.h>
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/machine/biosnum.h>
+#include <grub/machine/apm.h>
+#include <grub/machine/memory.h>
+#endif
+#include <grub/multiboot.h>
+#include <grub/cpu/relocator.h>
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/partition.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/relocator.h>
+#include <grub/video.h>
+#include <grub/file.h>
+
+/* The bits in the required part of flags field we don't support. */
+#define UNSUPPORTED_FLAGS 0x0000fff8
+
+struct module
+{
+ struct module *next;
+ grub_addr_t start;
+ grub_size_t size;
+ char *cmdline;
+ int cmdline_size;
+};
+
+static struct module *modules, *modules_last;
+static grub_size_t cmdline_size;
+static grub_size_t total_modcmd;
+static unsigned modcnt;
+static char *cmdline = NULL;
+static grub_uint32_t bootdev;
+static int bootdev_set;
+static grub_size_t elf_sec_num, elf_sec_entsize;
+static unsigned elf_sec_shstrndx;
+static void *elf_sections;
+
+
+grub_err_t
+grub_multiboot_load (grub_file_t file)
+{
+ char *buffer;
+ grub_ssize_t len;
+ struct multiboot_header *header;
+ grub_err_t err;
+
+ buffer = grub_malloc (MULTIBOOT_SEARCH);
+ if (!buffer)
+ return grub_errno;
+
+ len = grub_file_read (file, buffer, MULTIBOOT_SEARCH);
+ if (len < 32)
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_BAD_OS, "file too small");
+ }
+
+ /* Look for the multiboot header in the buffer. The header should
+ be at least 12 bytes and aligned on a 4-byte boundary. */
+ for (header = (struct multiboot_header *) buffer;
+ ((char *) header <= buffer + len - 12) || (header = 0);
+ header = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN))
+ {
+ if (header->magic == MULTIBOOT_HEADER_MAGIC
+ && !(header->magic + header->flags + header->checksum))
+ break;
+ }
+
+ if (header == 0)
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found");
+ }
+
+ if (header->flags & UNSUPPORTED_FLAGS)
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ "unsupported flag: 0x%x", header->flags);
+ }
+
+ if (header->flags & MULTIBOOT_AOUT_KLUDGE)
+ {
+ int offset = ((char *) header - buffer -
+ (header->header_addr - header->load_addr));
+ int load_size = ((header->load_end_addr == 0) ? file->size - offset :
+ header->load_end_addr - header->load_addr);
+ grub_size_t code_size;
+ void *source;
+ grub_relocator_chunk_t ch;
+
+ if (header->bss_end_addr)
+ code_size = (header->bss_end_addr - header->load_addr);
+ else
+ code_size = load_size;
+
+ err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator,
+ &ch, header->load_addr,
+ code_size);
+ if (err)
+ {
+ grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
+ grub_free (buffer);
+ return err;
+ }
+ source = get_virtual_current_address (ch);
+
+ if ((grub_file_seek (file, offset)) == (grub_off_t) -1)
+ {
+ grub_free (buffer);
+ return grub_errno;
+ }
+
+ grub_file_read (file, source, load_size);
+ if (grub_errno)
+ {
+ grub_free (buffer);
+ return grub_errno;
+ }
+
+ if (header->bss_end_addr)
+ grub_memset ((grub_uint8_t *) source + load_size, 0,
+ header->bss_end_addr - header->load_addr - load_size);
+
+ grub_multiboot_payload_eip = header->entry_addr;
+ }
+ else
+ {
+ err = grub_multiboot_load_elf (file, buffer);
+ if (err)
+ {
+ grub_free (buffer);
+ return err;
+ }
+ }
+
+ if (header->flags & MULTIBOOT_VIDEO_MODE)
+ {
+ switch (header->mode_type)
+ {
+ case 1:
+ err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
+ GRUB_MULTIBOOT_CONSOLE_EGA_TEXT
+ | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
+ 0, 0, 0, 0);
+ break;
+ case 0:
+ err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
+ GRUB_MULTIBOOT_CONSOLE_EGA_TEXT
+ | GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
+ header->width, header->height,
+ header->depth, 0);
+ break;
+ default:
+ err = grub_error (GRUB_ERR_BAD_OS,
+ "unsupported graphical mode type %d",
+ header->mode_type);
+ break;
+ }
+ }
+ else
+ err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
+ GRUB_MULTIBOOT_CONSOLE_EGA_TEXT,
+ 0, 0, 0, 0);
+ return err;
+}
+
+#if GRUB_MACHINE_HAS_VBE || GRUB_MACHINE_HAS_VGA_TEXT
+#include <grub/i386/pc/vbe.h>
+#endif
+
+static grub_size_t
+grub_multiboot_get_mbi_size (void)
+{
+ return sizeof (struct multiboot_info) + ALIGN_UP (cmdline_size, 4)
+ + modcnt * sizeof (struct multiboot_mod_list) + total_modcmd
+ + ALIGN_UP (sizeof(PACKAGE_STRING), 4)
+ + grub_get_multiboot_mmap_count () * sizeof (struct multiboot_mmap_entry)
+ + elf_sec_entsize * elf_sec_num
+ + 256 * sizeof (struct multiboot_color)
+#if GRUB_MACHINE_HAS_VBE || GRUB_MACHINE_HAS_VGA_TEXT
+ + sizeof (struct grub_vbe_info_block)
+ + sizeof (struct grub_vbe_mode_info_block)
+#endif
+ + ALIGN_UP (sizeof (struct multiboot_apm_info), 4);
+}
+
+/* Fill previously allocated Multiboot mmap. */
+static void
+grub_fill_multiboot_mmap (struct multiboot_mmap_entry *first_entry)
+{
+ struct multiboot_mmap_entry *mmap_entry = (struct multiboot_mmap_entry *) first_entry;
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+ mmap_entry->addr = addr;
+ mmap_entry->len = size;
+ switch (type)
+ {
+ case GRUB_MEMORY_AVAILABLE:
+ mmap_entry->type = MULTIBOOT_MEMORY_AVAILABLE;
+ break;
+
+ case GRUB_MEMORY_ACPI:
+ mmap_entry->type = MULTIBOOT_MEMORY_ACPI_RECLAIMABLE;
+ break;
+
+ case GRUB_MEMORY_NVS:
+ mmap_entry->type = MULTIBOOT_MEMORY_NVS;
+ break;
+
+ case GRUB_MEMORY_BADRAM:
+ mmap_entry->type = MULTIBOOT_MEMORY_BADRAM;
+ break;
+
+ default:
+ mmap_entry->type = MULTIBOOT_MEMORY_RESERVED;
+ break;
+ }
+ mmap_entry->size = sizeof (struct multiboot_mmap_entry) - sizeof (mmap_entry->size);
+ mmap_entry++;
+
+ return 0;
+ }
+
+ grub_mmap_iterate (hook);
+}
+
+#if GRUB_MACHINE_HAS_VBE || GRUB_MACHINE_HAS_VGA_TEXT
+
+static grub_err_t
+fill_vbe_info (struct multiboot_info *mbi, grub_uint8_t *ptrorig,
+ grub_uint32_t ptrdest, int fill_generic)
+{
+ grub_uint32_t vbe_mode;
+ struct grub_vbe_mode_info_block *mode_info;
+#if GRUB_MACHINE_HAS_VBE
+ grub_vbe_status_t status;
+ void *scratch = (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+
+ status = grub_vbe_bios_get_controller_info (scratch);
+ if (status != GRUB_VBE_STATUS_OK)
+ return grub_error (GRUB_ERR_IO, "Can't get controller info.");
+
+ mbi->vbe_control_info = ptrdest;
+ grub_memcpy (ptrorig, scratch, sizeof (struct grub_vbe_info_block));
+ ptrorig += sizeof (struct grub_vbe_info_block);
+ ptrdest += sizeof (struct grub_vbe_info_block);
+#else
+ mbi->vbe_control_info = 0;
+#endif
+
+#if GRUB_MACHINE_HAS_VBE
+ status = grub_vbe_bios_get_mode (scratch);
+ vbe_mode = *(grub_uint32_t *) scratch;
+ if (status != GRUB_VBE_STATUS_OK)
+ return grub_error (GRUB_ERR_IO, "can't get VBE mode");
+#else
+ vbe_mode = 3;
+#endif
+ mbi->vbe_mode = vbe_mode;
+
+ mode_info = (struct grub_vbe_mode_info_block *) ptrorig;
+ mbi->vbe_mode_info = ptrdest;
+ /* get_mode_info isn't available for mode 3. */
+ if (vbe_mode == 3)
+ {
+ grub_memset (mode_info, 0, sizeof (struct grub_vbe_mode_info_block));
+ mode_info->memory_model = GRUB_VBE_MEMORY_MODEL_TEXT;
+ mode_info->x_resolution = 80;
+ mode_info->y_resolution = 25;
+ }
+ else
+ {
+#if GRUB_MACHINE_HAS_VBE
+ status = grub_vbe_bios_get_mode_info (vbe_mode, scratch);
+ if (status != GRUB_VBE_STATUS_OK)
+ return grub_error (GRUB_ERR_IO, "can't get mode info");
+ grub_memcpy (mode_info, scratch,
+ sizeof (struct grub_vbe_mode_info_block));
+#endif
+ }
+ ptrorig += sizeof (struct grub_vbe_mode_info_block);
+ ptrdest += sizeof (struct grub_vbe_mode_info_block);
+
+#if GRUB_MACHINE_HAS_VBE
+ grub_vbe_bios_get_pm_interface (&mbi->vbe_interface_seg,
+ &mbi->vbe_interface_off,
+ &mbi->vbe_interface_len);
+#endif
+
+ mbi->flags |= MULTIBOOT_INFO_VBE_INFO;
+
+ if (fill_generic && mode_info->memory_model == GRUB_VBE_MEMORY_MODEL_TEXT)
+ {
+ mbi->framebuffer_addr = 0xb8000;
+
+ mbi->framebuffer_pitch = 2 * mode_info->x_resolution;
+ mbi->framebuffer_width = mode_info->x_resolution;
+ mbi->framebuffer_height = mode_info->y_resolution;
+
+ mbi->framebuffer_bpp = 16;
+
+ mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
+
+ mbi->flags |= MULTIBOOT_INFO_FRAMEBUFFER_INFO;
+ }
+
+ return GRUB_ERR_NONE;
+}
+#endif
+
+static grub_err_t
+retrieve_video_parameters (struct multiboot_info *mbi,
+ grub_uint8_t *ptrorig, grub_uint32_t ptrdest)
+{
+ grub_err_t err;
+ struct grub_video_mode_info mode_info;
+ void *framebuffer;
+ grub_video_driver_id_t driv_id;
+ struct grub_video_palette_data palette[256];
+
+ err = grub_multiboot_set_video_mode ();
+ if (err)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ grub_video_get_palette (0, ARRAY_SIZE (palette), palette);
+
+ driv_id = grub_video_get_driver_id ();
+#if GRUB_MACHINE_HAS_VGA_TEXT
+ if (driv_id == GRUB_VIDEO_DRIVER_NONE)
+ return fill_vbe_info (mbi, ptrorig, ptrdest, 1);
+#else
+ if (driv_id == GRUB_VIDEO_DRIVER_NONE)
+ return GRUB_ERR_NONE;
+#endif
+
+ err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+ if (err)
+ return err;
+
+ mbi->framebuffer_addr = (grub_addr_t) framebuffer;
+ mbi->framebuffer_pitch = mode_info.pitch;
+
+ mbi->framebuffer_width = mode_info.width;
+ mbi->framebuffer_height = mode_info.height;
+
+ mbi->framebuffer_bpp = mode_info.bpp;
+
+ if (mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)
+ {
+ struct multiboot_color *mb_palette;
+ unsigned i;
+ mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED;
+ mbi->framebuffer_palette_addr = ptrdest;
+ mbi->framebuffer_palette_num_colors = mode_info.number_of_colors;
+ if (mbi->framebuffer_palette_num_colors > ARRAY_SIZE (palette))
+ mbi->framebuffer_palette_num_colors = ARRAY_SIZE (palette);
+ mb_palette = (struct multiboot_color *) ptrorig;
+ for (i = 0; i < mbi->framebuffer_palette_num_colors; i++)
+ {
+ mb_palette[i].red = palette[i].r;
+ mb_palette[i].green = palette[i].g;
+ mb_palette[i].blue = palette[i].b;
+ }
+ ptrorig += mbi->framebuffer_palette_num_colors
+ * sizeof (struct multiboot_color);
+ ptrdest += mbi->framebuffer_palette_num_colors
+ * sizeof (struct multiboot_color);
+ }
+ else
+ {
+ mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
+ mbi->framebuffer_red_field_position = mode_info.red_field_pos;
+ mbi->framebuffer_red_mask_size = mode_info.red_mask_size;
+ mbi->framebuffer_green_field_position = mode_info.green_field_pos;
+ mbi->framebuffer_green_mask_size = mode_info.green_mask_size;
+ mbi->framebuffer_blue_field_position = mode_info.blue_field_pos;
+ mbi->framebuffer_blue_mask_size = mode_info.blue_mask_size;
+ }
+
+ mbi->flags |= MULTIBOOT_INFO_FRAMEBUFFER_INFO;
+
+#if GRUB_MACHINE_HAS_VBE
+ if (driv_id == GRUB_VIDEO_DRIVER_VBE)
+ return fill_vbe_info (mbi, ptrorig, ptrdest, 0);
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_multiboot_make_mbi (grub_uint32_t *target)
+{
+ struct multiboot_info *mbi;
+ struct multiboot_mod_list *modlist;
+ unsigned i;
+ struct module *cur;
+ grub_size_t mmap_size;
+ grub_uint8_t *ptrorig;
+ grub_addr_t ptrdest;
+
+ grub_err_t err;
+ grub_size_t bufsize;
+ grub_relocator_chunk_t ch;
+
+ bufsize = grub_multiboot_get_mbi_size ();
+
+ err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
+ 0x10000, 0x100000 - bufsize,
+ bufsize, 4,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ if (err)
+ return err;
+ ptrorig = get_virtual_current_address (ch);
+ ptrdest = get_physical_target_address (ch);
+
+ *target = ptrdest;
+
+ mbi = (struct multiboot_info *) ptrorig;
+ ptrorig += sizeof (*mbi);
+ ptrdest += sizeof (*mbi);
+ grub_memset (mbi, 0, sizeof (*mbi));
+
+ grub_memcpy (ptrorig, cmdline, cmdline_size);
+ mbi->flags |= MULTIBOOT_INFO_CMDLINE;
+ mbi->cmdline = ptrdest;
+ ptrorig += ALIGN_UP (cmdline_size, 4);
+ ptrdest += ALIGN_UP (cmdline_size, 4);
+
+ grub_memcpy (ptrorig, PACKAGE_STRING, sizeof(PACKAGE_STRING));
+ mbi->flags |= MULTIBOOT_INFO_BOOT_LOADER_NAME;
+ mbi->boot_loader_name = ptrdest;
+ ptrorig += ALIGN_UP (sizeof(PACKAGE_STRING), 4);
+ ptrdest += ALIGN_UP (sizeof(PACKAGE_STRING), 4);
+
+#ifdef GRUB_MACHINE_PCBIOS
+ {
+ struct grub_apm_info info;
+ if (grub_apm_get_info (&info))
+ {
+ struct multiboot_apm_info *mbinfo = (void *) ptrorig;
+
+ mbinfo->cseg = info.cseg;
+ mbinfo->offset = info.offset;
+ mbinfo->cseg_16 = info.cseg_16;
+ mbinfo->dseg = info.dseg;
+ mbinfo->flags = info.flags;
+ mbinfo->cseg_len = info.cseg_len;
+ mbinfo->dseg_len = info.dseg_len;
+ mbinfo->cseg_16_len = info.cseg_16_len;
+ mbinfo->version = info.version;
+
+ ptrorig += ALIGN_UP (sizeof (struct multiboot_apm_info), 4);
+ ptrdest += ALIGN_UP (sizeof (struct multiboot_apm_info), 4);
+ }
+ }
+#endif
+
+ if (modcnt)
+ {
+ mbi->flags |= MULTIBOOT_INFO_MODS;
+ mbi->mods_addr = ptrdest;
+ mbi->mods_count = modcnt;
+ modlist = (struct multiboot_mod_list *) ptrorig;
+ ptrorig += modcnt * sizeof (struct multiboot_mod_list);
+ ptrdest += modcnt * sizeof (struct multiboot_mod_list);
+
+ for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next)
+ {
+ modlist[i].mod_start = cur->start;
+ modlist[i].mod_end = modlist[i].mod_start + cur->size;
+ modlist[i].cmdline = ptrdest;
+ grub_memcpy (ptrorig, cur->cmdline, cur->cmdline_size);
+ ptrorig += ALIGN_UP (cur->cmdline_size, 4);
+ ptrdest += ALIGN_UP (cur->cmdline_size, 4);
+ }
+ }
+ else
+ {
+ mbi->mods_addr = 0;
+ mbi->mods_count = 0;
+ }
+
+ mmap_size = grub_get_multiboot_mmap_count ()
+ * sizeof (struct multiboot_mmap_entry);
+ grub_fill_multiboot_mmap ((struct multiboot_mmap_entry *) ptrorig);
+ mbi->mmap_length = mmap_size;
+ mbi->mmap_addr = ptrdest;
+ mbi->flags |= MULTIBOOT_INFO_MEM_MAP;
+ ptrorig += mmap_size;
+ ptrdest += mmap_size;
+
+ /* Convert from bytes to kilobytes. */
+ mbi->mem_lower = grub_mmap_get_lower () / 1024;
+ mbi->mem_upper = grub_mmap_get_upper () / 1024;
+ mbi->flags |= MULTIBOOT_INFO_MEMORY;
+
+ if (bootdev_set)
+ {
+ mbi->boot_device = bootdev;
+ mbi->flags |= MULTIBOOT_INFO_BOOTDEV;
+ }
+
+ if (elf_sec_num)
+ {
+ mbi->u.elf_sec.addr = ptrdest;
+ grub_memcpy (ptrorig, elf_sections, elf_sec_entsize * elf_sec_num);
+ mbi->u.elf_sec.num = elf_sec_num;
+ mbi->u.elf_sec.size = elf_sec_entsize;
+ mbi->u.elf_sec.shndx = elf_sec_shstrndx;
+
+ mbi->flags |= MULTIBOOT_INFO_ELF_SHDR;
+
+ ptrorig += elf_sec_entsize * elf_sec_num;
+ ptrdest += elf_sec_entsize * elf_sec_num;
+ }
+
+ err = retrieve_video_parameters (mbi, ptrorig, ptrdest);
+ if (err)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ if ((mbi->flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO)
+ && mbi->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED)
+ {
+ ptrorig += mbi->framebuffer_palette_num_colors
+ * sizeof (struct multiboot_color);
+ ptrdest += mbi->framebuffer_palette_num_colors
+ * sizeof (struct multiboot_color);
+ }
+
+#if GRUB_MACHINE_HAS_VBE
+ ptrorig += sizeof (struct grub_vbe_info_block);
+ ptrdest += sizeof (struct grub_vbe_info_block);
+ ptrorig += sizeof (struct grub_vbe_mode_info_block);
+ ptrdest += sizeof (struct grub_vbe_mode_info_block);
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_multiboot_add_elfsyms (grub_size_t num, grub_size_t entsize,
+ unsigned shndx, void *data)
+{
+ elf_sec_num = num;
+ elf_sec_shstrndx = shndx;
+ elf_sec_entsize = entsize;
+ elf_sections = data;
+}
+
+void
+grub_multiboot_free_mbi (void)
+{
+ struct module *cur, *next;
+
+ cmdline_size = 0;
+ total_modcmd = 0;
+ modcnt = 0;
+ grub_free (cmdline);
+ cmdline = NULL;
+ bootdev_set = 0;
+
+ for (cur = modules; cur; cur = next)
+ {
+ next = cur->next;
+ grub_free (cur->cmdline);
+ grub_free (cur);
+ }
+ modules = NULL;
+ modules_last = NULL;
+
+ grub_free (elf_sections);
+ elf_sections = NULL;
+ elf_sec_entsize = 0;
+ elf_sec_num = 0;
+}
+
+grub_err_t
+grub_multiboot_init_mbi (int argc, char *argv[])
+{
+ grub_ssize_t len = 0;
+ char *p;
+ int i;
+
+ grub_multiboot_free_mbi ();
+
+ for (i = 0; i < argc; i++)
+ len += grub_strlen (argv[i]) + 1;
+ if (len == 0)
+ len = 1;
+
+ cmdline = p = grub_malloc (len);
+ if (! cmdline)
+ return grub_errno;
+ cmdline_size = len;
+
+ for (i = 0; i < argc; i++)
+ {
+ p = grub_stpcpy (p, argv[i]);
+ *(p++) = ' ';
+ }
+
+ /* Remove the space after the last word. */
+ if (p != cmdline)
+ p--;
+ *p = '\0';
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_multiboot_add_module (grub_addr_t start, grub_size_t size,
+ int argc, char *argv[])
+{
+ struct module *newmod;
+ char *p;
+ grub_ssize_t len = 0;
+ int i;
+
+ newmod = grub_malloc (sizeof (*newmod));
+ if (!newmod)
+ return grub_errno;
+ newmod->start = start;
+ newmod->size = size;
+ newmod->next = 0;
+
+ for (i = 0; i < argc; i++)
+ len += grub_strlen (argv[i]) + 1;
+
+ if (len == 0)
+ len = 1;
+
+ newmod->cmdline = p = grub_malloc (len);
+ if (! newmod->cmdline)
+ {
+ grub_free (newmod);
+ return grub_errno;
+ }
+ newmod->cmdline_size = len;
+ total_modcmd += ALIGN_UP (len, 4);
+
+ for (i = 0; i < argc; i++)
+ {
+ p = grub_stpcpy (p, argv[i]);
+ *(p++) = ' ';
+ }
+
+ /* Remove the space after the last word. */
+ if (p != newmod->cmdline)
+ p--;
+ *p = '\0';
+
+ if (modules_last)
+ modules_last->next = newmod;
+ else
+ {
+ modules = newmod;
+ modules_last->next = NULL;
+ }
+ modules_last = newmod;
+
+ modcnt++;
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_multiboot_set_bootdev (void)
+{
+ grub_uint32_t biosdev, slice = ~0, part = ~0;
+ grub_device_t dev;
+
+#ifdef GRUB_MACHINE_PCBIOS
+ biosdev = grub_get_root_biosnumber ();
+#else
+ biosdev = 0xffffffff;
+#endif
+
+ if (biosdev == 0xffffffff)
+ return;
+
+ dev = grub_device_open (0);
+ if (dev && dev->disk && dev->disk->partition)
+ {
+ if (dev->disk->partition->parent)
+ {
+ part = dev->disk->partition->number;
+ slice = dev->disk->partition->parent->number;
+ }
+ else
+ slice = dev->disk->partition->number;
+ }
+ if (dev)
+ grub_device_close (dev);
+
+ bootdev = ((biosdev & 0xff) << 24) | ((slice & 0xff) << 16)
+ | ((part & 0xff) << 8) | 0xff;
+ bootdev_set = 1;
+}
diff --git a/grub-core/loader/i386/pc/chainloader.c b/grub-core/loader/i386/pc/chainloader.c
new file mode 100644
index 0000000..794316b
--- /dev/null
+++ b/grub-core/loader/i386/pc/chainloader.c
@@ -0,0 +1,177 @@
+/* chainloader.c - boot another boot loader */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2004,2007,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/machine/loader.h>
+#include <grub/machine/chainloader.h>
+#include <grub/machine/memory.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/device.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/partition.h>
+#include <grub/memory.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/msdos_partition.h>
+#include <grub/machine/biosnum.h>
+#include <grub/cpu/floppy.h>
+#include <grub/i18n.h>
+#include <grub/video.h>
+#include <grub/mm.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+static int boot_drive;
+static void *boot_part_addr;
+
+static grub_err_t
+grub_chainloader_boot (void)
+{
+ grub_video_set_mode ("text", 0, 0);
+ grub_chainloader_real_boot (boot_drive, boot_part_addr);
+
+ /* Never reach here. */
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_chainloader_unload (void)
+{
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_chainloader_cmd (const char *filename, grub_chainloader_flags_t flags)
+{
+ grub_file_t file = 0;
+ grub_uint16_t signature;
+ grub_device_t dev;
+ int drive = -1;
+ void *part_addr = 0;
+
+ grub_dl_ref (my_mod);
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (filename);
+ if (! file)
+ goto fail;
+
+ /* Read the first block. */
+ if (grub_file_read (file, (void *) 0x7C00, GRUB_DISK_SECTOR_SIZE)
+ != GRUB_DISK_SECTOR_SIZE)
+ {
+ if (grub_errno == GRUB_ERR_NONE)
+ grub_error (GRUB_ERR_BAD_OS, "too small");
+
+ goto fail;
+ }
+
+ /* Check the signature. */
+ signature = *((grub_uint16_t *) (0x7C00 + GRUB_DISK_SECTOR_SIZE - 2));
+ if (signature != grub_le_to_cpu16 (0xaa55)
+ && ! (flags & GRUB_CHAINLOADER_FORCE))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid signature");
+ goto fail;
+ }
+
+ grub_file_close (file);
+
+ /* Obtain the partition table from the root device. */
+ drive = grub_get_root_biosnumber ();
+ dev = grub_device_open (0);
+ if (dev && dev->disk && dev->disk->partition)
+ {
+ grub_disk_t disk = dev->disk;
+
+ if (disk)
+ {
+ grub_partition_t p = disk->partition;
+
+ if (p && grub_strcmp (p->partmap->name, "msdos") == 0)
+ {
+ disk->partition = p->parent;
+ grub_disk_read (disk, p->offset, 446, 64,
+ (void *) GRUB_MEMORY_MACHINE_PART_TABLE_ADDR);
+ part_addr = (void *) (GRUB_MEMORY_MACHINE_PART_TABLE_ADDR
+ + (p->index << 4));
+ disk->partition = p;
+ }
+ }
+ }
+
+ if (dev)
+ grub_device_close (dev);
+
+ /* Ignore errors. Perhaps it's not fatal. */
+ grub_errno = GRUB_ERR_NONE;
+
+ boot_drive = drive;
+ boot_part_addr = part_addr;
+
+ grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 1);
+ return;
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ grub_dl_unref (my_mod);
+}
+
+static grub_err_t
+grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_chainloader_flags_t flags = 0;
+
+ if (argc > 0 && grub_strcmp (argv[0], "--force") == 0)
+ {
+ flags |= GRUB_CHAINLOADER_FORCE;
+ argc--;
+ argv++;
+ }
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified");
+ else
+ grub_chainloader_cmd (argv[0], flags);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(chainloader)
+{
+ cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
+ 0, N_("Load another boot loader."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(chainloader)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
new file mode 100644
index 0000000..c6e6b67
--- /dev/null
+++ b/grub-core/loader/i386/pc/linux.c
@@ -0,0 +1,450 @@
+/* linux.c - boot Linux zImage or bzImage */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/machine/loader.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/device.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/memory.h>
+#include <grub/dl.h>
+#include <grub/cpu/linux.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/mm.h>
+#include <grub/cpu/relocator.h>
+#include <grub/video.h>
+#include <grub/i386/floppy.h>
+#include <grub/lib/cmdline.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define GRUB_LINUX_CL_OFFSET 0x9000
+#define GRUB_LINUX_CL_END_OFFSET 0x90FF
+
+static grub_dl_t my_mod;
+
+static grub_size_t linux_mem_size;
+static int loaded;
+static struct grub_relocator *relocator = NULL;
+static grub_addr_t grub_linux_real_target;
+static char *grub_linux_real_chunk;
+static grub_size_t grub_linux16_prot_size;
+
+static grub_err_t
+grub_linux16_boot (void)
+{
+ grub_uint16_t segment;
+ struct grub_relocator16_state state;
+
+ segment = grub_linux_real_target >> 4;
+ state.gs = state.fs = state.es = state.ds = state.ss = segment;
+ state.sp = GRUB_LINUX_SETUP_STACK;
+ state.cs = segment + 0x20;
+ state.ip = 0;
+
+ grub_video_set_mode ("text", 0, 0);
+
+ grub_stop_floppy ();
+
+ return grub_relocator16_boot (relocator, state);
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ grub_relocator_unload (relocator);
+ relocator = NULL;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ struct linux_kernel_header lh;
+ grub_uint8_t setup_sects;
+ grub_size_t real_size;
+ grub_ssize_t len;
+ int i;
+ char *grub_linux_prot_chunk;
+ int grub_linux_is_bzimage;
+ grub_addr_t grub_linux_prot_target;
+ grub_err_t err;
+
+ grub_dl_ref (my_mod);
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
+ goto fail;
+ }
+
+ if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
+ goto fail;
+ }
+
+ if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
+ goto fail;
+ }
+
+ grub_linux_is_bzimage = 0;
+ setup_sects = lh.setup_sects;
+ linux_mem_size = 0;
+
+ if (lh.header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
+ && grub_le_to_cpu16 (lh.version) >= 0x0200)
+ {
+ grub_linux_is_bzimage = (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL);
+ lh.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE;
+
+ /* Put the real mode part at as a high location as possible. */
+ grub_linux_real_target = grub_mmap_get_lower ()
+ - GRUB_LINUX_SETUP_MOVE_SIZE;
+ /* But it must not exceed the traditional area. */
+ if (grub_linux_real_target > GRUB_LINUX_OLD_REAL_MODE_ADDR)
+ grub_linux_real_target = GRUB_LINUX_OLD_REAL_MODE_ADDR;
+
+ if (grub_le_to_cpu16 (lh.version) >= 0x0201)
+ {
+ lh.heap_end_ptr = grub_cpu_to_le16 (GRUB_LINUX_HEAP_END_OFFSET);
+ lh.loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
+ }
+
+ if (grub_le_to_cpu16 (lh.version) >= 0x0202)
+ lh.cmd_line_ptr = grub_linux_real_target + GRUB_LINUX_CL_OFFSET;
+ else
+ {
+ lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
+ lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
+ lh.setup_move_size = grub_cpu_to_le16 (GRUB_LINUX_SETUP_MOVE_SIZE);
+ }
+ }
+ else
+ {
+ /* Your kernel is quite old... */
+ lh.cl_magic = grub_cpu_to_le16 (GRUB_LINUX_CL_MAGIC);
+ lh.cl_offset = grub_cpu_to_le16 (GRUB_LINUX_CL_OFFSET);
+
+ setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
+
+ grub_linux_real_target = GRUB_LINUX_OLD_REAL_MODE_ADDR;
+ }
+
+ /* If SETUP_SECTS is not set, set it to the default (4). */
+ if (! setup_sects)
+ setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
+
+ real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
+ grub_linux16_prot_size = grub_file_size (file)
+ - real_size - GRUB_DISK_SECTOR_SIZE;
+
+ if (! grub_linux_is_bzimage
+ && GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size
+ > grub_linux_real_target)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "too big zImage (0x%x > 0x%x), use bzImage instead",
+ (char *) GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size,
+ (grub_size_t) grub_linux_real_target);
+ goto fail;
+ }
+
+ if (grub_linux_real_target + GRUB_LINUX_SETUP_MOVE_SIZE
+ > grub_mmap_get_lower ())
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "too small lower memory (0x%x > 0x%x)",
+ grub_linux_real_target + GRUB_LINUX_SETUP_MOVE_SIZE,
+ (int) grub_mmap_get_lower ());
+ goto fail;
+ }
+
+ grub_printf (" [Linux-%s, setup=0x%x, size=0x%x]\n",
+ grub_linux_is_bzimage ? "bzImage" : "zImage", real_size,
+ grub_linux16_prot_size);
+
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ goto fail;
+
+ for (i = 1; i < argc; i++)
+ if (grub_memcmp (argv[i], "vga=", 4) == 0)
+ {
+ /* Video mode selection support. */
+ grub_uint16_t vid_mode;
+ char *val = argv[i] + 4;
+
+ if (grub_strcmp (val, "normal") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_NORMAL;
+ else if (grub_strcmp (val, "ext") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_EXTENDED;
+ else if (grub_strcmp (val, "ask") == 0)
+ vid_mode = GRUB_LINUX_VID_MODE_ASK;
+ else
+ vid_mode = (grub_uint16_t) grub_strtoul (val, 0, 0);
+
+ if (grub_errno)
+ goto fail;
+
+ lh.vid_mode = grub_cpu_to_le16 (vid_mode);
+ }
+ else if (grub_memcmp (argv[i], "mem=", 4) == 0)
+ {
+ char *val = argv[i] + 4;
+
+ linux_mem_size = grub_strtoul (val, &val, 0);
+
+ if (grub_errno)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ linux_mem_size = 0;
+ }
+ else
+ {
+ int shift = 0;
+
+ switch (grub_tolower (val[0]))
+ {
+ case 'g':
+ shift += 10;
+ case 'm':
+ shift += 10;
+ case 'k':
+ shift += 10;
+ default:
+ break;
+ }
+
+ /* Check an overflow. */
+ if (linux_mem_size > (~0UL >> shift))
+ linux_mem_size = 0;
+ else
+ linux_mem_size <<= shift;
+ }
+ }
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ grub_linux_real_target,
+ GRUB_LINUX_SETUP_MOVE_SIZE);
+ if (err)
+ return err;
+ grub_linux_real_chunk = get_virtual_current_address (ch);
+ }
+
+ /* Put the real mode code at the temporary address. */
+ grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh));
+
+ len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh);
+ if (grub_file_read (file, grub_linux_real_chunk + sizeof (lh), len) != len)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
+ || grub_le_to_cpu16 (lh.version) < 0x0200)
+ /* Clear the heap space. */
+ grub_memset (grub_linux_real_chunk
+ + ((setup_sects + 1) << GRUB_DISK_SECTOR_BITS),
+ 0,
+ ((GRUB_LINUX_MAX_SETUP_SECTS - setup_sects - 1)
+ << GRUB_DISK_SECTOR_BITS));
+
+ /* Create kernel command line. */
+ grub_memcpy ((char *)grub_linux_real_chunk + GRUB_LINUX_CL_OFFSET,
+ LINUX_IMAGE, sizeof (LINUX_IMAGE));
+ grub_create_loader_cmdline (argc, argv,
+ (char *)grub_linux_real_chunk
+ + GRUB_LINUX_CL_OFFSET + sizeof (LINUX_IMAGE) - 1,
+ GRUB_LINUX_CL_END_OFFSET - GRUB_LINUX_CL_OFFSET
+ - (sizeof (LINUX_IMAGE) - 1));
+
+ if (grub_linux_is_bzimage)
+ grub_linux_prot_target = GRUB_LINUX_BZIMAGE_ADDR;
+ else
+ grub_linux_prot_target = GRUB_LINUX_ZIMAGE_ADDR;
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ grub_linux_prot_target,
+ grub_linux16_prot_size);
+ if (err)
+ return err;
+ grub_linux_prot_chunk = get_virtual_current_address (ch);
+ }
+
+ len = grub_linux16_prot_size;
+ if (grub_file_read (file, grub_linux_prot_chunk, grub_linux16_prot_size)
+ != (grub_ssize_t) grub_linux16_prot_size)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ grub_loader_set (grub_linux16_boot, grub_linux_unload, 0);
+ loaded = 1;
+ }
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ grub_relocator_unload (relocator);
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_ssize_t size;
+ grub_addr_t addr_max, addr_min;
+ struct linux_kernel_header *lh;
+ grub_uint8_t *initrd_chunk;
+ grub_addr_t initrd_addr;
+ grub_err_t err;
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
+ goto fail;
+ }
+
+ if (!loaded)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
+ goto fail;
+ }
+
+ lh = (struct linux_kernel_header *) grub_linux_real_chunk;
+
+ if (!(lh->header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
+ && grub_le_to_cpu16 (lh->version) >= 0x0200))
+ {
+ grub_error (GRUB_ERR_BAD_OS, "the kernel is too old for initrd");
+ goto fail;
+ }
+
+ /* Get the highest address available for the initrd. */
+ if (grub_le_to_cpu16 (lh->version) >= 0x0203)
+ {
+ addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);
+
+ /* XXX in reality, Linux specifies a bogus value, so
+ it is necessary to make sure that ADDR_MAX does not exceed
+ 0x3fffffff. */
+ if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)
+ addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
+ }
+ else
+ addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
+
+ if (linux_mem_size != 0 && linux_mem_size < addr_max)
+ addr_max = linux_mem_size;
+
+ /* Linux 2.3.xx has a bug in the memory range check, so avoid
+ the last page.
+ Linux 2.2.xx has a bug in the memory range check, which is
+ worse than that of Linux 2.3.xx, so avoid the last 64kb. */
+ addr_max -= 0x10000;
+
+ addr_min = GRUB_LINUX_BZIMAGE_ADDR + grub_linux16_prot_size;
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (argv[0]);
+ if (!file)
+ goto fail;
+
+ size = grub_file_size (file);
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ addr_min, addr_max - size,
+ size, 0x1000,
+ GRUB_RELOCATOR_PREFERENCE_HIGH);
+ if (err)
+ return err;
+ initrd_chunk = get_virtual_current_address (ch);
+ initrd_addr = get_physical_target_address (ch);
+ }
+
+ if (grub_file_read (file, initrd_chunk, size) != size)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ lh->ramdisk_image = initrd_addr;
+ lh->ramdisk_size = size;
+
+ fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_linux, cmd_initrd;
+
+GRUB_MOD_INIT(linux16)
+{
+ cmd_linux =
+ grub_register_command ("linux16", grub_cmd_linux,
+ 0, N_("Load Linux."));
+ cmd_initrd =
+ grub_register_command ("initrd16", grub_cmd_initrd,
+ 0, N_("Load initrd."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(linux16)
+{
+ grub_unregister_command (cmd_linux);
+ grub_unregister_command (cmd_initrd);
+}
diff --git a/grub-core/loader/i386/pc/ntldr.c b/grub-core/loader/i386/pc/ntldr.c
new file mode 100644
index 0000000..4a08b54
--- /dev/null
+++ b/grub-core/loader/i386/pc/ntldr.c
@@ -0,0 +1,159 @@
+/* chainloader.c - boot another boot loader */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2004,2007,2009,2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/loader.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/device.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/partition.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/machine/biosnum.h>
+#include <grub/i18n.h>
+#include <grub/video.h>
+#include <grub/mm.h>
+#include <grub/cpu/relocator.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+static struct grub_relocator *rel;
+static grub_uint32_t edx = 0xffffffff;
+
+#define GRUB_NTLDR_SEGMENT 0x2000
+
+static grub_err_t
+grub_ntldr_boot (void)
+{
+ struct grub_relocator16_state state = {
+ .cs = GRUB_NTLDR_SEGMENT,
+ .ip = 0,
+ .ds = 0,
+ .es = 0,
+ .fs = 0,
+ .gs = 0,
+ .ss = 0,
+ .sp = 0x7c00,
+ .edx = edx
+ };
+ grub_video_set_mode ("text", 0, 0);
+
+ return grub_relocator16_boot (rel, state);
+}
+
+static grub_err_t
+grub_ntldr_unload (void)
+{
+ grub_relocator_unload (rel);
+ rel = NULL;
+ grub_dl_unref (my_mod);
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_ntldr (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ grub_err_t err;
+ void *bs, *ntldr;
+ grub_size_t ntldrsize;
+ grub_device_t dev;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified");
+
+ grub_dl_ref (my_mod);
+
+ rel = grub_relocator_new ();
+ if (!rel)
+ goto fail;
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, 0x7C00,
+ GRUB_DISK_SECTOR_SIZE);
+ if (err)
+ goto fail;
+ bs = get_virtual_current_address (ch);
+ }
+
+ edx = grub_get_root_biosnumber ();
+ dev = grub_device_open (0);
+
+ if (dev && dev->disk)
+ {
+ err = grub_disk_read (dev->disk, 0, 0, GRUB_DISK_SECTOR_SIZE, bs);
+ if (err)
+ {
+ grub_device_close (dev);
+ goto fail;
+ }
+ }
+
+ if (dev)
+ grub_device_close (dev);
+
+ ntldrsize = grub_file_size (file);
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (rel, &ch, GRUB_NTLDR_SEGMENT << 4,
+ ntldrsize);
+ if (err)
+ goto fail;
+ ntldr = get_virtual_current_address (ch);
+ }
+
+ if (grub_file_read (file, ntldr, ntldrsize)
+ != (grub_ssize_t) ntldrsize)
+ goto fail;
+
+ grub_loader_set (grub_ntldr_boot, grub_ntldr_unload, 1);
+ return GRUB_ERR_NONE;
+
+ fail:
+
+ if (file)
+ grub_file_close (file);
+
+ grub_ntldr_unload ();
+
+ return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(ntldr)
+{
+ cmd = grub_register_command ("ntldr", grub_cmd_ntldr,
+ 0, N_("Load NTLDR or BootMGR."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(ntldr)
+{
+ grub_unregister_command (cmd);
+}
diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c
new file mode 100644
index 0000000..b877b0e
--- /dev/null
+++ b/grub-core/loader/i386/xnu.c
@@ -0,0 +1,1137 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/xnu.h>
+#include <grub/cpu/xnu.h>
+#include <grub/mm.h>
+#include <grub/loader.h>
+#include <grub/autoefi.h>
+#include <grub/i386/tsc.h>
+#include <grub/efi/api.h>
+#include <grub/i386/pit.h>
+#include <grub/misc.h>
+#include <grub/charset.h>
+#include <grub/term.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/bitmap_scale.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+#define DEFAULT_VIDEO_MODE "auto"
+
+char grub_xnu_cmdline[1024];
+grub_uint32_t grub_xnu_entry_point, grub_xnu_arg1, grub_xnu_stack;
+
+/* Aliases set for some tables. */
+struct tbl_alias
+{
+ grub_efi_guid_t guid;
+ char *name;
+};
+
+static struct tbl_alias table_aliases[] =
+ {
+ {GRUB_EFI_ACPI_20_TABLE_GUID, "ACPI_20"},
+ {GRUB_EFI_ACPI_TABLE_GUID, "ACPI"},
+ };
+
+struct grub_xnu_devprop_device_descriptor
+{
+ struct grub_xnu_devprop_device_descriptor *next;
+ struct property_descriptor *properties;
+ struct grub_efi_device_path *path;
+ int pathlen;
+};
+
+static int
+utf16_strlen (grub_uint16_t *in)
+{
+ int i;
+ for (i = 0; in[i]; i++);
+ return i;
+}
+
+/* Read frequency from a string in MHz and return it in Hz. */
+static grub_uint64_t
+readfrequency (const char *str)
+{
+ grub_uint64_t num = 0;
+ int mul = 1000000;
+ int found = 0;
+
+ while (*str)
+ {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9)
+ break;
+
+ found = 1;
+
+ num = num * 10 + digit;
+ str++;
+ }
+ num *= 1000000;
+ if (*str == '.')
+ {
+ str++;
+ while (*str)
+ {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9)
+ break;
+
+ found = 1;
+
+ mul /= 10;
+ num = num + mul * digit;
+ str++;
+ }
+ }
+ if (! found)
+ return 0;
+
+ return num;
+}
+
+/* Thanks to Kabyl for precious information about Intel architecture. */
+static grub_uint64_t
+guessfsb (void)
+{
+ const grub_uint64_t sane_value = 100000000;
+ grub_uint32_t manufacturer[3], max_cpuid, capabilities, msrlow;
+ grub_uint64_t start_tsc;
+ grub_uint64_t end_tsc;
+ grub_uint64_t tsc_ticks_per_ms;
+
+ if (! grub_cpu_is_cpuid_supported ())
+ return sane_value;
+
+#ifdef APPLE_CC
+ asm volatile ("movl $0, %%eax\n"
+#ifdef __x86_64__
+ "push %%rbx\n"
+#else
+ "push %%ebx\n"
+#endif
+ "cpuid\n"
+#ifdef __x86_64__
+ "pop %%rbx\n"
+#else
+ "pop %%ebx\n"
+#endif
+ : "=a" (max_cpuid),
+ "=d" (manufacturer[1]), "=c" (manufacturer[2]));
+
+ /* Only Intel for now is done. */
+ if (grub_memcmp (manufacturer + 1, "ineIntel", 12) != 0)
+ return sane_value;
+
+#else
+ asm volatile ("movl $0, %%eax\n"
+ "cpuid"
+ : "=a" (max_cpuid), "=b" (manufacturer[0]),
+ "=d" (manufacturer[1]), "=c" (manufacturer[2]));
+
+ /* Only Intel for now is done. */
+ if (grub_memcmp (manufacturer, "GenuineIntel", 12) != 0)
+ return sane_value;
+#endif
+
+ /* Check Speedstep. */
+ if (max_cpuid < 1)
+ return sane_value;
+
+#ifdef APPLE_CC
+ asm volatile ("movl $1, %%eax\n"
+#ifdef __x86_64__
+ "push %%rbx\n"
+#else
+ "push %%ebx\n"
+#endif
+ "cpuid\n"
+#ifdef __x86_64__
+ "pop %%rbx\n"
+#else
+ "pop %%ebx\n"
+#endif
+ : "=c" (capabilities):
+ : "%rax", "%rdx");
+#else
+ asm volatile ("movl $1, %%eax\n"
+ "cpuid"
+ : "=c" (capabilities):
+ : "%rax", "%rbx", "%rdx");
+#endif
+
+ if (! (capabilities & (1 << 7)))
+ return sane_value;
+
+ /* Calibrate the TSC rate. */
+
+ start_tsc = grub_get_tsc ();
+ grub_pit_wait (0xffff);
+ end_tsc = grub_get_tsc ();
+
+ tsc_ticks_per_ms = grub_divmod64 (end_tsc - start_tsc, 55, 0);
+
+ /* Read the multiplier. */
+ asm volatile ("movl $0x198, %%ecx\n"
+ "rdmsr"
+ : "=d" (msrlow)
+ :
+ : "%ecx", "%eax");
+
+ return grub_divmod64 (2000 * tsc_ticks_per_ms,
+ ((msrlow >> 7) & 0x3e) + ((msrlow >> 14) & 1), 0);
+}
+
+struct property_descriptor
+{
+ struct property_descriptor *next;
+ grub_uint8_t *name;
+ grub_uint16_t *name16;
+ int name16len;
+ int length;
+ void *data;
+};
+
+static struct grub_xnu_devprop_device_descriptor *devices = 0;
+
+grub_err_t
+grub_xnu_devprop_remove_property (struct grub_xnu_devprop_device_descriptor *dev,
+ char *name)
+{
+ struct property_descriptor *prop;
+ prop = grub_named_list_find (GRUB_AS_NAMED_LIST_P (&dev->properties), name);
+ if (!prop)
+ return GRUB_ERR_NONE;
+
+ grub_free (prop->name);
+ grub_free (prop->name16);
+ grub_free (prop->data);
+
+ grub_list_remove (GRUB_AS_LIST_P (&dev->properties), GRUB_AS_LIST (prop));
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_devprop_remove_device (struct grub_xnu_devprop_device_descriptor *dev)
+{
+ void *t;
+ struct property_descriptor *prop;
+
+ grub_list_remove (GRUB_AS_LIST_P (&devices), GRUB_AS_LIST (dev));
+
+ for (prop = dev->properties; prop; )
+ {
+ grub_free (prop->name);
+ grub_free (prop->name16);
+ grub_free (prop->data);
+ t = prop;
+ prop = prop->next;
+ grub_free (t);
+ }
+
+ grub_free (dev->path);
+ grub_free (dev);
+
+ return GRUB_ERR_NONE;
+}
+
+struct grub_xnu_devprop_device_descriptor *
+grub_xnu_devprop_add_device (struct grub_efi_device_path *path, int length)
+{
+ struct grub_xnu_devprop_device_descriptor *ret;
+
+ ret = grub_zalloc (sizeof (*ret));
+ if (!ret)
+ return 0;
+
+ ret->path = grub_malloc (length);
+ if (!ret->path)
+ {
+ grub_free (ret);
+ return 0;
+ }
+ ret->pathlen = length;
+ grub_memcpy (ret->path, path, length);
+
+ grub_list_push (GRUB_AS_LIST_P (&devices), GRUB_AS_LIST (ret));
+
+ return ret;
+}
+
+static grub_err_t
+grub_xnu_devprop_add_property (struct grub_xnu_devprop_device_descriptor *dev,
+ grub_uint8_t *utf8, grub_uint16_t *utf16,
+ int utf16len, void *data, int datalen)
+{
+ struct property_descriptor *prop;
+
+ prop = grub_malloc (sizeof (*prop));
+ if (!prop)
+ return grub_errno;
+
+ prop->name = utf8;
+ prop->name16 = utf16;
+ prop->name16len = utf16len;
+
+ prop->length = datalen;
+ prop->data = grub_malloc (prop->length);
+ if (!prop->data)
+ {
+ grub_free (prop);
+ grub_free (prop->name);
+ grub_free (prop->name16);
+ return grub_errno;
+ }
+ grub_memcpy (prop->data, data, prop->length);
+ grub_list_push (GRUB_AS_LIST_P (&dev->properties),
+ GRUB_AS_LIST (prop));
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_devprop_add_property_utf8 (struct grub_xnu_devprop_device_descriptor *dev,
+ char *name, void *data, int datalen)
+{
+ grub_uint8_t *utf8;
+ grub_uint16_t *utf16;
+ int len, utf16len;
+ grub_err_t err;
+
+ utf8 = (grub_uint8_t *) grub_strdup (name);
+ if (!utf8)
+ return grub_errno;
+
+ len = grub_strlen (name);
+ utf16 = grub_malloc (sizeof (grub_uint16_t) * len);
+ if (!utf16)
+ {
+ grub_free (utf8);
+ return grub_errno;
+ }
+
+ utf16len = grub_utf8_to_utf16 (utf16, len, utf8, len, NULL);
+ if (utf16len < 0)
+ {
+ grub_free (utf8);
+ grub_free (utf16);
+ return grub_errno;
+ }
+
+ err = grub_xnu_devprop_add_property (dev, utf8, utf16,
+ utf16len, data, datalen);
+ if (err)
+ {
+ grub_free (utf8);
+ grub_free (utf16);
+ return err;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_devprop_add_property_utf16 (struct grub_xnu_devprop_device_descriptor *dev,
+ grub_uint16_t *name, int namelen,
+ void *data, int datalen)
+{
+ grub_uint8_t *utf8;
+ grub_uint16_t *utf16;
+ grub_err_t err;
+
+ utf16 = grub_malloc (sizeof (grub_uint16_t) * namelen);
+ if (!utf16)
+ return grub_errno;
+ grub_memcpy (utf16, name, sizeof (grub_uint16_t) * namelen);
+
+ utf8 = grub_malloc (namelen * 4 + 1);
+ if (!utf8)
+ {
+ grub_free (utf8);
+ return grub_errno;
+ }
+
+ *grub_utf16_to_utf8 ((grub_uint8_t *) utf8, name, namelen) = '\0';
+
+ err = grub_xnu_devprop_add_property (dev, utf8, utf16,
+ namelen, data, datalen);
+ if (err)
+ {
+ grub_free (utf8);
+ grub_free (utf16);
+ return err;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static inline int
+hextoval (char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
+ return 0;
+}
+
+void
+grub_cpu_xnu_unload (void)
+{
+ struct grub_xnu_devprop_device_descriptor *dev1, *dev2;
+
+ for (dev1 = devices; dev1; )
+ {
+ dev2 = dev1->next;
+ grub_xnu_devprop_remove_device (dev1);
+ dev1 = dev2;
+ }
+}
+
+static grub_err_t
+grub_cpu_xnu_fill_devprop (void)
+{
+ struct grub_xnu_devtree_key *efikey;
+ int total_length = sizeof (struct grub_xnu_devprop_header);
+ struct grub_xnu_devtree_key *devprop;
+ struct grub_xnu_devprop_device_descriptor *device;
+ void *ptr;
+ struct grub_xnu_devprop_header *head;
+ void *t;
+ int numdevs = 0;
+
+ /* The key "efi". */
+ efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
+ if (! efikey)
+ return grub_errno;
+
+ for (device = devices; device; device = device->next)
+ {
+ struct property_descriptor *propdesc;
+ total_length += sizeof (struct grub_xnu_devprop_device_header);
+ total_length += device->pathlen;
+
+ for (propdesc = device->properties; propdesc; propdesc = propdesc->next)
+ {
+ total_length += sizeof (grub_uint32_t);
+ total_length += sizeof (grub_uint16_t)
+ * (propdesc->name16len + 1);
+ total_length += sizeof (grub_uint32_t);
+ total_length += propdesc->length;
+ }
+ numdevs++;
+ }
+
+ devprop = grub_xnu_create_value (&(efikey->first_child), "device-properties");
+ if (devprop)
+ {
+ devprop->data = grub_malloc (total_length);
+ devprop->datasize = total_length;
+ }
+
+ ptr = devprop->data;
+ head = ptr;
+ ptr = head + 1;
+ head->length = total_length;
+ head->alwaysone = 1;
+ head->num_devices = numdevs;
+ for (device = devices; device; )
+ {
+ struct grub_xnu_devprop_device_header *devhead;
+ struct property_descriptor *propdesc;
+ devhead = ptr;
+ devhead->num_values = 0;
+ ptr = devhead + 1;
+
+ grub_memcpy (ptr, device->path, device->pathlen);
+ ptr = (char *) ptr + device->pathlen;
+
+ for (propdesc = device->properties; propdesc; )
+ {
+ grub_uint32_t *len;
+ grub_uint16_t *name;
+ void *data;
+
+ len = ptr;
+ *len = 2 * propdesc->name16len + sizeof (grub_uint16_t)
+ + sizeof (grub_uint32_t);
+ ptr = len + 1;
+
+ name = ptr;
+ grub_memcpy (name, propdesc->name16, 2 * propdesc->name16len);
+ name += propdesc->name16len;
+
+ /* NUL terminator. */
+ *name = 0;
+ ptr = name + 1;
+
+ len = ptr;
+ *len = propdesc->length + sizeof (grub_uint32_t);
+ data = len + 1;
+ ptr = data;
+ grub_memcpy (ptr, propdesc->data, propdesc->length);
+ ptr = (char *) ptr + propdesc->length;
+
+ grub_free (propdesc->name);
+ grub_free (propdesc->name16);
+ grub_free (propdesc->data);
+ t = propdesc;
+ propdesc = propdesc->next;
+ grub_free (t);
+ devhead->num_values++;
+ }
+
+ devhead->length = (char *) ptr - (char *) devhead;
+ t = device;
+ device = device->next;
+ grub_free (t);
+ }
+
+ devices = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_devprop_load (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_file_t file;
+ void *buf, *bufstart, *bufend;
+ struct grub_xnu_devprop_header *head;
+ grub_size_t size;
+ unsigned i, j;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+
+ file = grub_file_open (args[0]);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "couldn't load device-propertie dump");
+ size = grub_file_size (file);
+ buf = grub_malloc (size);
+ if (!buf)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+ if (grub_file_read (file, buf, size) != (grub_ssize_t) size)
+ {
+ grub_file_close (file);
+ return grub_errno;
+ }
+ grub_file_close (file);
+
+ bufstart = buf;
+ bufend = (char *) buf + size;
+ head = buf;
+ buf = head + 1;
+ for (i = 0; i < grub_le_to_cpu32 (head->num_devices) && buf < bufend; i++)
+ {
+ struct grub_efi_device_path *dp, *dpstart;
+ struct grub_xnu_devprop_device_descriptor *dev;
+ struct grub_xnu_devprop_device_header *devhead;
+
+ devhead = buf;
+ buf = devhead + 1;
+ dpstart = buf;
+
+ do
+ {
+ dp = buf;
+ buf = (char *) buf + GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+ }
+ while (!GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp) && buf < bufend);
+
+ dev = grub_xnu_devprop_add_device (dpstart, (char *) buf
+ - (char *) dpstart);
+
+ for (j = 0; j < grub_le_to_cpu32 (devhead->num_values) && buf < bufend;
+ j++)
+ {
+ grub_uint32_t *namelen;
+ grub_uint32_t *datalen;
+ grub_uint16_t *utf16;
+ void *data;
+ grub_err_t err;
+
+ namelen = buf;
+ buf = namelen + 1;
+ if (buf >= bufend)
+ break;
+
+ utf16 = buf;
+ buf = (char *) buf + *namelen - sizeof (grub_uint32_t);
+ if (buf >= bufend)
+ break;
+
+ datalen = buf;
+ buf = datalen + 1;
+ if (buf >= bufend)
+ break;
+
+ data = buf;
+ buf = (char *) buf + *datalen - sizeof (grub_uint32_t);
+ if (buf >= bufend)
+ break;
+ err = grub_xnu_devprop_add_property_utf16
+ (dev, utf16, (*namelen - sizeof (grub_uint32_t)
+ - sizeof (grub_uint16_t)) / sizeof (grub_uint16_t),
+ data, *datalen - sizeof (grub_uint32_t));
+ if (err)
+ {
+ grub_free (bufstart);
+ return err;
+ }
+ }
+ }
+
+ grub_free (bufstart);
+ return GRUB_ERR_NONE;
+}
+
+/* Fill device tree. */
+/* FIXME: some entries may be platform-agnostic. Move them to loader/xnu.c. */
+grub_err_t
+grub_cpu_xnu_fill_devicetree (void)
+{
+ struct grub_xnu_devtree_key *efikey;
+ struct grub_xnu_devtree_key *cfgtablekey;
+ struct grub_xnu_devtree_key *curval;
+ struct grub_xnu_devtree_key *runtimesrvkey;
+ struct grub_xnu_devtree_key *platformkey;
+ unsigned i, j;
+
+ /* The value "model". */
+ /* FIXME: may this value be sometimes different? */
+ curval = grub_xnu_create_value (&grub_xnu_devtree_root, "model");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof ("ACPI");
+ curval->data = grub_strdup ("ACPI");
+ curval = grub_xnu_create_value (&grub_xnu_devtree_root, "compatible");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof ("ACPI");
+ curval->data = grub_strdup ("ACPI");
+
+ /* The key "efi". */
+ efikey = grub_xnu_create_key (&grub_xnu_devtree_root, "efi");
+ if (! efikey)
+ return grub_errno;
+
+ /* Information about firmware. */
+ curval = grub_xnu_create_value (&(efikey->first_child), "firmware-revision");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = (SYSTEM_TABLE_SIZEOF (firmware_revision));
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ grub_memcpy (curval->data, (SYSTEM_TABLE_VAR(firmware_revision)),
+ curval->datasize);
+
+ curval = grub_xnu_create_value (&(efikey->first_child), "firmware-vendor");
+ if (! curval)
+ return grub_errno;
+ curval->datasize =
+ 2 * (utf16_strlen (SYSTEM_TABLE_PTR (firmware_vendor)) + 1);
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ grub_memcpy (curval->data, SYSTEM_TABLE_PTR (firmware_vendor),
+ curval->datasize);
+
+ curval = grub_xnu_create_value (&(efikey->first_child), "firmware-abi");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof ("EFI32");
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ if (SIZEOF_OF_UINTN == 4)
+ grub_memcpy (curval->data, "EFI32", curval->datasize);
+ else
+ grub_memcpy (curval->data, "EFI64", curval->datasize);
+
+ /* The key "platform". */
+ platformkey = grub_xnu_create_key (&(efikey->first_child),
+ "platform");
+ if (! platformkey)
+ return grub_errno;
+
+ /* Pass FSB frequency to the kernel. */
+ curval = grub_xnu_create_value (&(platformkey->first_child), "FSBFrequency");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof (grub_uint64_t);
+ curval->data = grub_malloc (curval->datasize);
+ if (!curval->data)
+ return grub_errno;
+
+ /* First see if user supplies the value. */
+ char *fsbvar = grub_env_get ("fsb");
+ if (! fsbvar)
+ *((grub_uint64_t *) curval->data) = 0;
+ else
+ *((grub_uint64_t *) curval->data) = readfrequency (fsbvar);
+ /* Try autodetect. */
+ if (! *((grub_uint64_t *) curval->data))
+ *((grub_uint64_t *) curval->data) = guessfsb ();
+ grub_dprintf ("xnu", "fsb autodetected as %llu\n",
+ (unsigned long long) *((grub_uint64_t *) curval->data));
+
+ cfgtablekey = grub_xnu_create_key (&(efikey->first_child),
+ "configuration-table");
+ if (!cfgtablekey)
+ return grub_errno;
+
+ /* Fill "configuration-table" key. */
+ for (i = 0; i < SYSTEM_TABLE (num_table_entries); i++)
+ {
+ void *ptr;
+ struct grub_xnu_devtree_key *curkey;
+ grub_efi_guid_t guid;
+ char guidbuf[64];
+
+ /* Retrieve current key. */
+#ifdef GRUB_MACHINE_EFI
+ {
+ ptr = (void *)
+ grub_efi_system_table->configuration_table[i].vendor_table;
+ guid = grub_efi_system_table->configuration_table[i].vendor_guid;
+ }
+#else
+ if (SIZEOF_OF_UINTN == 4)
+ {
+ ptr = UINT_TO_PTR (((grub_efiemu_configuration_table32_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i]
+ .vendor_table);
+ guid =
+ ((grub_efiemu_configuration_table32_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
+ }
+ else
+ {
+ ptr = UINT_TO_PTR (((grub_efiemu_configuration_table64_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i]
+ .vendor_table);
+ guid =
+ ((grub_efiemu_configuration_table64_t *)
+ SYSTEM_TABLE_PTR (configuration_table))[i].vendor_guid;
+ }
+#endif
+
+ /* The name of key for new table. */
+ grub_snprintf (guidbuf, sizeof (guidbuf), "%08x-%04x-%04x-%02x%02x-",
+ guid.data1, guid.data2, guid.data3, guid.data4[0],
+ guid.data4[1]);
+ for (j = 2; j < 8; j++)
+ grub_snprintf (guidbuf + grub_strlen (guidbuf),
+ sizeof (guidbuf) - grub_strlen (guidbuf),
+ "%02x", guid.data4[j]);
+ /* For some reason GUID has to be in uppercase. */
+ for (j = 0; guidbuf[j] ; j++)
+ if (guidbuf[j] >= 'a' && guidbuf[j] <= 'f')
+ guidbuf[j] += 'A' - 'a';
+ curkey = grub_xnu_create_key (&(cfgtablekey->first_child), guidbuf);
+ if (! curkey)
+ return grub_errno;
+
+ curval = grub_xnu_create_value (&(curkey->first_child), "guid");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = sizeof (guid);
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ grub_memcpy (curval->data, &guid, curval->datasize);
+
+ /* The value "table". */
+ curval = grub_xnu_create_value (&(curkey->first_child), "table");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = SIZEOF_OF_UINTN;
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ if (SIZEOF_OF_UINTN == 4)
+ *((grub_uint32_t *)curval->data) = PTR_TO_UINT32 (ptr);
+ else
+ *((grub_uint64_t *)curval->data) = PTR_TO_UINT64 (ptr);
+
+ /* Create alias. */
+ for (j = 0; j < sizeof (table_aliases) / sizeof (table_aliases[0]); j++)
+ if (grub_memcmp (&table_aliases[j].guid, &guid, sizeof (guid)) == 0)
+ break;
+ if (j != sizeof (table_aliases) / sizeof (table_aliases[0]))
+ {
+ curval = grub_xnu_create_value (&(curkey->first_child), "alias");
+ if (!curval)
+ return grub_errno;
+ curval->datasize = grub_strlen (table_aliases[j].name) + 1;
+ curval->data = grub_malloc (curval->datasize);
+ if (!curval->data)
+ return grub_errno;
+ grub_memcpy (curval->data, table_aliases[j].name, curval->datasize);
+ }
+ }
+
+ /* Create and fill "runtime-services" key. */
+ runtimesrvkey = grub_xnu_create_key (&(efikey->first_child),
+ "runtime-services");
+ if (! runtimesrvkey)
+ return grub_errno;
+ curval = grub_xnu_create_value (&(runtimesrvkey->first_child), "table");
+ if (! curval)
+ return grub_errno;
+ curval->datasize = SIZEOF_OF_UINTN;
+ curval->data = grub_malloc (curval->datasize);
+ if (! curval->data)
+ return grub_errno;
+ if (SIZEOF_OF_UINTN == 4)
+ *((grub_uint32_t *) curval->data)
+ = PTR_TO_UINT32 (SYSTEM_TABLE_PTR (runtime_services));
+ else
+ *((grub_uint64_t *) curval->data)
+ = PTR_TO_UINT64 (SYSTEM_TABLE_PTR (runtime_services));
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_xnu_boot_resume (void)
+{
+ struct grub_relocator32_state state;
+
+ state.esp = grub_xnu_stack;
+ state.ebp = grub_xnu_stack;
+ state.eip = grub_xnu_entry_point;
+ state.eax = grub_xnu_arg1;
+
+ return grub_relocator32_boot (grub_xnu_relocator, state);
+}
+
+/* Setup video for xnu. */
+static grub_err_t
+grub_xnu_set_video (struct grub_xnu_boot_params *params)
+{
+ struct grub_video_mode_info mode_info;
+ int ret;
+ char *tmp;
+ const char *modevar;
+ void *framebuffer;
+ grub_err_t err;
+ struct grub_video_bitmap *bitmap = NULL;
+
+ modevar = grub_env_get ("gfxpayload");
+ /* Consider only graphical 32-bit deep modes. */
+ if (! modevar || *modevar == 0)
+ err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
+ GRUB_VIDEO_MODE_TYPE_PURE_TEXT
+ | GRUB_VIDEO_MODE_TYPE_DEPTH_MASK,
+ 32 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS);
+ else
+ {
+ tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
+ if (! tmp)
+ return grub_errno;
+ err = grub_video_set_mode (tmp,
+ GRUB_VIDEO_MODE_TYPE_PURE_TEXT
+ | GRUB_VIDEO_MODE_TYPE_DEPTH_MASK,
+ 32 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS);
+ grub_free (tmp);
+ }
+
+ if (err)
+ return err;
+
+ ret = grub_video_get_info (&mode_info);
+ if (ret)
+ return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters");
+
+ if (grub_xnu_bitmap)
+ {
+ if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH)
+ err = grub_video_bitmap_create_scaled (&bitmap,
+ mode_info.width,
+ mode_info.height,
+ grub_xnu_bitmap,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ else
+ bitmap = grub_xnu_bitmap;
+ }
+
+ if (bitmap)
+ {
+ if (grub_xnu_bitmap_mode == GRUB_XNU_BITMAP_STRETCH)
+ err = grub_video_bitmap_create_scaled (&bitmap,
+ mode_info.width,
+ mode_info.height,
+ grub_xnu_bitmap,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ else
+ bitmap = grub_xnu_bitmap;
+ }
+
+ if (bitmap)
+ {
+ int x, y;
+
+ x = mode_info.width - bitmap->mode_info.width;
+ x /= 2;
+ y = mode_info.height - bitmap->mode_info.height;
+ y /= 2;
+ err = grub_video_blit_bitmap (bitmap,
+ GRUB_VIDEO_BLIT_REPLACE,
+ x > 0 ? x : 0,
+ y > 0 ? y : 0,
+ x < 0 ? -x : 0,
+ y < 0 ? -y : 0,
+ min (bitmap->mode_info.width,
+ mode_info.width),
+ min (bitmap->mode_info.height,
+ mode_info.height));
+ }
+ if (err)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ bitmap = 0;
+ }
+
+ ret = grub_video_get_info_and_fini (&mode_info, &framebuffer);
+ if (ret)
+ return grub_error (GRUB_ERR_IO, "couldn't retrieve video parameters");
+
+ params->lfb_width = mode_info.width;
+ params->lfb_height = mode_info.height;
+ params->lfb_depth = mode_info.bpp;
+ params->lfb_line_len = mode_info.pitch;
+
+ params->lfb_base = PTR_TO_UINT32 (framebuffer);
+ params->lfb_mode = bitmap ? GRUB_XNU_VIDEO_SPLASH
+ : GRUB_XNU_VIDEO_TEXT_IN_VIDEO;
+
+ return GRUB_ERR_NONE;
+}
+
+/* Boot xnu. */
+grub_err_t
+grub_xnu_boot (void)
+{
+ struct grub_xnu_boot_params *bootparams;
+ void *bp_in;
+ grub_addr_t bootparams_target;
+ grub_err_t err;
+ grub_efi_uintn_t memory_map_size = 0;
+ void *memory_map;
+ grub_addr_t memory_map_target;
+ grub_efi_uintn_t map_key = 0;
+ grub_efi_uintn_t descriptor_size = 0;
+ grub_efi_uint32_t descriptor_version = 0;
+ grub_uint64_t firstruntimepage, lastruntimepage;
+ grub_uint64_t curruntimepage;
+ grub_addr_t devtree_target;
+ grub_size_t devtreelen;
+ int i;
+ struct grub_relocator32_state state;
+
+ err = grub_autoefi_prepare ();
+ if (err)
+ return err;
+
+ err = grub_cpu_xnu_fill_devprop ();
+ if (err)
+ return err;
+
+ err = grub_cpu_xnu_fill_devicetree ();
+ if (err)
+ return err;
+
+ err = grub_xnu_fill_devicetree ();
+ if (err)
+ return err;
+
+ /* Page-align to avoid following parts to be inadvertently freed. */
+ err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
+ if (err)
+ return err;
+
+ /* Pass memory map to kernel. */
+ memory_map_size = 0;
+ memory_map = 0;
+ map_key = 0;
+ descriptor_size = 0;
+ descriptor_version = 0;
+
+ grub_dprintf ("xnu", "eip=%x\n", grub_xnu_entry_point);
+
+ const char *debug = grub_env_get ("debug");
+
+ if (debug && (grub_strword (debug, "all") || grub_strword (debug, "xnu")))
+ {
+ grub_printf ("Press any key to launch xnu\n");
+ grub_getkey ();
+ }
+
+ /* Relocate the boot parameters to heap. */
+ err = grub_xnu_heap_malloc (sizeof (*bootparams),
+ &bp_in, &bootparams_target);
+ if (err)
+ return err;
+ bootparams = bp_in;
+
+ /* Set video. */
+ err = grub_xnu_set_video (bootparams);
+ if (err != GRUB_ERR_NONE)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+ grub_printf ("Booting in blind mode\n");
+
+ bootparams->lfb_mode = 0;
+ bootparams->lfb_width = 0;
+ bootparams->lfb_height = 0;
+ bootparams->lfb_depth = 0;
+ bootparams->lfb_line_len = 0;
+ bootparams->lfb_base = 0;
+ }
+
+ if (grub_autoefi_get_memory_map (&memory_map_size, memory_map,
+ &map_key, &descriptor_size,
+ &descriptor_version) < 0)
+ return grub_errno;
+
+ /* We will do few allocations later. Reserve some space for possible
+ memory map growth. */
+ memory_map_size += 20 * descriptor_size;
+ err = grub_xnu_heap_malloc (memory_map_size,
+ &memory_map, &memory_map_target);
+ if (err)
+ return err;
+
+ err = grub_xnu_writetree_toheap (&devtree_target, &devtreelen);
+ if (err)
+ return err;
+
+ grub_memcpy (bootparams->cmdline, grub_xnu_cmdline,
+ sizeof (bootparams->cmdline));
+
+ bootparams->devtree = devtree_target;
+ bootparams->devtreelen = devtreelen;
+
+ err = grub_autoefi_finish_boot_services (&memory_map_size, memory_map,
+ &map_key, &descriptor_size,
+ &descriptor_version);
+ if (err)
+ return err;
+
+ bootparams->efi_system_table = PTR_TO_UINT32 (grub_autoefi_system_table);
+
+ firstruntimepage = (((grub_addr_t) grub_xnu_heap_target_start
+ + grub_xnu_heap_size + GRUB_XNU_PAGESIZE - 1)
+ / GRUB_XNU_PAGESIZE) + 20;
+ curruntimepage = firstruntimepage;
+
+ for (i = 0; (unsigned) i < memory_map_size / descriptor_size; i++)
+ {
+ grub_efi_memory_descriptor_t *curdesc = (grub_efi_memory_descriptor_t *)
+ ((char *) memory_map + descriptor_size * i);
+
+ curdesc->virtual_start = curdesc->physical_start;
+
+ if (curdesc->type == GRUB_EFI_RUNTIME_SERVICES_DATA
+ || curdesc->type == GRUB_EFI_RUNTIME_SERVICES_CODE)
+ {
+ curdesc->virtual_start = curruntimepage << 12;
+ curruntimepage += curdesc->num_pages;
+ if (curdesc->physical_start
+ <= PTR_TO_UINT64 (grub_autoefi_system_table)
+ && curdesc->physical_start + (curdesc->num_pages << 12)
+ > PTR_TO_UINT64 (grub_autoefi_system_table))
+ bootparams->efi_system_table
+ = PTR_TO_UINT64 (grub_autoefi_system_table)
+ - curdesc->physical_start + curdesc->virtual_start;
+ if (SIZEOF_OF_UINTN == 8 && grub_xnu_is_64bit)
+ curdesc->virtual_start |= 0xffffff8000000000ULL;
+ }
+ }
+
+ lastruntimepage = curruntimepage;
+
+ bootparams->efi_mmap = memory_map_target;
+ bootparams->efi_mmap_size = memory_map_size;
+ bootparams->efi_mem_desc_size = descriptor_size;
+ bootparams->efi_mem_desc_version = descriptor_version;
+
+ bootparams->heap_start = grub_xnu_heap_target_start;
+ bootparams->heap_size = grub_xnu_heap_size;
+ bootparams->efi_runtime_first_page = firstruntimepage;
+
+ bootparams->efi_runtime_npages = lastruntimepage - firstruntimepage;
+ bootparams->efi_uintnbits = SIZEOF_OF_UINTN * 8;
+
+ bootparams->verminor = GRUB_XNU_BOOTARGS_VERMINOR;
+ bootparams->vermajor = GRUB_XNU_BOOTARGS_VERMAJOR;
+
+ /* Parameters for asm helper. */
+ grub_xnu_stack = bootparams->heap_start
+ + bootparams->heap_size + GRUB_XNU_PAGESIZE;
+ grub_xnu_arg1 = bootparams_target;
+
+ grub_autoefi_set_virtual_address_map (memory_map_size, descriptor_size,
+ descriptor_version, memory_map);
+
+ state.eip = grub_xnu_entry_point;
+ state.eax = grub_xnu_arg1;
+ state.esp = grub_xnu_stack;
+ state.ebp = grub_xnu_stack;
+ return grub_relocator32_boot (grub_xnu_relocator, state);
+}
+
+static grub_command_t cmd_devprop_load;
+
+void
+grub_cpu_xnu_init (void)
+{
+ cmd_devprop_load = grub_register_command ("xnu_devprop_load",
+ grub_cmd_devprop_load,
+ 0, N_("Load device-properties dump."));
+}
+
+void
+grub_cpu_xnu_fini (void)
+{
+ grub_unregister_command (cmd_devprop_load);
+}
diff --git a/grub-core/loader/macho.c b/grub-core/loader/macho.c
new file mode 100644
index 0000000..ecf0d8c
--- /dev/null
+++ b/grub-core/loader/macho.c
@@ -0,0 +1,160 @@
+/* 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/cpu/macho.h>
+#include <grub/machoload.h>
+#include <grub/file.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+grub_err_t
+grub_macho_close (grub_macho_t macho)
+{
+ grub_file_t file = macho->file;
+
+ grub_free (macho->cmds32);
+ grub_free (macho->cmds64);
+
+ grub_free (macho);
+
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+grub_macho_t
+grub_macho_file (grub_file_t file)
+{
+ 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;
+
+ if (grub_file_seek (macho->file, 0) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (macho->file, &filestart, sizeof (filestart))
+ != sizeof (filestart))
+ {
+ grub_error_push ();
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read Mach-O header");
+ goto fail;
+ }
+
+ /* Is it a fat file? */
+ if (filestart.fat.magic == grub_be_to_cpu32 (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_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+ 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);
+ grub_error_push ();
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read Mach-O header");
+ goto fail;
+ }
+
+ for (i = 0; i < narchs; i++)
+ {
+ if (GRUB_MACHO_CPUTYPE_IS_HOST32
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ 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 (GRUB_MACHO_CPUTYPE_IS_HOST64
+ (grub_be_to_cpu32 (archs[i].cputype)))
+ {
+ 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)
+ {
+ macho->offset32 = 0;
+ macho->end32 = grub_file_size (file);
+ }
+
+ /* Is it a thin 64-bit file? */
+ if (filestart.thin64.magic == GRUB_MACHO_MAGIC64)
+ {
+ macho->offset64 = 0;
+ macho->end64 = grub_file_size (file);
+ }
+
+ grub_macho_parse32 (macho);
+ grub_macho_parse64 (macho);
+
+ return macho;
+
+fail:
+ grub_macho_close (macho);
+ return 0;
+}
+
+grub_macho_t
+grub_macho_open (const char *name)
+{
+ grub_file_t file;
+ grub_macho_t macho;
+
+ file = grub_file_open (name);
+ if (! file)
+ return 0;
+
+ macho = grub_macho_file (file);
+ 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..0d740ed
--- /dev/null
+++ b/grub-core/loader/macho32.c
@@ -0,0 +1,18 @@
+#include <grub/cpu/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 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..17a8021
--- /dev/null
+++ b/grub-core/loader/macho64.c
@@ -0,0 +1,18 @@
+#include <grub/cpu/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 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..918ddbb
--- /dev/null
+++ b/grub-core/loader/machoXX.c
@@ -0,0 +1,239 @@
+
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+int
+SUFFIX (grub_macho_contains_macho) (grub_macho_t macho)
+{
+ return macho->offsetXX != -1;
+}
+
+void
+SUFFIX (grub_macho_parse) (grub_macho_t macho)
+{
+ grub_macho_header_t 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))
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read Mach-O header");
+ macho->offsetXX = -1;
+ return;
+ }
+ if (head.magic != GRUB_MACHO_MAGIC)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid Mach-O " XX "-bit header");
+ macho->offsetXX = -1;
+ return;
+ }
+
+ /* Read commands. */
+ macho->ncmdsXX = head.ncmds;
+ macho->cmdsizeXX = head.sizeofcmds;
+ macho->cmdsXX = grub_malloc(macho->cmdsizeXX);
+ if (! macho->cmdsXX)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "not enough memory to read commands");
+ return;
+ }
+ if (grub_file_read (macho->file, macho->cmdsXX,
+ (grub_size_t) macho->cmdsizeXX)
+ != (grub_ssize_t) macho->cmdsizeXX)
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read Mach-O header");
+ macho->offsetXX = -1;
+ }
+}
+
+typedef int NESTED_FUNC_ATTR (*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)
+{
+ grub_uint8_t *hdrs = macho->cmdsXX;
+ int i;
+ if (! macho->cmdsXX)
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't find " XX "-bit Mach-O");
+ 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, 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)
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "invalid offset in program header");
+ }
+
+ read = grub_file_read (macho->file, dest,
+ macho->endXX - macho->offsetXX);
+ if (read != (grub_ssize_t) (macho->endXX - macho->offsetXX))
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "couldn't read architecture-specific part");
+ }
+ return GRUB_ERR_NONE;
+}
+
+/* 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)
+{
+ int nr_phdrs = 0;
+
+ /* Run through the program headers to calculate the total memory size we
+ should claim. */
+ auto int NESTED_FUNC_ATTR calcsize (grub_macho_t _macho,
+ struct grub_macho_cmd *phdr, void *_arg);
+ int NESTED_FUNC_ATTR calcsize (grub_macho_t _macho __attribute__ ((unused)),
+ struct grub_macho_cmd *hdr0,
+ void *_arg __attribute__ ((unused)))
+ {
+ grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
+ return 0;
+
+ if (! hdr->vmsize)
+ return 0;
+
+ if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+ return 0;
+
+ nr_phdrs++;
+ if (hdr->vmaddr < *segments_start)
+ *segments_start = hdr->vmaddr;
+ if (hdr->vmaddr + hdr->vmsize > *segments_end)
+ *segments_end = hdr->vmaddr + hdr->vmsize;
+ return 0;
+ }
+
+ *segments_start = (grub_macho_addr_t) -1;
+ *segments_end = 0;
+
+ grub_macho_cmds_iterate (macho, calcsize, 0);
+
+ if (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;
+}
+
+/* Load every loadable segment into memory specified by `_load_hook'. */
+grub_err_t
+SUFFIX (grub_macho_load) (grub_macho_t macho, char *offset, int flags)
+{
+ grub_err_t err = 0;
+ auto int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr0,
+ void *_arg __attribute__ ((unused)));
+ int NESTED_FUNC_ATTR do_load(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr0,
+ void *_arg __attribute__ ((unused)))
+ {
+ grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
+
+ if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
+ return 0;
+
+ if (! hdr->filesize && (flags & GRUB_MACHO_NOBSS))
+ return 0;
+ if (! hdr->vmsize)
+ return 0;
+
+ if (grub_file_seek (_macho->file, hdr->fileoff
+ + _macho->offsetXX) == (grub_off_t) -1)
+ {
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS,
+ "invalid offset in program header");
+ return 1;
+ }
+
+ if (hdr->filesize)
+ {
+ grub_ssize_t read;
+ read = grub_file_read (_macho->file, offset + hdr->vmaddr,
+ min (hdr->filesize, hdr->vmsize));
+ if (read != (grub_ssize_t) min (hdr->filesize, hdr->vmsize))
+ {
+ /* XXX How can we free memory from `load_hook'? */
+ grub_error_push ();
+ err=grub_error (GRUB_ERR_BAD_OS,
+ "couldn't read segment from file: "
+ "wanted 0x%lx bytes; read 0x%lx bytes",
+ hdr->filesize, read);
+ return 1;
+ }
+ }
+
+ if (hdr->filesize < hdr->vmsize)
+ grub_memset (offset + hdr->vmaddr + hdr->filesize,
+ 0, hdr->vmsize - hdr->filesize);
+ return 0;
+ }
+
+ grub_macho_cmds_iterate (macho, do_load, 0);
+
+ return err;
+}
+
+grub_macho_addr_t
+SUFFIX (grub_macho_get_entry_point) (grub_macho_t macho)
+{
+ grub_macho_addr_t entry_point = 0;
+ auto int NESTED_FUNC_ATTR hook(grub_macho_t _macho,
+ struct grub_macho_cmd *hdr,
+ void *_arg __attribute__ ((unused)));
+ int NESTED_FUNC_ATTR hook(grub_macho_t _macho __attribute__ ((unused)),
+ struct grub_macho_cmd *hdr,
+ void *_arg __attribute__ ((unused)))
+ {
+ if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
+ entry_point = ((grub_macho_thread_t *) hdr)->entry_point;
+ return 0;
+ }
+ grub_macho_cmds_iterate (macho, hook, 0);
+ 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..0bf7b1f
--- /dev/null
+++ b/grub-core/loader/mips/linux.c
@@ -0,0 +1,443 @@
+/* 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>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* For frequencies. */
+#include <grub/pci.h>
+#include <grub/machine/time.h>
+
+#ifdef GRUB_MACHINE_MIPS_YEELOONG
+/* This can be detected on runtime from PMON, but:
+ a) it wouldn't work when GRUB is the firmware
+ and
+ b) for now we only support Yeeloong anyway. */
+#define LOONGSON_MACHTYPE "machtype=lemote-yeeloong-2f-8.9inches"
+#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;
+static int linux_argc;
+static grub_off_t argv_off, envp_off;
+static grub_off_t rd_addr_arg_off, rd_size_arg_off;
+static int initrd_loaded = 0;
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ struct grub_relocator32_state state;
+
+ /* Boot the kernel. */
+ state.gpr[1] = entry_addr;
+ state.gpr[4] = linux_argc;
+ state.gpr[5] = target_addr + argv_off;
+ state.gpr[6] = target_addr + envp_off;
+ 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);
+
+ loaded = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_load32 (grub_elf_t elf, 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. */
+ auto grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load);
+ grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load)
+ {
+ if (phdr->p_type != PT_LOAD)
+ {
+ *do_load = 0;
+ return 0;
+ }
+ *do_load = 1;
+
+ /* Linux's program headers incorrectly contain virtual addresses.
+ * Translate those to physical, and offset to the area we claimed. */
+ *addr = (grub_addr_t) (phdr->p_paddr - base + playground);
+ return 0;
+ }
+ return grub_elf32_load (elf, offset_phdr, 0, 0);
+}
+
+static grub_err_t
+grub_linux_load64 (grub_elf_t elf, 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. */
+ auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load);
+ grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load)
+ {
+ if (phdr->p_type != PT_LOAD)
+ {
+ *do_load = 0;
+ return 0;
+ }
+ *do_load = 1;
+ /* Linux's program headers incorrectly contain virtual addresses.
+ * Translate those to physical, and offset to the area we claimed. */
+ *addr = (grub_addr_t) (phdr->p_paddr - base + playground);
+ return 0;
+ }
+ return grub_elf64_load (elf, offset_phdr, 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 i;
+ int size;
+ void *extra = NULL;
+ grub_uint32_t *linux_argv, *linux_envp;
+ char *linux_args, *linux_envs;
+ grub_err_t err;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+
+ elf = grub_elf_open (argv[0]);
+ if (! elf)
+ return grub_errno;
+
+ if (elf->ehdr.ehdr32.e_type != ET_EXEC)
+ {
+ grub_elf_close (elf);
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ "this ELF file is not of the right type\n");
+ }
+
+ /* Release the previously used memory. */
+ grub_loader_unset ();
+ loaded = 0;
+
+ /* For arguments. */
+ linux_argc = argc;
+#ifdef LOONGSON_MACHTYPE
+ 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 LOONGSON_MACHTYPE
+ size += ALIGN_UP (sizeof (LOONGSON_MACHTYPE), 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);
+
+ if (grub_elf_is_elf32 (elf))
+ err = grub_linux_load32 (elf, &extra, size);
+ else
+ if (grub_elf_is_elf64 (elf))
+ err = grub_linux_load64 (elf, &extra, size);
+ else
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "unknown ELF class");
+
+ grub_elf_close (elf);
+
+ if (err)
+ return err;
+
+ 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);
+
+#ifdef LOONGSON_MACHTYPE
+ /* 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_MACHTYPE, sizeof (LOONGSON_MACHTYPE));
+ *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
+ + target_addr;
+ linux_argv++;
+ linux_args += ALIGN_UP (sizeof (LOONGSON_MACHTYPE), 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);
+ }
+
+ /* 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;
+
+ 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;
+
+ 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_file_t file = 0;
+ grub_ssize_t size;
+ void *initrd_src;
+ grub_addr_t initrd_dest;
+ grub_err_t err;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no initrd specified");
+
+ if (!loaded)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load Linux first.");
+
+ if (initrd_loaded)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one initrd can be loaded.");
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (argv[0]);
+ if (! file)
+ return grub_errno;
+
+ size = grub_file_size (file);
+
+ {
+ grub_relocator_chunk_t ch;
+
+ err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ (target_addr & 0x1fffffff)
+ + linux_size + 0x10000,
+ (0x10000000 - size),
+ size, 0x10000,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+ initrd_src = get_virtual_current_address (ch);
+ initrd_dest = get_physical_target_address (ch) | 0x80000000;
+ }
+
+ if (grub_file_read (file, initrd_src, size) != size)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ grub_file_close (file);
+
+ return grub_errno;
+ }
+
+ 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++;
+
+ initrd_loaded = 1;
+
+ grub_file_close (file);
+
+ return GRUB_ERR_NONE;
+}
+
+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..d9e74b3
--- /dev/null
+++ b/grub-core/loader/multiboot.c
@@ -0,0 +1,358 @@
+/* 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>
+#include <grub/multiboot.h>
+#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;
+
+
+/* Return the length of the Multiboot mmap that will be needed to allocate
+ our platform's map. */
+grub_uint32_t
+grub_get_multiboot_mmap_count (void)
+{
+ grub_size_t count = 0;
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr __attribute__ ((unused)),
+ grub_uint64_t size __attribute__ ((unused)),
+ grub_memory_type_t type __attribute__ ((unused)))
+ {
+ count++;
+ return 0;
+ }
+
+ grub_mmap_iterate (hook);
+
+ return count;
+}
+
+grub_err_t
+grub_multiboot_set_video_mode (void)
+{
+ grub_err_t err;
+ const char *modevar;
+
+ if (accepts_video || !GRUB_MACHINE_HAS_VGA_TEXT)
+ {
+ 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);
+ }
+ }
+ else
+ err = grub_video_set_mode ("text", 0, 0);
+
+ return err;
+}
+
+static grub_err_t
+grub_multiboot_boot (void)
+{
+ grub_err_t err;
+ struct grub_relocator32_state state = MULTIBOOT_INITIAL_STATE;
+
+ state.MULTIBOOT_ENTRY_REGISTER = grub_multiboot_payload_eip;
+
+ err = grub_multiboot_make_mbi (&state.MULTIBOOT_MBI_REGISTER);
+
+ if (err)
+ return err;
+
+#ifdef GRUB_MACHINE_EFI
+ err = grub_efi_finish_boot_services (NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ return err;
+#endif
+
+ grub_relocator32_boot (grub_multiboot_relocator, state);
+
+ /* 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;
+}
+
+#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 (grub_file_t file, void *buffer)
+{
+ if (grub_multiboot_is_elf32 (buffer))
+ return grub_multiboot_load_elf32 (file, buffer);
+ else if (grub_multiboot_is_elf64 (buffer))
+ return grub_multiboot_load_elf64 (file, buffer);
+
+ return grub_error (GRUB_ERR_UNKNOWN_OS, "unknown ELF class");
+}
+
+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_printf ("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
+ grub_env_set ("gfxpayload", "text");
+
+ 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 ();
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "couldn't open file");
+
+ 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);
+ 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;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
+
+ if (grub_strcmp (argv[0], "--nounzip") == 0)
+ {
+ argv++;
+ argc--;
+ nounzip = 1;
+ }
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
+
+ if (!grub_multiboot_relocator)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "you need to load the multiboot kernel first");
+
+ if (nounzip)
+ grub_file_filter_disable_compression ();
+
+ file = grub_file_open (argv[0]);
+ if (! file)
+ return grub_errno;
+
+ size = grub_file_size (file);
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
+ 0, (0xffffffff - size) + 1,
+ size, MULTIBOOT_MOD_ALIGN,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+ module = get_virtual_current_address (ch);
+ target = (grub_addr_t) get_virtual_current_address (ch);
+ }
+
+ err = grub_multiboot_add_module (target, size, argc - 1, argv + 1);
+ if (err)
+ {
+ grub_file_close (file);
+ return err;
+ }
+
+ if (grub_file_read (file, module, size) != size)
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ }
+
+ 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..0c29fe9
--- /dev/null
+++ b/grub-core/loader/multiboot_elfxx.c
@@ -0,0 +1,225 @@
+/*
+ * 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
+#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
+#else
+#error "I'm confused"
+#endif
+
+#include <grub/i386/relocator.h>
+
+#define CONCAT(a,b) CONCAT_(a, b)
+#define CONCAT_(a,b) a ## b
+
+/* 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) (grub_file_t file, void *buffer)
+{
+ Elf_Ehdr *ehdr = (Elf_Ehdr *) buffer;
+ char *phdr_base;
+ int i;
+
+ if (ehdr->e_ident[EI_CLASS] != ELFCLASSXX)
+ return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF class");
+
+ 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_version != EV_CURRENT
+ || ehdr->e_ident[EI_DATA] != ELFDATA2LSB
+ || ehdr->e_machine != E_MACHINE)
+ return grub_error(GRUB_ERR_UNKNOWN_OS, "no valid ELF header found");
+
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ return grub_error (GRUB_ERR_UNKNOWN_OS, "invalid ELF file type");
+
+ /* FIXME: Should we support program headers at strange locations? */
+ if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
+ return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
+
+#if defined (MULTIBOOT_LOAD_ELF64) && defined (__mips)
+ /* We still in 32-bit mode. */
+ if (ehdr->e_entry < 0xffffffff80000000ULL)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
+#else
+ /* We still in 32-bit mode. */
+ if (ehdr->e_entry > 0xffffffff)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
+#endif
+
+ phdr_base = (char *) buffer + ehdr->e_phoff;
+#define phdr(i) ((Elf_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))
+
+ /* Load every loadable segment in memory. */
+ for (i = 0; i < ehdr->e_phnum; i++)
+ {
+ if (phdr(i)->p_type == PT_LOAD)
+ {
+ grub_err_t err;
+ void *source;
+
+ 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);
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator,
+ &ch, phdr(i)->p_paddr,
+ phdr(i)->p_memsz);
+ if (err)
+ {
+ grub_dprintf ("multiboot_loader", "Error loading phdr %d\n", i);
+ return err;
+ }
+ source = get_virtual_current_address (ch);
+ }
+
+ if (phdr(i)->p_filesz != 0)
+ {
+ if (grub_file_seek (file, (grub_off_t) phdr(i)->p_offset)
+ == (grub_off_t) -1)
+ return grub_error (GRUB_ERR_BAD_OS,
+ "invalid offset in program header");
+
+ if (grub_file_read (file, source, phdr(i)->p_filesz)
+ != (grub_ssize_t) phdr(i)->p_filesz)
+ return grub_error (GRUB_ERR_BAD_OS,
+ "couldn't read segment from file");
+ }
+
+ if (phdr(i)->p_filesz < phdr(i)->p_memsz)
+ grub_memset ((grub_uint8_t *) source + 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;
+ 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_malloc (ehdr->e_shnum * ehdr->e_shentsize);
+ if (!shdr)
+ return grub_errno;
+
+ if (grub_file_seek (file, ehdr->e_shoff) == (grub_off_t) -1)
+ return grub_error (GRUB_ERR_BAD_OS,
+ "invalid offset to section headers");
+
+ if (grub_file_read (file, shdr, ehdr->e_shnum * ehdr->e_shentsize)
+ != (grub_ssize_t) ehdr->e_shnum * ehdr->e_shentsize)
+ return grub_error (GRUB_ERR_BAD_OS,
+ "couldn't read sections headers from file");
+
+ 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;
+ grub_err_t err;
+
+ /* 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;
+
+ {
+ grub_relocator_chunk_t ch;
+ err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator,
+ &ch, 0,
+ (0xffffffff - sh->sh_size)
+ + 1, sh->sh_size,
+ sh->sh_addralign,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ 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 (file, sh->sh_offset) == (grub_off_t) -1)
+ return grub_error (GRUB_ERR_BAD_OS,
+ "invalid offset in section header");
+
+ if (grub_file_read (file, src, sh->sh_size)
+ != (grub_ssize_t) sh->sh_size)
+ return grub_error (GRUB_ERR_BAD_OS,
+ "couldn't read segment from file");
+ 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
diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c
new file mode 100644
index 0000000..05f581b
--- /dev/null
+++ b/grub-core/loader/multiboot_mbi2.c
@@ -0,0 +1,911 @@
+/*
+ * 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/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>
+
+#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
+
+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;
+
+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;
+}
+
+grub_err_t
+grub_multiboot_load (grub_file_t file)
+{
+ char *buffer;
+ 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;
+ int entry_specified = 0;
+ grub_addr_t entry = 0;
+ grub_uint32_t console_required = 0;
+ struct multiboot_header_tag_framebuffer *fbtag = NULL;
+ int accepted_consoles = GRUB_MULTIBOOT_CONSOLE_EGA_TEXT;
+
+ buffer = grub_malloc (MULTIBOOT_SEARCH);
+ if (!buffer)
+ return grub_errno;
+
+ len = grub_file_read (file, buffer, MULTIBOOT_SEARCH);
+ if (len < 32)
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_BAD_OS, "file too small");
+ }
+
+ /* Look for the multiboot header in the buffer. The header should
+ be at least 12 bytes and aligned on a 4-byte boundary. */
+ for (header = (struct multiboot_header *) buffer;
+ ((char *) header <= buffer + len - 12) || (header = 0);
+ header = (struct multiboot_header *) ((char *) header + MULTIBOOT_HEADER_ALIGN))
+ {
+ if (header->magic == MULTIBOOT_HEADER_MAGIC
+ && !(header->magic + header->architecture
+ + header->header_length + header->checksum)
+ && header->architecture == MULTIBOOT_ARCHITECTURE_CURRENT)
+ break;
+ }
+
+ if (header == 0)
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no multiboot header found");
+ }
+
+ for (tag = (struct multiboot_header_tag *) (header + 1);
+ tag->type != MULTIBOOT_TAG_TYPE_END;
+ tag = (struct multiboot_header_tag *) ((char *) tag + tag->size))
+ 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:
+ break;
+
+ default:
+ grub_free (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_CONSOLE_FLAGS:
+ if (!(((struct multiboot_header_tag_console_flags *) tag)->console_flags
+ & MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED))
+ accepted_consoles &= ~GRUB_MULTIBOOT_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_MULTIBOOT_CONSOLE_FRAMEBUFFER;
+ break;
+
+ /* GRUB always page-aligns modules. */
+ case MULTIBOOT_HEADER_TAG_MODULE_ALIGN:
+ break;
+
+ default:
+ if (! (tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL))
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ "unsupported tag: 0x%x", tag->type);
+ }
+ break;
+ }
+
+ if (addr_tag && !entry_specified)
+ {
+ grub_free (buffer);
+ return grub_error (GRUB_ERR_UNKNOWN_OS,
+ "load address tag without entry address tag");
+ }
+
+ if (addr_tag)
+ {
+ int offset = ((char *) header - buffer -
+ (addr_tag->header_addr - addr_tag->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 - addr_tag->load_addr);
+ else
+ code_size = load_size;
+
+ err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator,
+ &ch, addr_tag->load_addr,
+ code_size);
+ if (err)
+ {
+ grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
+ grub_free (buffer);
+ return err;
+ }
+ source = get_virtual_current_address (ch);
+
+ if ((grub_file_seek (file, offset)) == (grub_off_t) -1)
+ {
+ grub_free (buffer);
+ return grub_errno;
+ }
+
+ grub_file_read (file, source, load_size);
+ if (grub_errno)
+ {
+ grub_free (buffer);
+ return grub_errno;
+ }
+
+ if (addr_tag->bss_end_addr)
+ grub_memset ((grub_uint32_t *) source + load_size, 0,
+ addr_tag->bss_end_addr - addr_tag->load_addr - load_size);
+ }
+ else
+ {
+ err = grub_multiboot_load_elf (file, buffer);
+ if (err)
+ {
+ grub_free (buffer);
+ return err;
+ }
+ }
+
+ if (entry_specified)
+ grub_multiboot_payload_eip = entry;
+
+ if (fbtag)
+ err = grub_multiboot_set_console (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER,
+ accepted_consoles,
+ fbtag->width, fbtag->height,
+ fbtag->depth, console_required);
+ else
+ err = grub_multiboot_set_console (GRUB_MULTIBOOT_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
+}
+
+static grub_size_t
+grub_multiboot_get_mbi_size (void)
+{
+ return 2 * sizeof (grub_uint32_t) + 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_get_multiboot_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_efi32), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_efi64), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_old_acpi)
+ + sizeof (struct grub_acpi_rsdp_v10), MULTIBOOT_TAG_ALIGN)
+ + acpiv2_size ()
+ + sizeof (struct multiboot_tag_vbe) + MULTIBOOT_TAG_ALIGN - 1
+ + sizeof (struct multiboot_tag_apm) + MULTIBOOT_TAG_ALIGN - 1;
+}
+
+/* Fill previously allocated Multiboot mmap. */
+static void
+grub_fill_multiboot_mmap (struct multiboot_tag_mmap *tag)
+{
+ struct multiboot_mmap_entry *mmap_entry = tag->entries;
+
+ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+ mmap_entry->addr = addr;
+ mmap_entry->len = size;
+ switch (type)
+ {
+ case GRUB_MEMORY_AVAILABLE:
+ mmap_entry->type = MULTIBOOT_MEMORY_AVAILABLE;
+ break;
+
+ case GRUB_MEMORY_ACPI:
+ mmap_entry->type = MULTIBOOT_MEMORY_ACPI_RECLAIMABLE;
+ break;
+
+ case GRUB_MEMORY_NVS:
+ mmap_entry->type = MULTIBOOT_MEMORY_NVS;
+ break;
+
+ case GRUB_MEMORY_BADRAM:
+ mmap_entry->type = MULTIBOOT_MEMORY_BADRAM;
+ break;
+
+ default:
+ mmap_entry->type = MULTIBOOT_MEMORY_RESERVED;
+ break;
+ }
+ mmap_entry++;
+
+ return 0;
+ }
+
+ tag->type = MULTIBOOT_TAG_TYPE_MMAP;
+ tag->size = sizeof (struct multiboot_tag_mmap)
+ + sizeof (struct multiboot_mmap_entry) * grub_get_multiboot_mmap_count ();
+ tag->entry_size = sizeof (struct multiboot_mmap_entry);
+ tag->entry_version = 0;
+
+ grub_mmap_iterate (hook);
+}
+
+#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_uint8_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_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 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);
+ }
+ 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);
+ }
+#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);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_multiboot_make_mbi (grub_uint32_t *target)
+{
+ grub_uint8_t *ptrorig;
+ grub_uint8_t *mbistart;
+ grub_err_t err;
+ grub_size_t bufsize;
+ grub_relocator_chunk_t ch;
+
+ bufsize = grub_multiboot_get_mbi_size ();
+
+ err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
+ 0, 0xffffffff - bufsize,
+ bufsize, MULTIBOOT_TAG_ALIGN,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ 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;
+ ptrorig += 2 * sizeof (grub_uint32_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);
+ }
+
+ {
+ 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);
+ }
+
+#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);
+ }
+ }
+#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);
+ }
+ }
+
+ {
+ struct multiboot_tag_mmap *tag = (struct multiboot_tag_mmap *) ptrorig;
+ grub_fill_multiboot_mmap (tag);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN);
+ }
+
+ {
+ 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);
+ }
+
+ {
+ 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);
+ }
+
+ 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);
+ }
+
+ {
+ 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);
+ }
+#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);
+ }
+#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);
+ }
+ }
+
+ {
+ 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);
+ }
+ }
+#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);
+ }
+
+ ((grub_uint32_t *) mbistart)[0] = ptrorig - mbistart;
+ ((grub_uint32_t *) mbistart)[1] = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+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_err_t
+grub_multiboot_init_mbi (int argc, char *argv[])
+{
+ grub_ssize_t len = 0;
+ char *p;
+ int i;
+
+ grub_multiboot_free_mbi ();
+
+ for (i = 0; i < argc; i++)
+ len += grub_strlen (argv[i]) + 1;
+ if (len == 0)
+ len = 1;
+
+ cmdline = p = grub_malloc (len);
+ if (! cmdline)
+ return grub_errno;
+ cmdline_size = len;
+
+ for (i = 0; i < argc; i++)
+ {
+ p = grub_stpcpy (p, argv[i]);
+ *(p++) = ' ';
+ }
+
+ /* Remove the space after the last word. */
+ if (p != cmdline)
+ p--;
+ *p = '\0';
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_multiboot_add_module (grub_addr_t start, grub_size_t size,
+ int argc, char *argv[])
+{
+ struct module *newmod;
+ char *p;
+ grub_ssize_t len = 0;
+ int i;
+
+ newmod = grub_malloc (sizeof (*newmod));
+ if (!newmod)
+ return grub_errno;
+ newmod->start = start;
+ newmod->size = size;
+
+ for (i = 0; i < argc; i++)
+ len += grub_strlen (argv[i]) + 1;
+
+ if (len == 0)
+ len = 1;
+
+ newmod->cmdline = p = grub_malloc (len);
+ if (! newmod->cmdline)
+ {
+ grub_free (newmod);
+ return grub_errno;
+ }
+ newmod->cmdline_size = len;
+ total_modcmd += ALIGN_UP (len, MULTIBOOT_TAG_ALIGN);
+
+ for (i = 0; i < argc; i++)
+ {
+ p = grub_stpcpy (p, argv[i]);
+ *(p++) = ' ';
+ }
+
+ /* Remove the space after the last word. */
+ if (p != newmod->cmdline)
+ p--;
+ *p = '\0';
+
+ if (modules_last)
+ modules_last->next = newmod;
+ else
+ {
+ modules = newmod;
+ modules_last->next = NULL;
+ }
+ modules_last = newmod;
+
+ modcnt++;
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_multiboot_set_bootdev (void)
+{
+ grub_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..12a3fa9
--- /dev/null
+++ b/grub-core/loader/powerpc/ieee1275/linux.c
@@ -0,0 +1,385 @@
+/* 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>
+
+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_size_t linux_size;
+
+static char *linux_args;
+
+typedef void (*kernel_entry_t) (void *, unsigned long, int (void *),
+ unsigned long, unsigned long);
+
+static grub_addr_t
+grub_linux_claimmap_iterate (grub_addr_t target, grub_size_t size,
+ grub_size_t align)
+{
+ grub_addr_t found_addr = (grub_addr_t) -1;
+
+ auto int NESTED_FUNC_ATTR alloc_mem (grub_uint64_t addr, grub_uint64_t len,
+ grub_memory_type_t type);
+ int NESTED_FUNC_ATTR alloc_mem (grub_uint64_t addr, grub_uint64_t len,
+ grub_memory_type_t type)
+ {
+ grub_uint64_t end = addr + len;
+ addr = ALIGN_UP (addr, align);
+ target = ALIGN_UP (target, align);
+
+ /* Target above the memory chunk. */
+ if (type != GRUB_MEMORY_AVAILABLE || target > end)
+ return 0;
+
+ /* Target inside the memory chunk. */
+ if (target >= addr && target < end && size <= end - target)
+ {
+ if (grub_claimmap (target, size) == GRUB_ERR_NONE)
+ {
+ found_addr = target;
+ return 1;
+ }
+ }
+ /* Target below the memory chunk. */
+ if (target < addr && addr + size <= end)
+ {
+ if (grub_claimmap (addr, size) == GRUB_ERR_NONE)
+ {
+ found_addr = addr;
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ grub_machine_mmap_iterate (alloc_mem);
+
+ return found_addr;
+}
+
+static grub_err_t
+grub_linux_boot (void)
+{
+ kernel_entry_t linuxmain;
+ grub_ssize_t actual;
+
+ /* 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_addr);
+ 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_addr;
+ 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)
+{
+ 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_addr = seg_addr + offset;
+
+ /* Now load the segments into the area we claimed. */
+ auto grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load);
+ grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load)
+ {
+ if (phdr->p_type != PT_LOAD)
+ {
+ *do_load = 0;
+ return 0;
+ }
+ *do_load = 1;
+
+ *addr = (phdr->p_paddr - base_addr) + seg_addr;
+ return 0;
+ }
+ return grub_elf32_load (elf, offset_phdr, 0, 0);
+}
+
+static grub_err_t
+grub_linux_load64 (grub_elf_t elf)
+{
+ 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_addr = seg_addr + offset;
+
+ /* Now load the segments into the area we claimed. */
+ auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load);
+ grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load)
+ {
+ if (phdr->p_type != PT_LOAD)
+ {
+ *do_load = 0;
+ return 0;
+ }
+ *do_load = 1;
+
+ *addr = (phdr->p_paddr - base_addr) + seg_addr;
+ return 0;
+ }
+ return grub_elf64_load (elf, offset_phdr, 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, "no kernel specified");
+ goto out;
+ }
+
+ elf = grub_elf_open (argv[0]);
+ 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,
+ "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);
+ else
+ if (grub_elf_is_elf64 (elf))
+ grub_linux_load64 (elf);
+ else
+ {
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, "unknown ELF class");
+ 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));
+ grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1,
+ size);
+
+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_file_t file = 0;
+ grub_ssize_t size;
+ grub_addr_t first_addr;
+ grub_addr_t addr;
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no initrd specified");
+ goto fail;
+ }
+
+ if (!loaded)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
+ goto fail;
+ }
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ first_addr = linux_addr + linux_size;
+ size = grub_file_size (file);
+
+ /* 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_file_read (file, (void *) addr, size) != size)
+ {
+ grub_ieee1275_release (addr, size);
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ initrd_addr = addr;
+ initrd_size = size;
+
+ fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_linux, cmd_initrd;
+
+GRUB_MOD_INIT(linux)
+{
+ cmd_linux = grub_register_command ("linux", grub_cmd_linux,
+ 0, N_("Load Linux."));
+ cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
+ 0, N_("Load initrd."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(linux)
+{
+ grub_unregister_command (cmd_linux);
+ grub_unregister_command (cmd_initrd);
+}
diff --git a/grub-core/loader/sparc64/ieee1275/linux.c b/grub-core/loader/sparc64/ieee1275/linux.c
new file mode 100644
index 0000000..8ed61f8
--- /dev/null
+++ b/grub-core/loader/sparc64/ieee1275/linux.c
@@ -0,0 +1,525 @@
+/* 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>
+
+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;
+ 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 ("sethi %hi(grub_ieee1275_entry_fn), %o1\n"
+ "ldx [%o1 + %lo(grub_ieee1275_entry_fn)], %o4\n"
+ "sethi %hi(grub_ieee1275_original_stack), %o1\n"
+ "ldx [%o1 + %lo(grub_ieee1275_original_stack)], %o6\n"
+ "sethi %hi(linux_addr), %o1\n"
+ "ldx [%o1 + %lo(linux_addr)], %o5\n"
+ "mov %g0, %o0\n"
+ "mov %g0, %o2\n"
+ "mov %g0, %o3\n"
+ "jmp %o5\n"
+ "mov %g0, %o1\n");
+
+ 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)
+
+static grub_addr_t
+alloc_phys (grub_addr_t size)
+{
+ grub_addr_t ret = (grub_addr_t) -1;
+
+ auto int NESTED_FUNC_ATTR choose (grub_uint64_t addr, grub_uint64_t len,
+ grub_memory_type_t type);
+ int NESTED_FUNC_ATTR choose (grub_uint64_t addr, grub_uint64_t len,
+ grub_memory_type_t type)
+ {
+ grub_addr_t end = addr + len;
+
+ if (type != 1)
+ return 0;
+
+ addr = ALIGN_UP (addr, FOUR_MB);
+ if (addr + size >= end)
+ return 0;
+
+ if (addr >= grub_phys_start && addr < grub_phys_end)
+ {
+ addr = ALIGN_UP (grub_phys_end, FOUR_MB);
+ if (addr + size >= end)
+ return 0;
+ }
+ if ((addr + size) >= grub_phys_start
+ && (addr + size) < grub_phys_end)
+ {
+ addr = ALIGN_UP (grub_phys_end, FOUR_MB);
+ if (addr + size >= end)
+ return 0;
+ }
+
+ 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 + size >= end)
+ return 0;
+ }
+ if ((addr + size) >= linux_paddr
+ && (addr + size) < linux_end)
+ {
+ addr = linux_end;
+ if (addr + size >= end)
+ return 0;
+ }
+ }
+
+ ret = addr;
+ return 1;
+ }
+
+ grub_machine_mmap_iterate (choose);
+
+ return ret;
+}
+
+static grub_err_t
+grub_linux_load64 (grub_elf_t elf)
+{
+ 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. */
+ auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load);
+ grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load)
+ {
+ if (phdr->p_type != PT_LOAD)
+ {
+ *do_load = 0;
+ return 0;
+ }
+ *do_load = 1;
+
+ /* Adjust the program load address to linux_addr. */
+ *addr = (phdr->p_paddr - base) + (linux_addr - off);
+ return 0;
+ }
+ return grub_elf64_load (elf, offset_phdr, 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, "no kernel specified");
+ goto out;
+ }
+
+ file = grub_file_open (argv[0]);
+ if (!file)
+ goto out;
+
+ elf = grub_elf_file (file);
+ if (! elf)
+ goto out;
+
+ if (elf->ehdr.ehdr32.e_type != ET_EXEC)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_OS,
+ "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);
+ else
+ {
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, "unknown ELF class");
+ 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));
+ grub_create_loader_cmdline (argc, argv, linux_args + sizeof (LINUX_IMAGE) - 1,
+ size);
+
+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_file_t file = 0;
+ grub_ssize_t size;
+ grub_addr_t paddr;
+ grub_addr_t addr;
+ int ret;
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "no initrd specified");
+ goto fail;
+ }
+
+ if (!loaded)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
+ goto fail;
+ }
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (argv[0]);
+ if (! file)
+ goto fail;
+
+ addr = 0x60000000;
+ size = grub_file_size (file);
+
+ 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_file_read (file, (void *) addr, size) != size)
+ {
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
+ goto fail;
+ }
+
+ initrd_addr = addr;
+ initrd_paddr = paddr;
+ initrd_size = size;
+
+ fail:
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+static void
+determine_phys_base (void)
+{
+ auto int NESTED_FUNC_ATTR get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__((unused)), grub_uint32_t type);
+ int NESTED_FUNC_ATTR get_physbase (grub_uint64_t addr, grub_uint64_t len __attribute__((unused)), grub_uint32_t type)
+ {
+ if (type != 1)
+ return 0;
+ if (addr < phys_base)
+ phys_base = addr;
+ return 0;
+ }
+
+ phys_base = ~(grub_uint64_t) 0;
+ grub_machine_mmap_iterate (get_physbase);
+}
+
+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..a98d60c
--- /dev/null
+++ b/grub-core/loader/xnu.c
@@ -0,0 +1,1498 @@
+/* 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>
+
+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;
+
+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 (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;
+
+ err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch,
+ grub_xnu_heap_target_start
+ + grub_xnu_heap_size, size);
+ if (err)
+ return err;
+
+ *src = get_virtual_current_address (ch);
+ *target = grub_xnu_heap_target_start + grub_xnu_heap_size;
+ 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, 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, 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)
+ if (!(curptr = grub_xnu_writetree_toheap_real (curptr,
+ cur->first_child,
+ cur->name)))
+ 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_malloc (sizeof (*driverkey));
+ if (! driverkey)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
+ driverkey->name = grub_strdup ("DeviceTree");
+ if (! driverkey->name)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
+ 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)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't write device tree");
+
+ /* 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)
+ return err;
+
+ /* 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;
+}
+
+/* Find a key or value in parent key. */
+struct grub_xnu_devtree_key *
+grub_xnu_find_key (struct grub_xnu_devtree_key *parent, 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, 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)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
+ return 0;
+ }
+ ret->name = grub_strdup (name);
+ if (! ret->name)
+ {
+ grub_free (ret);
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create key %s", name);
+ 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, 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)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
+ return 0;
+ }
+ ret->name = grub_strdup (name);
+ if (! ret->name)
+ {
+ grub_free (ret);
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't create value %s", name);
+ 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, "file name required");
+
+ grub_xnu_unload ();
+
+ macho = grub_macho_open (args[0]);
+ if (! macho)
+ return grub_errno;
+ if (! grub_macho_contains_macho32 (macho))
+ {
+ grub_macho_close (macho);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "kernel doesn't contain suitable 32-bit architecture");
+ }
+
+ err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
+ 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, (char *) loadaddr - startcode,
+ GRUB_MACHO_NOBSS);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ grub_xnu_entry_point = grub_macho_get_entry_point32 (macho);
+ 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;
+
+#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, "file name required");
+
+ grub_xnu_unload ();
+
+ macho = grub_macho_open (args[0]);
+ if (! macho)
+ return grub_errno;
+ if (! grub_macho_contains_macho64 (macho))
+ {
+ grub_macho_close (macho);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "kernel doesn't contain suitable 64-bit architecture");
+ }
+
+ err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS);
+ 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, (char *) loadaddr - startcode,
+ GRUB_MACHO_NOBSS);
+ if (err)
+ {
+ grub_macho_close (macho);
+ grub_xnu_unload ();
+ return err;
+ }
+
+ grub_xnu_entry_point = grub_macho_get_entry_point64 (macho) & 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;
+
+#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 (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, "no xnu kernel loaded");
+
+ 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_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
+ if (suffix)
+ {
+ driverkey->name = grub_xasprintf ("%s%d", prefix, (*suffix)++);
+ if (!driverkey->name)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register memory");
+ }
+ else
+ driverkey->name = grub_strdup (prefix);
+ if (! driverkey->name)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
+ 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)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "can't register extension");
+ 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)
+{
+ 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;
+
+ 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, "no xnu kernel loaded");
+
+ /* Compute the needed space. */
+ if (binaryfile)
+ {
+ macho = grub_macho_file (binaryfile);
+ if (! macho || ! grub_macho_contains_macho32 (macho))
+ {
+ if (macho)
+ grub_macho_close (macho);
+ return grub_error (GRUB_ERR_BAD_OS,
+ "extension doesn't contain suitable architecture");
+ }
+ if (grub_xnu_is_64bit)
+ machosize = grub_macho_filesize64 (macho);
+ else
+ machosize = grub_macho_filesize32 (macho);
+ neededspace += machosize;
+ }
+ else
+ macho = 0;
+
+ if (infoplistname)
+ infoplist = grub_file_open (infoplistname);
+ else
+ infoplist = 0;
+ 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)
+ return err;
+ err = grub_xnu_heap_malloc (neededspace, &buf0, &buf_target);
+ if (err)
+ return err;
+ 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, buf);
+ else
+ err = grub_macho_readfile32 (macho, buf);
+ if (err)
+ {
+ grub_macho_close (macho);
+ return err;
+ }
+ 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);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s: ",
+ infoplistname);
+ }
+ 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);
+}
+
+/* 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, "file name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ file = grub_file_open (args[0]);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "couldn't load driver package");
+
+ /* 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);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
+ }
+
+ /* 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_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+ if (! archs)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't read file %s", args[0]);
+
+ }
+ 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);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_READ_ERROR, "cannot read fat header");
+ }
+ 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);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
+ }
+ 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, "file name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ file = grub_file_open (args[0]);
+ if (! file)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ "couldn't load ramdisk");
+
+ 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);
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[0]);
+ }
+ 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, 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);
+ if (! file)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", plistname);
+ return 0;
+ }
+
+ size = grub_file_size (file);
+ buf = grub_malloc (size);
+ if (! buf)
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't read file %s", plistname);
+ return 0;
+ }
+ if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
+ {
+ grub_file_close (file);
+ grub_error_push ();
+ grub_error (GRUB_ERR_BAD_OS, "couldn't read 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;
+}
+
+/* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
+grub_err_t
+grub_xnu_scan_dir_for_kexts (char *dirname, char *osbundlerequired,
+ int maxrecursion)
+{
+ grub_device_t dev;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+
+ auto int load_hook (const char *filename,
+ const struct grub_dirhook_info *info);
+ int load_hook (const char *filename, const struct grub_dirhook_info *info)
+ {
+ 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 (dirname) + grub_strlen (filename) + 2);
+
+ /* It's a .kext. Try to load it. */
+ if (newdirname)
+ {
+ grub_strcpy (newdirname, 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, osbundlerequired,
+ maxrecursion);
+ if (grub_errno == GRUB_ERR_BAD_OS)
+ grub_errno = GRUB_ERR_NONE;
+ grub_free (newdirname);
+ }
+ return 0;
+ }
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ 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->dir) (dev, path, load_hook);
+ grub_device_close (dev);
+ }
+ grub_free (device_name);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load extension DIRNAME. (extensions are directories in xnu) */
+grub_err_t
+grub_xnu_load_kext_from_dir (char *dirname, char *osbundlerequired,
+ int maxrecursion)
+{
+ grub_device_t dev;
+ char *plistname = 0;
+ char *newdirname;
+ char *newpath;
+ char *device_name;
+ grub_fs_t fs;
+ const char *path;
+ char *binsuffix;
+ int usemacos = 0;
+ grub_file_t binfile;
+
+ auto int load_hook (const char *filename,
+ const struct grub_dirhook_info *info);
+
+ int load_hook (const char *filename, const struct grub_dirhook_info *info)
+ {
+ if (grub_strlen (filename) > 15)
+ return 0;
+ grub_strcpy (newdirname + grub_strlen (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 (newdirname, osbundlerequired,
+ maxrecursion - 1);
+
+ /* Directory "Plugins" contains nested kexts. */
+ if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
+ grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired,
+ maxrecursion - 1);
+
+ /* Directory "MacOS" contains executable, otherwise executable is
+ on the top. */
+ if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
+ usemacos = 1;
+
+ /* Info.plist is the file which governs our future actions. */
+ if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
+ && ! plistname)
+ plistname = grub_strdup (newdirname);
+ return 0;
+ }
+
+ newdirname = grub_malloc (grub_strlen (dirname) + 20);
+ if (! newdirname)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate buffer");
+ grub_strcpy (newdirname, dirname);
+ newdirname[grub_strlen (dirname)] = '/';
+ 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 (newdirname, ')');
+ if (! newpath)
+ newpath = newdirname;
+ else
+ newpath++;
+
+ /* Look at the directory. */
+ if (fs)
+ (fs->dir) (dev, path, load_hook);
+
+ if (plistname && grub_xnu_check_os_bundle_required
+ (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 (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", plistname, binname);
+ binfile = grub_file_open (binname);
+ if (! binfile)
+ grub_errno = GRUB_ERR_NONE;
+
+ /* Load the extension. */
+ grub_xnu_load_driver (plistname, binfile);
+ grub_free (binname);
+ grub_free (binsuffix);
+ }
+ else
+ {
+ grub_dprintf ("xnu", "%s:0\n", plistname);
+ grub_xnu_load_driver (plistname, 0);
+ }
+ }
+ grub_free (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, "no xnu kernel loaded");
+
+ if (argc == 2)
+ {
+ /* User explicitly specified plist and binary. */
+ if (grub_strcmp (args[1], "-") != 0)
+ {
+ binfile = grub_file_open (args[1]);
+ if (! binfile)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "can't open file");
+ return GRUB_ERR_NONE;
+ }
+ }
+ return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
+ binfile);
+ }
+
+ /* load kext normally. */
+ if (argc == 1)
+ return grub_xnu_load_kext_from_dir (args[0], 0, 10);
+
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
+}
+
+/* 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, "no xnu kernel loaded");
+
+ 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_error (GRUB_ERR_OUT_OF_MEMORY,
+ "couldn't allocate string temporary space");
+ 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)
+{
+ auto int iterate_env (struct grub_env_var *var);
+ int iterate_env (struct grub_env_var *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)
+ return 0;
+
+ 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 1;
+
+ 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 1;
+
+ unescape (name, curdot, nextdot, &len);
+ name[len] = 0;
+
+ curvalue = grub_xnu_create_value (curkey, name);
+ grub_free (name);
+
+ data = grub_malloc (grub_strlen (var->value) + 1);
+ if (!data)
+ return 1;
+
+ unescape (data, var->value, var->value + grub_strlen (var->value),
+ &len);
+ curvalue->datasize = len;
+ curvalue->data = data;
+
+ return 0;
+ }
+
+ grub_env_iterate (iterate_env);
+
+ 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, "Background image mode.", "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, "file name required");
+
+ if (! grub_xnu_heap_size)
+ return grub_error (GRUB_ERR_BAD_OS, "no xnu kernel loaded");
+
+ 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, "file name required");
+
+ 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 ("xnu_mkext", grub_cmd_xnu_mkext, 0,
+ N_("Load XNU extension package."));
+ cmd_kext = grub_register_command ("xnu_kext", grub_cmd_xnu_kext, 0,
+ N_("Load XNU extension."));
+ cmd_kextdir = grub_register_command ("xnu_kextdir", grub_cmd_xnu_kextdir,
+ N_("DIRECTORY [OSBundleRequired]"),
+ N_("Load XNU extension directory."));
+ cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
+ "Load XNU ramdisk. "
+ "It will be seen 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 XNU hibernate image."));
+#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..8f0e244
--- /dev/null
+++ b/grub-core/loader/xnu_resume.c
@@ -0,0 +1,172 @@
+/*
+ * 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>
+
+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;
+
+ grub_file_filter_disable_compression ();
+ file = grub_file_open (imagename);
+ if (! file)
+ return 0;
+
+ /* Read the header. */
+ if (grub_file_read (file, &hibhead, sizeof (hibhead))
+ !=sizeof (hibhead))
+ {
+ grub_file_close (file);
+ return grub_error (GRUB_ERR_READ_ERROR,
+ "cannot read the hibernate header");
+ }
+
+ /* 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");
+ }
+
+ 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,
+ (0xffffffff - hibhead.image_size) + 1,
+ hibhead.image_size,
+ GRUB_XNU_PAGESIZE,
+ GRUB_RELOCATOR_PREFERENCE_NONE);
+ 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);
+ return grub_error (GRUB_ERR_READ_ERROR, "cannot read resume image");
+ }
+
+ /* 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);
+ return grub_error (GRUB_ERR_READ_ERROR, "cannot read resume image");
+ }
+ 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;
+}