aboutsummaryrefslogtreecommitdiffstats
path: root/grub-core/kern
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/kern
downloadgrub-1.99-041d1ea37802bf7178a31a53f96c26efa6b8fb7b.tar.gz
grub-1.99-041d1ea37802bf7178a31a53f96c26efa6b8fb7b.tar.bz2
grub-1.99-041d1ea37802bf7178a31a53f96c26efa6b8fb7b.zip
fish
Diffstat (limited to 'grub-core/kern')
-rw-r--r--grub-core/kern/command.c58
-rw-r--r--grub-core/kern/corecmd.c194
-rw-r--r--grub-core/kern/device.c169
-rw-r--r--grub-core/kern/disk.c604
-rw-r--r--grub-core/kern/dl.c724
-rw-r--r--grub-core/kern/efi/efi.c742
-rw-r--r--grub-core/kern/efi/init.c130
-rw-r--r--grub-core/kern/efi/mm.c453
-rw-r--r--grub-core/kern/elf.c488
-rw-r--r--grub-core/kern/emu/cache.S28
-rw-r--r--grub-core/kern/emu/console.c293
-rw-r--r--grub-core/kern/emu/full.c71
-rw-r--r--grub-core/kern/emu/getroot.c932
-rw-r--r--grub-core/kern/emu/hostdisk.c1776
-rw-r--r--grub-core/kern/emu/hostfs.c175
-rw-r--r--grub-core/kern/emu/lite.c45
-rw-r--r--grub-core/kern/emu/main.c259
-rw-r--r--grub-core/kern/emu/misc.c537
-rw-r--r--grub-core/kern/emu/mm.c87
-rw-r--r--grub-core/kern/emu/time.c46
-rw-r--r--grub-core/kern/env.c262
-rw-r--r--grub-core/kern/err.c139
-rw-r--r--grub-core/kern/file.c186
-rw-r--r--grub-core/kern/fs.c231
-rw-r--r--grub-core/kern/generic/millisleep.c39
-rw-r--r--grub-core/kern/generic/rtc_get_time_ms.c37
-rw-r--r--grub-core/kern/i386/coreboot/init.c132
-rw-r--r--grub-core/kern/i386/coreboot/mmap.c108
-rw-r--r--grub-core/kern/i386/coreboot/startup.S89
-rw-r--r--grub-core/kern/i386/dl.c109
-rw-r--r--grub-core/kern/i386/efi/init.c47
-rw-r--r--grub-core/kern/i386/efi/startup.S66
-rw-r--r--grub-core/kern/i386/ieee1275/startup.S65
-rw-r--r--grub-core/kern/i386/multiboot_mmap.c73
-rw-r--r--grub-core/kern/i386/pc/init.c234
-rw-r--r--grub-core/kern/i386/pc/lzma_decode.S614
-rw-r--r--grub-core/kern/i386/pc/mmap.c190
-rw-r--r--grub-core/kern/i386/pc/startup.S1011
-rw-r--r--grub-core/kern/i386/pit.c56
-rw-r--r--grub-core/kern/i386/qemu/init.c147
-rw-r--r--grub-core/kern/i386/qemu/mmap.c100
-rw-r--r--grub-core/kern/i386/qemu/startup.S104
-rw-r--r--grub-core/kern/i386/realmode.S242
-rw-r--r--grub-core/kern/i386/tsc.c74
-rw-r--r--grub-core/kern/ieee1275/cmain.c182
-rw-r--r--grub-core/kern/ieee1275/ieee1275.c609
-rw-r--r--grub-core/kern/ieee1275/init.c274
-rw-r--r--grub-core/kern/ieee1275/mmap.c80
-rw-r--r--grub-core/kern/ieee1275/openfw.c469
-rw-r--r--grub-core/kern/list.c87
-rw-r--r--grub-core/kern/main.c200
-rw-r--r--grub-core/kern/mips/cache.S45
-rw-r--r--grub-core/kern/mips/cache_flush.S23
-rw-r--r--grub-core/kern/mips/dl.c239
-rw-r--r--grub-core/kern/mips/init.c34
-rw-r--r--grub-core/kern/mips/qemu-mips/init.c58
-rw-r--r--grub-core/kern/mips/startup.S126
-rw-r--r--grub-core/kern/mips/yeeloong/init.c253
-rw-r--r--grub-core/kern/misc.c1019
-rw-r--r--grub-core/kern/mm.c572
-rw-r--r--grub-core/kern/parser.c267
-rw-r--r--grub-core/kern/partition.c253
-rw-r--r--grub-core/kern/powerpc/cache.S26
-rw-r--r--grub-core/kern/powerpc/cache_flush.S43
-rw-r--r--grub-core/kern/powerpc/dl.c136
-rw-r--r--grub-core/kern/powerpc/ieee1275/startup.S64
-rw-r--r--grub-core/kern/rescue_parser.c76
-rw-r--r--grub-core/kern/rescue_reader.c97
-rw-r--r--grub-core/kern/sparc64/cache.S41
-rw-r--r--grub-core/kern/sparc64/dl.c142
-rw-r--r--grub-core/kern/sparc64/ieee1275/crt0.S94
-rw-r--r--grub-core/kern/sparc64/ieee1275/ieee1275.c91
-rw-r--r--grub-core/kern/sparc64/ieee1275/init.c174
-rw-r--r--grub-core/kern/term.c130
-rw-r--r--grub-core/kern/time.c37
-rw-r--r--grub-core/kern/x86_64/dl.c119
-rw-r--r--grub-core/kern/x86_64/efi/callwrap.S116
-rw-r--r--grub-core/kern/x86_64/efi/startup.S63
78 files changed, 18405 insertions, 0 deletions
diff --git a/grub-core/kern/command.c b/grub-core/kern/command.c
new file mode 100644
index 0000000..7f353b6
--- /dev/null
+++ b/grub-core/kern/command.c
@@ -0,0 +1,58 @@
+/* command.c - support basic command */
+/*
+ * 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/mm.h>
+#include <grub/command.h>
+
+grub_command_t grub_command_list;
+
+grub_command_t
+grub_register_command_prio (const char *name,
+ grub_command_func_t func,
+ const char *summary,
+ const char *description,
+ int prio)
+{
+ grub_command_t cmd;
+
+ cmd = (grub_command_t) grub_zalloc (sizeof (*cmd));
+ if (! cmd)
+ return 0;
+
+ cmd->name = name;
+ cmd->func = func;
+ cmd->summary = (summary) ? summary : "";
+ cmd->description = description;
+
+ cmd->flags = 0;
+ cmd->prio = prio;
+
+ grub_prio_list_insert (GRUB_AS_PRIO_LIST_P (&grub_command_list),
+ GRUB_AS_PRIO_LIST (cmd));
+
+ return cmd;
+}
+
+void
+grub_unregister_command (grub_command_t cmd)
+{
+ grub_prio_list_remove (GRUB_AS_PRIO_LIST_P (&grub_command_list),
+ GRUB_AS_PRIO_LIST (cmd));
+ grub_free (cmd);
+}
diff --git a/grub-core/kern/corecmd.c b/grub-core/kern/corecmd.c
new file mode 100644
index 0000000..687692f
--- /dev/null
+++ b/grub-core/kern/corecmd.c
@@ -0,0 +1,194 @@
+/* corecmd.c - critical commands which are registered in kernel */
+/*
+ * 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/mm.h>
+#include <grub/dl.h>
+#include <grub/err.h>
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/term.h>
+#include <grub/file.h>
+#include <grub/device.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+
+/* set ENVVAR=VALUE */
+static grub_err_t
+grub_core_cmd_set (struct grub_command *cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ char *var;
+ char *val;
+
+ auto int print_env (struct grub_env_var *env);
+
+ int print_env (struct grub_env_var *env)
+ {
+ grub_printf ("%s=%s\n", env->name, env->value);
+ return 0;
+ }
+
+ if (argc < 1)
+ {
+ grub_env_iterate (print_env);
+ return 0;
+ }
+
+ var = argv[0];
+ val = grub_strchr (var, '=');
+ if (! val)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "not an assignment");
+
+ val[0] = 0;
+ grub_env_set (var, val + 1);
+ val[0] = '=';
+
+ return 0;
+}
+
+static grub_err_t
+grub_core_cmd_unset (struct grub_command *cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ if (argc < 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "no environment variable specified");
+
+ grub_env_unset (argv[0]);
+ return 0;
+}
+
+/* insmod MODULE */
+static grub_err_t
+grub_core_cmd_insmod (struct grub_command *cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ char *p;
+ grub_dl_t mod;
+
+ if (argc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
+
+ p = grub_strchr (argv[0], '/');
+ if (! p)
+ mod = grub_dl_load (argv[0]);
+ else
+ mod = grub_dl_load_file (argv[0]);
+
+ if (mod)
+ grub_dl_ref (mod);
+
+ return 0;
+}
+
+static int
+grub_mini_print_devices (const char *name)
+{
+ grub_printf ("(%s) ", name);
+
+ return 0;
+}
+
+static int
+grub_mini_print_files (const char *filename,
+ const struct grub_dirhook_info *info)
+{
+ grub_printf ("%s%s ", filename, info->dir ? "/" : "");
+
+ return 0;
+}
+
+/* ls [ARG] */
+static grub_err_t
+grub_core_cmd_ls (struct grub_command *cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ if (argc < 1)
+ {
+ grub_device_iterate (grub_mini_print_devices);
+ grub_xputs ("\n");
+ grub_refresh ();
+ }
+ else
+ {
+ char *device_name;
+ grub_device_t dev;
+ grub_fs_t fs;
+ char *path;
+
+ device_name = grub_file_get_device_name (argv[0]);
+ dev = grub_device_open (device_name);
+ if (! dev)
+ goto fail;
+
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (argv[0], ')');
+ if (! path)
+ path = argv[0];
+ else
+ path++;
+
+ if (! path && ! device_name)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument");
+ goto fail;
+ }
+
+ if (! path)
+ {
+ if (grub_errno == GRUB_ERR_UNKNOWN_FS)
+ grub_errno = GRUB_ERR_NONE;
+
+ grub_printf ("(%s): Filesystem is %s.\n",
+ device_name, fs ? fs->name : "unknown");
+ }
+ else if (fs)
+ {
+ (fs->dir) (dev, path, grub_mini_print_files);
+ grub_xputs ("\n");
+ grub_refresh ();
+ }
+
+ fail:
+ if (dev)
+ grub_device_close (dev);
+
+ grub_free (device_name);
+ }
+
+ return grub_errno;
+}
+
+void
+grub_register_core_commands (void)
+{
+ grub_command_t cmd;
+ cmd = grub_register_command ("set", grub_core_cmd_set,
+ N_("[ENVVAR=VALUE]"),
+ N_("Set an environment variable."));
+ if (cmd)
+ cmd->flags |= GRUB_COMMAND_FLAG_EXTRACTOR;
+ grub_register_command ("unset", grub_core_cmd_unset,
+ N_("ENVVAR"),
+ N_("Remove an environment variable."));
+ grub_register_command ("ls", grub_core_cmd_ls,
+ N_("[ARG]"), N_("List devices or files."));
+ grub_register_command ("insmod", grub_core_cmd_insmod,
+ N_("MODULE"), N_("Insert a module."));
+}
diff --git a/grub-core/kern/device.c b/grub-core/kern/device.c
new file mode 100644
index 0000000..3db14f5
--- /dev/null
+++ b/grub-core/kern/device.c
@@ -0,0 +1,169 @@
+/* device.c - device manager */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,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/device.h>
+#include <grub/disk.h>
+#include <grub/net.h>
+#include <grub/fs.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/partition.h>
+
+grub_device_t
+grub_device_open (const char *name)
+{
+ grub_disk_t disk = 0;
+ grub_device_t dev = 0;
+
+ if (! name)
+ {
+ name = grub_env_get ("root");
+ if (*name == '\0')
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "no device is set");
+ goto fail;
+ }
+ }
+
+ dev = grub_malloc (sizeof (*dev));
+ if (! dev)
+ goto fail;
+
+ /* Try to open a disk. */
+ disk = grub_disk_open (name);
+ if (! disk)
+ goto fail;
+
+ dev->disk = disk;
+ dev->net = 0; /* FIXME */
+
+ return dev;
+
+ fail:
+ if (disk)
+ grub_disk_close (disk);
+
+ grub_free (dev);
+
+ return 0;
+}
+
+grub_err_t
+grub_device_close (grub_device_t device)
+{
+ if (device->disk)
+ grub_disk_close (device->disk);
+
+ grub_free (device);
+
+ return grub_errno;
+}
+
+int
+grub_device_iterate (int (*hook) (const char *name))
+{
+ auto int iterate_disk (const char *disk_name);
+ auto int iterate_partition (grub_disk_t disk,
+ const grub_partition_t partition);
+
+ struct part_ent
+ {
+ struct part_ent *next;
+ char *name;
+ } *ents;
+
+ int iterate_disk (const char *disk_name)
+ {
+ grub_device_t dev;
+
+ if (hook (disk_name))
+ return 1;
+
+ dev = grub_device_open (disk_name);
+ if (! dev)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ return 0;
+ }
+
+ if (dev->disk)
+ {
+ struct part_ent *p;
+ int ret = 0;
+
+ ents = NULL;
+ (void) grub_partition_iterate (dev->disk, iterate_partition);
+ grub_device_close (dev);
+
+ grub_errno = GRUB_ERR_NONE;
+
+ p = ents;
+ while (p != NULL)
+ {
+ struct part_ent *next = p->next;
+
+ if (!ret)
+ ret = hook (p->name);
+ grub_free (p->name);
+ grub_free (p);
+ p = next;
+ }
+
+ return ret;
+ }
+
+ grub_device_close (dev);
+ return 0;
+ }
+
+ int iterate_partition (grub_disk_t disk, const grub_partition_t partition)
+ {
+ struct part_ent *p;
+ char *part_name;
+
+ p = grub_malloc (sizeof (*p));
+ if (!p)
+ {
+ return 1;
+ }
+
+ part_name = grub_partition_get_name (partition);
+ if (!part_name)
+ {
+ grub_free (p);
+ return 1;
+ }
+ p->name = grub_xasprintf ("%s,%s", disk->name, part_name);
+ grub_free (part_name);
+ if (!p->name)
+ {
+ grub_free (p);
+ return 1;
+ }
+
+ p->next = ents;
+ ents = p;
+
+ return 0;
+ }
+
+ /* Only disk devices are supported at the moment. */
+ return grub_disk_dev_iterate (iterate_disk);
+}
diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c
new file mode 100644
index 0000000..807ee42
--- /dev/null
+++ b/grub-core/kern/disk.c
@@ -0,0 +1,604 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2004,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/disk.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/partition.h>
+#include <grub/misc.h>
+#include <grub/time.h>
+#include <grub/file.h>
+
+#define GRUB_CACHE_TIMEOUT 2
+
+/* The last time the disk was used. */
+static grub_uint64_t grub_last_time = 0;
+
+
+/* Disk cache. */
+struct grub_disk_cache
+{
+ enum grub_disk_dev_id dev_id;
+ unsigned long disk_id;
+ grub_disk_addr_t sector;
+ char *data;
+ int lock;
+};
+
+static struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
+
+void (*grub_disk_firmware_fini) (void);
+int grub_disk_firmware_is_tainted;
+
+grub_err_t (* grub_disk_ata_pass_through) (grub_disk_t,
+ struct grub_disk_ata_pass_through_parms *);
+
+
+#if 0
+static unsigned long grub_disk_cache_hits;
+static unsigned long grub_disk_cache_misses;
+
+void
+grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
+{
+ *hits = grub_disk_cache_hits;
+ *misses = grub_disk_cache_misses;
+}
+#endif
+
+static unsigned
+grub_disk_cache_get_index (unsigned long dev_id, unsigned long disk_id,
+ grub_disk_addr_t sector)
+{
+ return ((dev_id * 524287UL + disk_id * 2606459UL
+ + ((unsigned) (sector >> GRUB_DISK_CACHE_BITS)))
+ % GRUB_DISK_CACHE_NUM);
+}
+
+static void
+grub_disk_cache_invalidate (unsigned long dev_id, unsigned long disk_id,
+ grub_disk_addr_t sector)
+{
+ unsigned index;
+ struct grub_disk_cache *cache;
+
+ sector &= ~(GRUB_DISK_CACHE_SIZE - 1);
+ index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+ cache = grub_disk_cache_table + index;
+
+ if (cache->dev_id == dev_id && cache->disk_id == disk_id
+ && cache->sector == sector && cache->data)
+ {
+ cache->lock = 1;
+ grub_free (cache->data);
+ cache->data = 0;
+ cache->lock = 0;
+ }
+}
+
+void
+grub_disk_cache_invalidate_all (void)
+{
+ unsigned i;
+
+ for (i = 0; i < GRUB_DISK_CACHE_NUM; i++)
+ {
+ struct grub_disk_cache *cache = grub_disk_cache_table + i;
+
+ if (cache->data && ! cache->lock)
+ {
+ grub_free (cache->data);
+ cache->data = 0;
+ }
+ }
+}
+
+static char *
+grub_disk_cache_fetch (unsigned long dev_id, unsigned long disk_id,
+ grub_disk_addr_t sector)
+{
+ struct grub_disk_cache *cache;
+ unsigned index;
+
+ index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+ cache = grub_disk_cache_table + index;
+
+ if (cache->dev_id == dev_id && cache->disk_id == disk_id
+ && cache->sector == sector)
+ {
+ cache->lock = 1;
+#if 0
+ grub_disk_cache_hits++;
+#endif
+ return cache->data;
+ }
+
+#if 0
+ grub_disk_cache_misses++;
+#endif
+
+ return 0;
+}
+
+static void
+grub_disk_cache_unlock (unsigned long dev_id, unsigned long disk_id,
+ grub_disk_addr_t sector)
+{
+ struct grub_disk_cache *cache;
+ unsigned index;
+
+ index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+ cache = grub_disk_cache_table + index;
+
+ if (cache->dev_id == dev_id && cache->disk_id == disk_id
+ && cache->sector == sector)
+ cache->lock = 0;
+}
+
+static grub_err_t
+grub_disk_cache_store (unsigned long dev_id, unsigned long disk_id,
+ grub_disk_addr_t sector, const char *data)
+{
+ unsigned index;
+ struct grub_disk_cache *cache;
+
+ index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+ cache = grub_disk_cache_table + index;
+
+ cache->lock = 1;
+ grub_free (cache->data);
+ cache->data = 0;
+ cache->lock = 0;
+
+ cache->data = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
+ if (! cache->data)
+ return grub_errno;
+
+ grub_memcpy (cache->data, data,
+ GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
+ cache->dev_id = dev_id;
+ cache->disk_id = disk_id;
+ cache->sector = sector;
+
+ return GRUB_ERR_NONE;
+}
+
+
+
+static grub_disk_dev_t grub_disk_dev_list;
+
+void
+grub_disk_dev_register (grub_disk_dev_t dev)
+{
+ dev->next = grub_disk_dev_list;
+ grub_disk_dev_list = dev;
+}
+
+void
+grub_disk_dev_unregister (grub_disk_dev_t dev)
+{
+ grub_disk_dev_t *p, q;
+
+ for (p = &grub_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
+ if (q == dev)
+ {
+ *p = q->next;
+ break;
+ }
+}
+
+int
+grub_disk_dev_iterate (int (*hook) (const char *name))
+{
+ grub_disk_dev_t p;
+
+ for (p = grub_disk_dev_list; p; p = p->next)
+ if (p->iterate && (p->iterate) (hook))
+ return 1;
+
+ return 0;
+}
+
+/* Return the location of the first ',', if any, which is not
+ escaped by a '\'. */
+static const char *
+find_part_sep (const char *name)
+{
+ const char *p = name;
+ char c;
+
+ while ((c = *p++) != '\0')
+ {
+ if (c == '\\' && *p == ',')
+ p++;
+ else if (c == ',')
+ return p - 1;
+ }
+ return NULL;
+}
+
+grub_disk_t
+grub_disk_open (const char *name)
+{
+ const char *p;
+ grub_disk_t disk;
+ grub_disk_dev_t dev;
+ char *raw = (char *) name;
+ grub_uint64_t current_time;
+
+ grub_dprintf ("disk", "Opening `%s'...\n", name);
+
+ disk = (grub_disk_t) grub_zalloc (sizeof (*disk));
+ if (! disk)
+ return 0;
+
+ p = find_part_sep (name);
+ if (p)
+ {
+ grub_size_t len = p - name;
+
+ raw = grub_malloc (len + 1);
+ if (! raw)
+ goto fail;
+
+ grub_memcpy (raw, name, len);
+ raw[len] = '\0';
+ disk->name = grub_strdup (raw);
+ }
+ else
+ disk->name = grub_strdup (name);
+ if (! disk->name)
+ goto fail;
+
+
+ for (dev = grub_disk_dev_list; dev; dev = dev->next)
+ {
+ if ((dev->open) (raw, disk) == GRUB_ERR_NONE)
+ break;
+ else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
+ grub_errno = GRUB_ERR_NONE;
+ else
+ goto fail;
+ }
+
+ if (! dev)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such disk");
+ goto fail;
+ }
+
+ disk->dev = dev;
+
+ if (p)
+ {
+ disk->partition = grub_partition_probe (disk, p + 1);
+ if (! disk->partition)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such partition");
+ goto fail;
+ }
+ }
+
+ /* The cache will be invalidated about 2 seconds after a device was
+ closed. */
+ current_time = grub_get_time_ms ();
+
+ if (current_time > (grub_last_time
+ + GRUB_CACHE_TIMEOUT * 1000))
+ grub_disk_cache_invalidate_all ();
+
+ grub_last_time = current_time;
+
+ fail:
+
+ if (raw && raw != name)
+ grub_free (raw);
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_error_push ();
+ grub_dprintf ("disk", "Opening `%s' failed.\n", name);
+ grub_error_pop ();
+
+ grub_disk_close (disk);
+ return 0;
+ }
+
+ return disk;
+}
+
+void
+grub_disk_close (grub_disk_t disk)
+{
+ grub_partition_t part;
+ grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
+
+ if (disk->dev && disk->dev->close)
+ (disk->dev->close) (disk);
+
+ /* Reset the timer. */
+ grub_last_time = grub_get_time_ms ();
+
+ while (disk->partition)
+ {
+ part = disk->partition->parent;
+ grub_free (disk->partition);
+ disk->partition = part;
+ }
+ grub_free ((void *) disk->name);
+ grub_free (disk);
+}
+
+/* This function performs three tasks:
+ - Make sectors disk relative from partition relative.
+ - Normalize offset to be less than the sector size.
+ - Verify that the range is inside the partition. */
+static grub_err_t
+grub_disk_adjust_range (grub_disk_t disk, grub_disk_addr_t *sector,
+ grub_off_t *offset, grub_size_t size)
+{
+ grub_partition_t part;
+ *sector += *offset >> GRUB_DISK_SECTOR_BITS;
+ *offset &= GRUB_DISK_SECTOR_SIZE - 1;
+
+ for (part = disk->partition; part; part = part->parent)
+ {
+ grub_disk_addr_t start;
+ grub_uint64_t len;
+
+ start = part->start;
+ len = part->len;
+
+ if (*sector >= len
+ || len - *sector < ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1)
+ >> GRUB_DISK_SECTOR_BITS))
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of partition");
+
+ *sector += start;
+ }
+
+ if (disk->total_sectors <= *sector
+ || ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1)
+ >> GRUB_DISK_SECTOR_BITS) > disk->total_sectors - *sector)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of disk");
+
+ return GRUB_ERR_NONE;
+}
+
+/* Read data from the disk. */
+grub_err_t
+grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
+ grub_off_t offset, grub_size_t size, void *buf)
+{
+ char *tmp_buf;
+ unsigned real_offset;
+
+ /* First of all, check if the region is within the disk. */
+ if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
+ {
+ grub_error_push ();
+ grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
+ (unsigned long long) sector, grub_errmsg);
+ grub_error_pop ();
+ return grub_errno;
+ }
+
+ real_offset = offset;
+
+ /* Allocate a temporary buffer. */
+ tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
+ if (! tmp_buf)
+ return grub_errno;
+
+ /* Until SIZE is zero... */
+ while (size)
+ {
+ char *data;
+ grub_disk_addr_t start_sector;
+ grub_size_t len;
+ grub_size_t pos;
+
+ /* For reading bulk data. */
+ start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1);
+ pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
+ len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
+ - pos - real_offset);
+ if (len > size)
+ len = size;
+
+ /* Fetch the cache. */
+ data = grub_disk_cache_fetch (disk->dev->id, disk->id, start_sector);
+ if (data)
+ {
+ /* Just copy it! */
+ grub_memcpy (buf, data + pos + real_offset, len);
+ grub_disk_cache_unlock (disk->dev->id, disk->id, start_sector);
+ }
+ else
+ {
+ /* Otherwise read data from the disk actually. */
+ if (start_sector + GRUB_DISK_CACHE_SIZE > disk->total_sectors
+ || (disk->dev->read) (disk, start_sector,
+ GRUB_DISK_CACHE_SIZE, tmp_buf)
+ != GRUB_ERR_NONE)
+ {
+ /* Uggh... Failed. Instead, just read necessary data. */
+ unsigned num;
+ char *p;
+
+ grub_errno = GRUB_ERR_NONE;
+
+ num = ((size + real_offset + GRUB_DISK_SECTOR_SIZE - 1)
+ >> GRUB_DISK_SECTOR_BITS);
+
+ p = grub_realloc (tmp_buf, num << GRUB_DISK_SECTOR_BITS);
+ if (!p)
+ goto finish;
+
+ tmp_buf = p;
+
+ if ((disk->dev->read) (disk, sector, num, tmp_buf))
+ {
+ grub_error_push ();
+ grub_dprintf ("disk", "%s read failed\n", disk->name);
+ grub_error_pop ();
+ goto finish;
+ }
+
+ grub_memcpy (buf, tmp_buf + real_offset, size);
+
+ /* Call the read hook, if any. */
+ if (disk->read_hook)
+ while (size)
+ {
+ grub_size_t to_read = (size > GRUB_DISK_SECTOR_SIZE) ? GRUB_DISK_SECTOR_SIZE : size;
+ (disk->read_hook) (sector, real_offset,
+ to_read);
+ if (grub_errno != GRUB_ERR_NONE)
+ goto finish;
+
+ sector++;
+ size -= to_read - real_offset;
+ real_offset = 0;
+ }
+
+ /* This must be the end. */
+ goto finish;
+ }
+
+ /* Copy it and store it in the disk cache. */
+ grub_memcpy (buf, tmp_buf + pos + real_offset, len);
+ grub_disk_cache_store (disk->dev->id, disk->id,
+ start_sector, tmp_buf);
+ }
+
+ /* Call the read hook, if any. */
+ if (disk->read_hook)
+ {
+ grub_disk_addr_t s = sector;
+ grub_size_t l = len;
+
+ while (l)
+ {
+ (disk->read_hook) (s, real_offset,
+ ((l > GRUB_DISK_SECTOR_SIZE)
+ ? GRUB_DISK_SECTOR_SIZE
+ : l));
+
+ if (l < GRUB_DISK_SECTOR_SIZE - real_offset)
+ break;
+
+ s++;
+ l -= GRUB_DISK_SECTOR_SIZE - real_offset;
+ real_offset = 0;
+ }
+ }
+
+ sector = start_sector + GRUB_DISK_CACHE_SIZE;
+ buf = (char *) buf + len;
+ size -= len;
+ real_offset = 0;
+ }
+
+ finish:
+
+ grub_free (tmp_buf);
+
+ return grub_errno;
+}
+
+grub_err_t
+grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector,
+ grub_off_t offset, grub_size_t size, const void *buf)
+{
+ unsigned real_offset;
+
+ grub_dprintf ("disk", "Writing `%s'...\n", disk->name);
+
+ if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
+ return -1;
+
+ real_offset = offset;
+
+ while (size)
+ {
+ if (real_offset != 0 || (size < GRUB_DISK_SECTOR_SIZE && size != 0))
+ {
+ char tmp_buf[GRUB_DISK_SECTOR_SIZE];
+ grub_size_t len;
+ grub_partition_t part;
+
+ part = disk->partition;
+ disk->partition = 0;
+ if (grub_disk_read (disk, sector, 0, GRUB_DISK_SECTOR_SIZE, tmp_buf)
+ != GRUB_ERR_NONE)
+ {
+ disk->partition = part;
+ goto finish;
+ }
+ disk->partition = part;
+
+ len = GRUB_DISK_SECTOR_SIZE - real_offset;
+ if (len > size)
+ len = size;
+
+ grub_memcpy (tmp_buf + real_offset, buf, len);
+
+ grub_disk_cache_invalidate (disk->dev->id, disk->id, sector);
+
+ if ((disk->dev->write) (disk, sector, 1, tmp_buf) != GRUB_ERR_NONE)
+ goto finish;
+
+ sector++;
+ buf = (char *) buf + len;
+ size -= len;
+ real_offset = 0;
+ }
+ else
+ {
+ grub_size_t len;
+ grub_size_t n;
+
+ len = size & ~(GRUB_DISK_SECTOR_SIZE - 1);
+ n = size >> GRUB_DISK_SECTOR_BITS;
+
+ if ((disk->dev->write) (disk, sector, n, buf) != GRUB_ERR_NONE)
+ goto finish;
+
+ while (n--)
+ grub_disk_cache_invalidate (disk->dev->id, disk->id, sector++);
+
+ buf = (char *) buf + len;
+ size -= len;
+ }
+ }
+
+ finish:
+
+ return grub_errno;
+}
+
+grub_uint64_t
+grub_disk_get_size (grub_disk_t disk)
+{
+ if (disk->partition)
+ return grub_partition_get_len (disk->partition);
+ else
+ return disk->total_sectors;
+}
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
new file mode 100644
index 0000000..623e0cb
--- /dev/null
+++ b/grub-core/kern/dl.c
@@ -0,0 +1,724 @@
+/* dl.c - loadable module support */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/>.
+ */
+
+/* Force native word size */
+#define GRUB_TARGET_WORDSIZE (8 * GRUB_CPU_SIZEOF_VOID_P)
+
+#include <config.h>
+#include <grub/elf.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/symbol.h>
+#include <grub/file.h>
+#include <grub/env.h>
+#include <grub/cache.h>
+
+/* Platforms where modules are in a readonly area of memory. */
+#if defined(GRUB_MACHINE_QEMU)
+#define GRUB_MODULES_MACHINE_READONLY
+#endif
+
+
+
+grub_dl_t grub_dl_head = 0;
+
+static grub_err_t
+grub_dl_add (grub_dl_t mod)
+{
+ if (grub_dl_get (mod->name))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "`%s' is already loaded", mod->name);
+
+ mod->next = grub_dl_head;
+ grub_dl_head = mod;
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_dl_remove (grub_dl_t mod)
+{
+ grub_dl_t *p, q;
+
+ for (p = &grub_dl_head, q = *p; q; p = &q->next, q = *p)
+ if (q == mod)
+ {
+ *p = q->next;
+ return;
+ }
+}
+
+grub_dl_t
+grub_dl_get (const char *name)
+{
+ grub_dl_t l;
+
+ for (l = grub_dl_head; l; l = l->next)
+ if (grub_strcmp (name, l->name) == 0)
+ return l;
+
+ return 0;
+}
+
+
+
+struct grub_symbol
+{
+ struct grub_symbol *next;
+ const char *name;
+ void *addr;
+ grub_dl_t mod; /* The module to which this symbol belongs. */
+};
+typedef struct grub_symbol *grub_symbol_t;
+
+/* The size of the symbol table. */
+#define GRUB_SYMTAB_SIZE 509
+
+/* The symbol table (using an open-hash). */
+static struct grub_symbol *grub_symtab[GRUB_SYMTAB_SIZE];
+
+/* Simple hash function. */
+static unsigned
+grub_symbol_hash (const char *s)
+{
+ unsigned key = 0;
+
+ while (*s)
+ key = key * 65599 + *s++;
+
+ return (key + (key >> 5)) % GRUB_SYMTAB_SIZE;
+}
+
+/* Resolve the symbol name NAME and return the address.
+ Return NULL, if not found. */
+static void *
+grub_dl_resolve_symbol (const char *name)
+{
+ grub_symbol_t sym;
+
+ for (sym = grub_symtab[grub_symbol_hash (name)]; sym; sym = sym->next)
+ if (grub_strcmp (sym->name, name) == 0)
+ return sym->addr;
+
+ return 0;
+}
+
+/* Register a symbol with the name NAME and the address ADDR. */
+grub_err_t
+grub_dl_register_symbol (const char *name, void *addr, grub_dl_t mod)
+{
+ grub_symbol_t sym;
+ unsigned k;
+
+ sym = (grub_symbol_t) grub_malloc (sizeof (*sym));
+ if (! sym)
+ return grub_errno;
+
+ if (mod)
+ {
+ sym->name = grub_strdup (name);
+ if (! sym->name)
+ {
+ grub_free (sym);
+ return grub_errno;
+ }
+ }
+ else
+ sym->name = name;
+
+ sym->addr = addr;
+ sym->mod = mod;
+
+ k = grub_symbol_hash (name);
+ sym->next = grub_symtab[k];
+ grub_symtab[k] = sym;
+
+ return GRUB_ERR_NONE;
+}
+
+/* Unregister all the symbols defined in the module MOD. */
+static void
+grub_dl_unregister_symbols (grub_dl_t mod)
+{
+ unsigned i;
+
+ if (! mod)
+ grub_fatal ("core symbols cannot be unregistered");
+
+ for (i = 0; i < GRUB_SYMTAB_SIZE; i++)
+ {
+ grub_symbol_t sym, *p, q;
+
+ for (p = &grub_symtab[i], sym = *p; sym; sym = q)
+ {
+ q = sym->next;
+ if (sym->mod == mod)
+ {
+ *p = q;
+ grub_free ((void *) sym->name);
+ grub_free (sym);
+ }
+ else
+ p = &sym->next;
+ }
+ }
+}
+
+/* Return the address of a section whose index is N. */
+static void *
+grub_dl_get_section_addr (grub_dl_t mod, unsigned n)
+{
+ grub_dl_segment_t seg;
+
+ for (seg = mod->segment; seg; seg = seg->next)
+ if (seg->section == n)
+ return seg->addr;
+
+ return 0;
+}
+
+/* Check if EHDR is a valid ELF header. */
+static grub_err_t
+grub_dl_check_header (void *ehdr, grub_size_t size)
+{
+ Elf_Ehdr *e = ehdr;
+
+ /* Check the header size. */
+ if (size < sizeof (Elf_Ehdr))
+ return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected");
+
+ /* Check the magic numbers. */
+ if (grub_arch_dl_check_header (ehdr)
+ || 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");
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load all segments from memory specified by E. */
+static grub_err_t
+grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+{
+ unsigned i;
+ Elf_Shdr *s;
+
+ for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
+ {
+ if (s->sh_flags & SHF_ALLOC)
+ {
+ grub_dl_segment_t seg;
+
+ seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
+ if (! seg)
+ return grub_errno;
+
+ if (s->sh_size)
+ {
+ void *addr;
+
+ addr = grub_memalign (s->sh_addralign, s->sh_size);
+ if (! addr)
+ {
+ grub_free (seg);
+ return grub_errno;
+ }
+
+ switch (s->sh_type)
+ {
+ case SHT_PROGBITS:
+ grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
+ break;
+ case SHT_NOBITS:
+ grub_memset (addr, 0, s->sh_size);
+ break;
+ }
+
+ seg->addr = addr;
+ }
+ else
+ seg->addr = 0;
+
+ seg->size = s->sh_size;
+ seg->section = i;
+ seg->next = mod->segment;
+ mod->segment = seg;
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
+{
+ unsigned i;
+ Elf_Shdr *s;
+ Elf_Sym *sym;
+ const char *str;
+ Elf_Word size, entsize;
+
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+
+ if (i == e->e_shnum)
+ return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table");
+
+#ifdef GRUB_MODULES_MACHINE_READONLY
+ mod->symtab = grub_malloc (s->sh_size);
+ memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size);
+#else
+ mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset);
+#endif
+ sym = mod->symtab;
+ size = s->sh_size;
+ entsize = s->sh_entsize;
+
+ s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
+ str = (char *) e + s->sh_offset;
+
+ for (i = 0;
+ i < size / entsize;
+ i++, sym = (Elf_Sym *) ((char *) sym + entsize))
+ {
+ unsigned char type = ELF_ST_TYPE (sym->st_info);
+ unsigned char bind = ELF_ST_BIND (sym->st_info);
+ const char *name = str + sym->st_name;
+
+ switch (type)
+ {
+ case STT_NOTYPE:
+ case STT_OBJECT:
+ /* Resolve a global symbol. */
+ if (sym->st_name != 0 && sym->st_shndx == 0)
+ {
+ sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name);
+ if (! sym->st_value)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "symbol not found: `%s'", name);
+ }
+ else
+ {
+ sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
+ sym->st_shndx);
+ if (bind != STB_LOCAL)
+ if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
+ return grub_errno;
+ }
+ break;
+
+ case STT_FUNC:
+ sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
+ sym->st_shndx);
+ if (bind != STB_LOCAL)
+ if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
+ return grub_errno;
+
+ if (grub_strcmp (name, "grub_mod_init") == 0)
+ mod->init = (void (*) (grub_dl_t)) sym->st_value;
+ else if (grub_strcmp (name, "grub_mod_fini") == 0)
+ mod->fini = (void (*) (void)) sym->st_value;
+ break;
+
+ case STT_SECTION:
+ sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
+ sym->st_shndx);
+ break;
+
+ case STT_FILE:
+ sym->st_value = 0;
+ break;
+
+ default:
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "unknown symbol type `%d'", (int) type);
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_dl_call_init (grub_dl_t mod)
+{
+ if (mod->init)
+ (mod->init) (mod);
+}
+
+/* Me, Vladimir Serbinenko, hereby I add this module check as per new
+ GNU module policy. Note that this license check is informative only.
+ Modules have to be licensed under GPLv3 or GPLv3+ (optionally
+ multi-licensed under other licences as well) independently of the
+ presence of this check and solely by linking (module loading in GRUB
+ constitutes linking) and GRUB core being licensed under GPLv3+.
+ Be sure to understand your license obligations.
+*/
+static grub_err_t
+grub_dl_check_license (Elf_Ehdr *e)
+{
+ Elf_Shdr *s;
+ const char *str;
+ unsigned i;
+
+ s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
+ str = (char *) e + s->sh_offset;
+
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (grub_strcmp (str + s->sh_name, ".module_license") == 0)
+ {
+ if (grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3") == 0
+ || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3+") == 0
+ || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv2+") == 0)
+ return GRUB_ERR_NONE;
+ }
+
+ return grub_error (GRUB_ERR_BAD_MODULE, "incompatible license");
+}
+
+static grub_err_t
+grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
+{
+ Elf_Shdr *s;
+ const char *str;
+ unsigned i;
+
+ s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
+ str = (char *) e + s->sh_offset;
+
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (grub_strcmp (str + s->sh_name, ".modname") == 0)
+ {
+ mod->name = grub_strdup ((char *) e + s->sh_offset);
+ if (! mod->name)
+ return grub_errno;
+ break;
+ }
+
+ if (i == e->e_shnum)
+ return grub_error (GRUB_ERR_BAD_MODULE, "no module name found");
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
+{
+ Elf_Shdr *s;
+ const char *str;
+ unsigned i;
+
+ s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
+ str = (char *) e + s->sh_offset;
+
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (grub_strcmp (str + s->sh_name, ".moddeps") == 0)
+ {
+ const char *name = (char *) e + s->sh_offset;
+ const char *max = name + s->sh_size;
+
+ while ((name < max) && (*name))
+ {
+ grub_dl_t m;
+ grub_dl_dep_t dep;
+
+ m = grub_dl_load (name);
+ if (! m)
+ return grub_errno;
+
+ grub_dl_ref (m);
+
+ dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
+ if (! dep)
+ return grub_errno;
+
+ dep->mod = m;
+ dep->next = mod->dep;
+ mod->dep = dep;
+
+ name += grub_strlen (name) + 1;
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+int
+grub_dl_ref (grub_dl_t mod)
+{
+ grub_dl_dep_t dep;
+
+ if (!mod)
+ return 0;
+
+ for (dep = mod->dep; dep; dep = dep->next)
+ grub_dl_ref (dep->mod);
+
+ return ++mod->ref_count;
+}
+
+int
+grub_dl_unref (grub_dl_t mod)
+{
+ grub_dl_dep_t dep;
+
+ if (!mod)
+ return 0;
+
+ for (dep = mod->dep; dep; dep = dep->next)
+ grub_dl_unref (dep->mod);
+
+ return --mod->ref_count;
+}
+
+static void
+grub_dl_flush_cache (grub_dl_t mod)
+{
+ grub_dl_segment_t seg;
+
+ for (seg = mod->segment; seg; seg = seg->next) {
+ if (seg->size) {
+ grub_dprintf ("modules", "flushing 0x%lx bytes at %p\n",
+ (unsigned long) seg->size, seg->addr);
+ grub_arch_sync_caches (seg->addr, seg->size);
+ }
+ }
+}
+
+/* Load a module from core memory. */
+grub_dl_t
+grub_dl_load_core (void *addr, grub_size_t size)
+{
+ Elf_Ehdr *e;
+ grub_dl_t mod;
+
+ grub_dprintf ("modules", "module at %p, size 0x%lx\n", addr,
+ (unsigned long) size);
+ e = addr;
+ if (grub_dl_check_header (e, size))
+ return 0;
+
+ if (e->e_type != ET_REL)
+ {
+ grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type");
+ return 0;
+ }
+
+ /* Make sure that every section is within the core. */
+ if (size < e->e_shoff + e->e_shentsize * e->e_shnum)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
+ return 0;
+ }
+
+ mod = (grub_dl_t) grub_zalloc (sizeof (*mod));
+ if (! mod)
+ return 0;
+
+ mod->ref_count = 1;
+
+ grub_dprintf ("modules", "relocating to %p\n", mod);
+ /* Me, Vladimir Serbinenko, hereby I add this module check as per new
+ GNU module policy. Note that this license check is informative only.
+ Modules have to be licensed under GPLv3 or GPLv3+ (optionally
+ multi-licensed under other licences as well) independently of the
+ presence of this check and solely by linking (module loading in GRUB
+ constitutes linking) and GRUB core being licensed under GPLv3+.
+ Be sure to understand your license obligations.
+ */
+ if (grub_dl_check_license (e)
+ || grub_dl_resolve_name (mod, e)
+ || grub_dl_resolve_dependencies (mod, e)
+ || grub_dl_load_segments (mod, e)
+ || grub_dl_resolve_symbols (mod, e)
+ || grub_arch_dl_relocate_symbols (mod, e))
+ {
+ mod->fini = 0;
+ grub_dl_unload (mod);
+ return 0;
+ }
+
+ grub_dl_flush_cache (mod);
+
+ grub_dprintf ("modules", "module name: %s\n", mod->name);
+ grub_dprintf ("modules", "init function: %p\n", mod->init);
+ grub_dl_call_init (mod);
+
+ if (grub_dl_add (mod))
+ {
+ grub_dl_unload (mod);
+ return 0;
+ }
+
+ return mod;
+}
+
+/* Load a module from the file FILENAME. */
+grub_dl_t
+grub_dl_load_file (const char *filename)
+{
+ grub_file_t file = NULL;
+ grub_ssize_t size;
+ void *core = 0;
+ grub_dl_t mod = 0;
+
+ file = grub_file_open (filename);
+ if (! file)
+ return 0;
+
+ size = grub_file_size (file);
+ core = grub_malloc (size);
+ if (! core)
+ {
+ grub_file_close (file);
+ return 0;
+ }
+
+ if (grub_file_read (file, core, size) != (int) size)
+ {
+ grub_file_close (file);
+ grub_free (core);
+ return 0;
+ }
+
+ /* We must close this before we try to process dependencies.
+ Some disk backends do not handle gracefully multiple concurrent
+ opens of the same device. */
+ grub_file_close (file);
+
+ mod = grub_dl_load_core (core, size);
+ if (! mod)
+ {
+ grub_free (core);
+ return 0;
+ }
+
+ mod->ref_count--;
+ return mod;
+}
+
+/* Load a module using a symbolic name. */
+grub_dl_t
+grub_dl_load (const char *name)
+{
+ char *filename;
+ grub_dl_t mod;
+ char *grub_dl_dir = grub_env_get ("prefix");
+
+ mod = grub_dl_get (name);
+ if (mod)
+ return mod;
+
+ if (! grub_dl_dir) {
+ grub_error (GRUB_ERR_FILE_NOT_FOUND, "\"prefix\" is not set");
+ return 0;
+ }
+
+ filename = grub_xasprintf ("%s/%s.mod", grub_dl_dir, name);
+ if (! filename)
+ return 0;
+
+ mod = grub_dl_load_file (filename);
+ grub_free (filename);
+
+ if (! mod)
+ return 0;
+
+ if (grub_strcmp (mod->name, name) != 0)
+ grub_error (GRUB_ERR_BAD_MODULE, "mismatched names");
+
+ return mod;
+}
+
+/* Unload the module MOD. */
+int
+grub_dl_unload (grub_dl_t mod)
+{
+ grub_dl_dep_t dep, depn;
+ grub_dl_segment_t seg, segn;
+
+ if (mod->ref_count > 0)
+ return 0;
+
+ if (mod->fini)
+ (mod->fini) ();
+
+ grub_dl_remove (mod);
+ grub_dl_unregister_symbols (mod);
+
+ for (dep = mod->dep; dep; dep = depn)
+ {
+ depn = dep->next;
+
+ grub_dl_unload (dep->mod);
+
+ grub_free (dep);
+ }
+
+ for (seg = mod->segment; seg; seg = segn)
+ {
+ segn = seg->next;
+ grub_free (seg->addr);
+ grub_free (seg);
+ }
+
+ grub_free (mod->name);
+#ifdef GRUB_MODULES_MACHINE_READONLY
+ grub_free (mod->symtab);
+#endif
+ grub_free (mod);
+ return 1;
+}
+
+/* Unload unneeded modules. */
+void
+grub_dl_unload_unneeded (void)
+{
+ /* Because grub_dl_remove modifies the list of modules, this
+ implementation is tricky. */
+ grub_dl_t p = grub_dl_head;
+
+ while (p)
+ {
+ if (grub_dl_unload (p))
+ {
+ p = grub_dl_head;
+ continue;
+ }
+
+ p = p->next;
+ }
+}
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
new file mode 100644
index 0000000..fa1d0c7
--- /dev/null
+++ b/grub-core/kern/efi/efi.c
@@ -0,0 +1,742 @@
+/* efi.c - generic EFI support */
+/*
+ * 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/misc.h>
+#include <grub/charset.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/console_control.h>
+#include <grub/efi/pe32.h>
+#include <grub/machine/time.h>
+#include <grub/term.h>
+#include <grub/kernel.h>
+#include <grub/mm.h>
+
+/* The handle of GRUB itself. Filled in by the startup code. */
+grub_efi_handle_t grub_efi_image_handle;
+
+/* The pointer to a system table. Filled in by the startup code. */
+grub_efi_system_table_t *grub_efi_system_table;
+
+static grub_efi_guid_t console_control_guid = GRUB_EFI_CONSOLE_CONTROL_GUID;
+static grub_efi_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID;
+static grub_efi_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID;
+
+void *
+grub_efi_locate_protocol (grub_efi_guid_t *protocol, void *registration)
+{
+ void *interface;
+ grub_efi_status_t status;
+
+ status = efi_call_3 (grub_efi_system_table->boot_services->locate_protocol,
+ protocol, registration, &interface);
+ if (status != GRUB_EFI_SUCCESS)
+ return 0;
+
+ return interface;
+}
+
+/* Return the array of handles which meet the requirement. If successful,
+ the number of handles is stored in NUM_HANDLES. The array is allocated
+ from the heap. */
+grub_efi_handle_t *
+grub_efi_locate_handle (grub_efi_locate_search_type_t search_type,
+ grub_efi_guid_t *protocol,
+ void *search_key,
+ grub_efi_uintn_t *num_handles)
+{
+ grub_efi_boot_services_t *b;
+ grub_efi_status_t status;
+ grub_efi_handle_t *buffer;
+ grub_efi_uintn_t buffer_size = 8 * sizeof (grub_efi_handle_t);
+
+ buffer = grub_malloc (buffer_size);
+ if (! buffer)
+ return 0;
+
+ b = grub_efi_system_table->boot_services;
+ status = efi_call_5 (b->locate_handle, search_type, protocol, search_key,
+ &buffer_size, buffer);
+ if (status == GRUB_EFI_BUFFER_TOO_SMALL)
+ {
+ grub_free (buffer);
+ buffer = grub_malloc (buffer_size);
+ if (! buffer)
+ return 0;
+
+ status = efi_call_5 (b->locate_handle, search_type, protocol, search_key,
+ &buffer_size, buffer);
+ }
+
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_free (buffer);
+ return 0;
+ }
+
+ *num_handles = buffer_size / sizeof (grub_efi_handle_t);
+ return buffer;
+}
+
+void *
+grub_efi_open_protocol (grub_efi_handle_t handle,
+ grub_efi_guid_t *protocol,
+ grub_efi_uint32_t attributes)
+{
+ grub_efi_boot_services_t *b;
+ grub_efi_status_t status;
+ void *interface;
+
+ b = grub_efi_system_table->boot_services;
+ status = efi_call_6 (b->open_protocol, handle,
+ protocol,
+ &interface,
+ grub_efi_image_handle,
+ 0,
+ attributes);
+ if (status != GRUB_EFI_SUCCESS)
+ return 0;
+
+ return interface;
+}
+
+int
+grub_efi_set_text_mode (int on)
+{
+ grub_efi_console_control_protocol_t *c;
+ grub_efi_screen_mode_t mode, new_mode;
+
+ c = grub_efi_locate_protocol (&console_control_guid, 0);
+ if (! c)
+ /* No console control protocol instance available, assume it is
+ already in text mode. */
+ return 1;
+
+ if (efi_call_4 (c->get_mode, c, &mode, 0, 0) != GRUB_EFI_SUCCESS)
+ return 0;
+
+ new_mode = on ? GRUB_EFI_SCREEN_TEXT : GRUB_EFI_SCREEN_GRAPHICS;
+ if (mode != new_mode)
+ if (efi_call_2 (c->set_mode, c, new_mode) != GRUB_EFI_SUCCESS)
+ return 0;
+
+ return 1;
+}
+
+void
+grub_efi_stall (grub_efi_uintn_t microseconds)
+{
+ efi_call_1 (grub_efi_system_table->boot_services->stall, microseconds);
+}
+
+grub_efi_loaded_image_t *
+grub_efi_get_loaded_image (grub_efi_handle_t image_handle)
+{
+ return grub_efi_open_protocol (image_handle,
+ &loaded_image_guid,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+}
+
+void
+grub_exit (void)
+{
+ grub_efi_fini ();
+ efi_call_4 (grub_efi_system_table->boot_services->exit,
+ grub_efi_image_handle, GRUB_EFI_SUCCESS, 0, 0);
+ for (;;) ;
+}
+
+/* On i386, a firmware-independant grub_reboot() is provided by realmode.S. */
+#ifndef __i386__
+void
+grub_reboot (void)
+{
+ grub_efi_fini ();
+ efi_call_4 (grub_efi_system_table->runtime_services->reset_system,
+ GRUB_EFI_RESET_COLD, GRUB_EFI_SUCCESS, 0, NULL);
+ for (;;) ;
+}
+#endif
+
+grub_err_t
+grub_efi_set_virtual_address_map (grub_efi_uintn_t memory_map_size,
+ grub_efi_uintn_t descriptor_size,
+ grub_efi_uint32_t descriptor_version,
+ grub_efi_memory_descriptor_t *virtual_map)
+{
+ grub_efi_runtime_services_t *r;
+ grub_efi_status_t status;
+
+ r = grub_efi_system_table->runtime_services;
+ status = efi_call_4 (r->set_virtual_address_map, memory_map_size,
+ descriptor_size, descriptor_version, virtual_map);
+
+ if (status == GRUB_EFI_SUCCESS)
+ return GRUB_ERR_NONE;
+
+ return grub_error (GRUB_ERR_IO, "set_virtual_address_map failed");
+}
+
+grub_uint32_t
+grub_get_rtc (void)
+{
+ grub_efi_time_t time;
+ grub_efi_runtime_services_t *r;
+
+ r = grub_efi_system_table->runtime_services;
+ if (efi_call_2 (r->get_time, &time, 0) != GRUB_EFI_SUCCESS)
+ /* What is possible in this case? */
+ return 0;
+
+ return (((time.minute * 60 + time.second) * 1000
+ + time.nanosecond / 1000000)
+ * GRUB_TICKS_PER_SECOND / 1000);
+}
+
+/* Search the mods section from the PE32/PE32+ image. This code uses
+ a PE32 header, but should work with PE32+ as well. */
+grub_addr_t
+grub_arch_modules_addr (void)
+{
+ grub_efi_loaded_image_t *image;
+ struct grub_pe32_header *header;
+ struct grub_pe32_coff_header *coff_header;
+ struct grub_pe32_section_table *sections;
+ struct grub_pe32_section_table *section;
+ struct grub_module_info *info;
+ grub_uint16_t i;
+
+ image = grub_efi_get_loaded_image (grub_efi_image_handle);
+ if (! image)
+ return 0;
+
+ header = image->image_base;
+ coff_header = &(header->coff_header);
+ sections
+ = (struct grub_pe32_section_table *) ((char *) coff_header
+ + sizeof (*coff_header)
+ + coff_header->optional_header_size);
+
+ for (i = 0, section = sections;
+ i < coff_header->num_sections;
+ i++, section++)
+ {
+ if (grub_strcmp (section->name, "mods") == 0)
+ break;
+ }
+
+ if (i == coff_header->num_sections)
+ return 0;
+
+ info = (struct grub_module_info *) ((char *) image->image_base
+ + section->virtual_address);
+ if (info->magic != GRUB_MODULE_MAGIC)
+ return 0;
+
+ return (grub_addr_t) info;
+}
+
+char *
+grub_efi_get_filename (grub_efi_device_path_t *dp)
+{
+ char *name = 0;
+
+ while (1)
+ {
+ grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
+ grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
+
+ if (type == GRUB_EFI_END_DEVICE_PATH_TYPE)
+ break;
+ else if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
+ && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE)
+ {
+ grub_efi_file_path_device_path_t *fp;
+ grub_efi_uint16_t len;
+ char *p;
+ grub_size_t size;
+
+ if (name)
+ {
+ size = grub_strlen (name);
+ name[size] = '/';
+ size++;
+ }
+ else
+ size = 0;
+
+ len = ((GRUB_EFI_DEVICE_PATH_LENGTH (dp) - 4)
+ / sizeof (grub_efi_char16_t));
+ p = grub_realloc (name, size + len * 4 + 1);
+ if (! p)
+ {
+ grub_free (name);
+ return 0;
+ }
+
+ name = p;
+ fp = (grub_efi_file_path_device_path_t *) dp;
+ *grub_utf16_to_utf8 ((grub_uint8_t *) name + size,
+ fp->path_name, len) = '\0';
+ }
+
+ dp = GRUB_EFI_NEXT_DEVICE_PATH (dp);
+ }
+
+ if (name)
+ {
+ /* EFI breaks paths with backslashes. */
+ char *p;
+
+ for (p = name; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+ }
+
+ return name;
+}
+
+grub_efi_device_path_t *
+grub_efi_get_device_path (grub_efi_handle_t handle)
+{
+ return grub_efi_open_protocol (handle, &device_path_guid,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+}
+
+/* Print the chain of Device Path nodes. This is mainly for debugging. */
+void
+grub_efi_print_device_path (grub_efi_device_path_t *dp)
+{
+ while (1)
+ {
+ grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
+ grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
+ grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+
+ switch (type)
+ {
+ case GRUB_EFI_END_DEVICE_PATH_TYPE:
+ switch (subtype)
+ {
+ case GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE:
+ grub_printf ("/EndEntire\n");
+ //grub_putchar ('\n');
+ break;
+ case GRUB_EFI_END_THIS_DEVICE_PATH_SUBTYPE:
+ grub_printf ("/EndThis\n");
+ //grub_putchar ('\n');
+ break;
+ default:
+ grub_printf ("/EndUnknown(%x)\n", (unsigned) subtype);
+ break;
+ }
+ break;
+
+ case GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE:
+ switch (subtype)
+ {
+ case GRUB_EFI_PCI_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_pci_device_path_t pci;
+ grub_memcpy (&pci, dp, len);
+ grub_printf ("/PCI(%x,%x)",
+ (unsigned) pci.function, (unsigned) pci.device);
+ }
+ break;
+ case GRUB_EFI_PCCARD_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_pccard_device_path_t pccard;
+ grub_memcpy (&pccard, dp, len);
+ grub_printf ("/PCCARD(%x)",
+ (unsigned) pccard.function);
+ }
+ break;
+ case GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_memory_mapped_device_path_t mmapped;
+ grub_memcpy (&mmapped, dp, len);
+ grub_printf ("/MMap(%x,%llx,%llx)",
+ (unsigned) mmapped.memory_type,
+ (unsigned long long) mmapped.start_address,
+ (unsigned long long) mmapped.end_address);
+ }
+ break;
+ case GRUB_EFI_VENDOR_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_vendor_device_path_t vendor;
+ grub_memcpy (&vendor, dp, sizeof (vendor));
+ grub_printf ("/Vendor(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)",
+ (unsigned) vendor.vendor_guid.data1,
+ (unsigned) vendor.vendor_guid.data2,
+ (unsigned) vendor.vendor_guid.data3,
+ (unsigned) vendor.vendor_guid.data4[0],
+ (unsigned) vendor.vendor_guid.data4[1],
+ (unsigned) vendor.vendor_guid.data4[2],
+ (unsigned) vendor.vendor_guid.data4[3],
+ (unsigned) vendor.vendor_guid.data4[4],
+ (unsigned) vendor.vendor_guid.data4[5],
+ (unsigned) vendor.vendor_guid.data4[6],
+ (unsigned) vendor.vendor_guid.data4[7]);
+ }
+ break;
+ case GRUB_EFI_CONTROLLER_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_controller_device_path_t controller;
+ grub_memcpy (&controller, dp, len);
+ grub_printf ("/Ctrl(%x)",
+ (unsigned) controller.controller_number);
+ }
+ break;
+ default:
+ grub_printf ("/UnknownHW(%x)", (unsigned) subtype);
+ break;
+ }
+ break;
+
+ case GRUB_EFI_ACPI_DEVICE_PATH_TYPE:
+ switch (subtype)
+ {
+ case GRUB_EFI_ACPI_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_acpi_device_path_t acpi;
+ grub_memcpy (&acpi, dp, len);
+ grub_printf ("/ACPI(%x,%x)",
+ (unsigned) acpi.hid,
+ (unsigned) acpi.uid);
+ }
+ break;
+ case GRUB_EFI_EXPANDED_ACPI_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_expanded_acpi_device_path_t eacpi;
+ grub_memcpy (&eacpi, dp, sizeof (eacpi));
+ grub_printf ("/ACPI(");
+
+ if (GRUB_EFI_EXPANDED_ACPI_HIDSTR (dp)[0] == '\0')
+ grub_printf ("%x,", (unsigned) eacpi.hid);
+ else
+ grub_printf ("%s,", GRUB_EFI_EXPANDED_ACPI_HIDSTR (dp));
+
+ if (GRUB_EFI_EXPANDED_ACPI_UIDSTR (dp)[0] == '\0')
+ grub_printf ("%x,", (unsigned) eacpi.uid);
+ else
+ grub_printf ("%s,", GRUB_EFI_EXPANDED_ACPI_UIDSTR (dp));
+
+ if (GRUB_EFI_EXPANDED_ACPI_CIDSTR (dp)[0] == '\0')
+ grub_printf ("%x)", (unsigned) eacpi.cid);
+ else
+ grub_printf ("%s)", GRUB_EFI_EXPANDED_ACPI_CIDSTR (dp));
+ }
+ break;
+ default:
+ grub_printf ("/UnknownACPI(%x)", (unsigned) subtype);
+ break;
+ }
+ break;
+
+ case GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE:
+ switch (subtype)
+ {
+ case GRUB_EFI_ATAPI_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_atapi_device_path_t atapi;
+ grub_memcpy (&atapi, dp, len);
+ grub_printf ("/ATAPI(%x,%x,%x)",
+ (unsigned) atapi.primary_secondary,
+ (unsigned) atapi.slave_master,
+ (unsigned) atapi.lun);
+ }
+ break;
+ case GRUB_EFI_SCSI_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_scsi_device_path_t scsi;
+ grub_memcpy (&scsi, dp, len);
+ grub_printf ("/SCSI(%x,%x)",
+ (unsigned) scsi.pun,
+ (unsigned) scsi.lun);
+ }
+ break;
+ case GRUB_EFI_FIBRE_CHANNEL_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_fibre_channel_device_path_t fc;
+ grub_memcpy (&fc, dp, len);
+ grub_printf ("/FibreChannel(%llx,%llx)",
+ (unsigned long long) fc.wwn,
+ (unsigned long long) fc.lun);
+ }
+ break;
+ case GRUB_EFI_1394_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_1394_device_path_t firewire;
+ grub_memcpy (&firewire, dp, len);
+ grub_printf ("/1394(%llx)", (unsigned long long) firewire.guid);
+ }
+ break;
+ case GRUB_EFI_USB_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_usb_device_path_t usb;
+ grub_memcpy (&usb, dp, len);
+ grub_printf ("/USB(%x,%x)",
+ (unsigned) usb.parent_port_number,
+ (unsigned) usb.interface);
+ }
+ break;
+ case GRUB_EFI_USB_CLASS_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_usb_class_device_path_t usb_class;
+ grub_memcpy (&usb_class, dp, len);
+ grub_printf ("/USBClass(%x,%x,%x,%x,%x)",
+ (unsigned) usb_class.vendor_id,
+ (unsigned) usb_class.product_id,
+ (unsigned) usb_class.device_class,
+ (unsigned) usb_class.device_subclass,
+ (unsigned) usb_class.device_protocol);
+ }
+ break;
+ case GRUB_EFI_I2O_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_i2o_device_path_t i2o;
+ grub_memcpy (&i2o, dp, len);
+ grub_printf ("/I2O(%x)", (unsigned) i2o.tid);
+ }
+ break;
+ case GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_mac_address_device_path_t mac;
+ grub_memcpy (&mac, dp, len);
+ grub_printf ("/MacAddr(%02x:%02x:%02x:%02x:%02x:%02x,%x)",
+ (unsigned) mac.mac_address[0],
+ (unsigned) mac.mac_address[1],
+ (unsigned) mac.mac_address[2],
+ (unsigned) mac.mac_address[3],
+ (unsigned) mac.mac_address[4],
+ (unsigned) mac.mac_address[5],
+ (unsigned) mac.if_type);
+ }
+ break;
+ case GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_ipv4_device_path_t ipv4;
+ grub_memcpy (&ipv4, dp, len);
+ grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)",
+ (unsigned) ipv4.local_ip_address[0],
+ (unsigned) ipv4.local_ip_address[1],
+ (unsigned) ipv4.local_ip_address[2],
+ (unsigned) ipv4.local_ip_address[3],
+ (unsigned) ipv4.remote_ip_address[0],
+ (unsigned) ipv4.remote_ip_address[1],
+ (unsigned) ipv4.remote_ip_address[2],
+ (unsigned) ipv4.remote_ip_address[3],
+ (unsigned) ipv4.local_port,
+ (unsigned) ipv4.remote_port,
+ (unsigned) ipv4.protocol,
+ (unsigned) ipv4.static_ip_address);
+ }
+ break;
+ case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_ipv6_device_path_t ipv6;
+ grub_memcpy (&ipv6, dp, len);
+ grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)",
+ (unsigned) ipv6.local_ip_address[0],
+ (unsigned) ipv6.local_ip_address[1],
+ (unsigned) ipv6.local_ip_address[2],
+ (unsigned) ipv6.local_ip_address[3],
+ (unsigned) ipv6.local_ip_address[4],
+ (unsigned) ipv6.local_ip_address[5],
+ (unsigned) ipv6.local_ip_address[6],
+ (unsigned) ipv6.local_ip_address[7],
+ (unsigned) ipv6.remote_ip_address[0],
+ (unsigned) ipv6.remote_ip_address[1],
+ (unsigned) ipv6.remote_ip_address[2],
+ (unsigned) ipv6.remote_ip_address[3],
+ (unsigned) ipv6.remote_ip_address[4],
+ (unsigned) ipv6.remote_ip_address[5],
+ (unsigned) ipv6.remote_ip_address[6],
+ (unsigned) ipv6.remote_ip_address[7],
+ (unsigned) ipv6.local_port,
+ (unsigned) ipv6.remote_port,
+ (unsigned) ipv6.protocol,
+ (unsigned) ipv6.static_ip_address);
+ }
+ break;
+ case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_infiniband_device_path_t ib;
+ grub_memcpy (&ib, dp, len);
+ grub_printf ("/InfiniBand(%x,%llx,%llx,%llx)",
+ (unsigned) ib.port_gid[0], /* XXX */
+ (unsigned long long) ib.remote_id,
+ (unsigned long long) ib.target_port_id,
+ (unsigned long long) ib.device_id);
+ }
+ break;
+ case GRUB_EFI_UART_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_uart_device_path_t uart;
+ grub_memcpy (&uart, dp, len);
+ grub_printf ("/UART(%llu,%u,%x,%x)",
+ (unsigned long long) uart.baud_rate,
+ uart.data_bits,
+ uart.parity,
+ uart.stop_bits);
+ }
+ break;
+ case GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_vendor_messaging_device_path_t vendor;
+ grub_memcpy (&vendor, dp, sizeof (vendor));
+ grub_printf ("/Vendor(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)",
+ (unsigned) vendor.vendor_guid.data1,
+ (unsigned) vendor.vendor_guid.data2,
+ (unsigned) vendor.vendor_guid.data3,
+ (unsigned) vendor.vendor_guid.data4[0],
+ (unsigned) vendor.vendor_guid.data4[1],
+ (unsigned) vendor.vendor_guid.data4[2],
+ (unsigned) vendor.vendor_guid.data4[3],
+ (unsigned) vendor.vendor_guid.data4[4],
+ (unsigned) vendor.vendor_guid.data4[5],
+ (unsigned) vendor.vendor_guid.data4[6],
+ (unsigned) vendor.vendor_guid.data4[7]);
+ }
+ break;
+ default:
+ grub_printf ("/UnknownMessaging(%x)", (unsigned) subtype);
+ break;
+ }
+ break;
+
+ case GRUB_EFI_MEDIA_DEVICE_PATH_TYPE:
+ switch (subtype)
+ {
+ case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_hard_drive_device_path_t hd;
+ grub_memcpy (&hd, dp, len);
+ grub_printf ("/HD(%u,%llx,%llx,%02x%02x%02x%02x%02x%02x%02x%02x,%x,%x)",
+ hd.partition_number,
+ (unsigned long long) hd.partition_start,
+ (unsigned long long) hd.partition_size,
+ (unsigned) hd.partition_signature[0],
+ (unsigned) hd.partition_signature[1],
+ (unsigned) hd.partition_signature[2],
+ (unsigned) hd.partition_signature[3],
+ (unsigned) hd.partition_signature[4],
+ (unsigned) hd.partition_signature[5],
+ (unsigned) hd.partition_signature[6],
+ (unsigned) hd.partition_signature[7],
+ (unsigned) hd.mbr_type,
+ (unsigned) hd.signature_type);
+ }
+ break;
+ case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_cdrom_device_path_t cd;
+ grub_memcpy (&cd, dp, len);
+ grub_printf ("/CD(%u,%llx,%llx)",
+ cd.boot_entry,
+ (unsigned long long) cd.partition_start,
+ (unsigned long long) cd.partition_size);
+ }
+ break;
+ case GRUB_EFI_VENDOR_MEDIA_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_vendor_media_device_path_t vendor;
+ grub_memcpy (&vendor, dp, sizeof (vendor));
+ grub_printf ("/Vendor(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)",
+ (unsigned) vendor.vendor_guid.data1,
+ (unsigned) vendor.vendor_guid.data2,
+ (unsigned) vendor.vendor_guid.data3,
+ (unsigned) vendor.vendor_guid.data4[0],
+ (unsigned) vendor.vendor_guid.data4[1],
+ (unsigned) vendor.vendor_guid.data4[2],
+ (unsigned) vendor.vendor_guid.data4[3],
+ (unsigned) vendor.vendor_guid.data4[4],
+ (unsigned) vendor.vendor_guid.data4[5],
+ (unsigned) vendor.vendor_guid.data4[6],
+ (unsigned) vendor.vendor_guid.data4[7]);
+ }
+ break;
+ case GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_file_path_device_path_t *fp;
+ grub_uint8_t buf[(len - 4) * 2 + 1];
+ fp = (grub_efi_file_path_device_path_t *) dp;
+ *grub_utf16_to_utf8 (buf, fp->path_name,
+ (len - 4) / sizeof (grub_efi_char16_t))
+ = '\0';
+ grub_printf ("/File(%s)", buf);
+ }
+ break;
+ case GRUB_EFI_PROTOCOL_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_protocol_device_path_t proto;
+ grub_memcpy (&proto, dp, sizeof (proto));
+ grub_printf ("/Protocol(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)",
+ (unsigned) proto.guid.data1,
+ (unsigned) proto.guid.data2,
+ (unsigned) proto.guid.data3,
+ (unsigned) proto.guid.data4[0],
+ (unsigned) proto.guid.data4[1],
+ (unsigned) proto.guid.data4[2],
+ (unsigned) proto.guid.data4[3],
+ (unsigned) proto.guid.data4[4],
+ (unsigned) proto.guid.data4[5],
+ (unsigned) proto.guid.data4[6],
+ (unsigned) proto.guid.data4[7]);
+ }
+ break;
+ default:
+ grub_printf ("/UnknownMedia(%x)", (unsigned) subtype);
+ break;
+ }
+ break;
+
+ case GRUB_EFI_BIOS_DEVICE_PATH_TYPE:
+ switch (subtype)
+ {
+ case GRUB_EFI_BIOS_DEVICE_PATH_SUBTYPE:
+ {
+ grub_efi_bios_device_path_t bios;
+ grub_memcpy (&bios, dp, sizeof (bios));
+ grub_printf ("/BIOS(%x,%x,%s)",
+ (unsigned) bios.device_type,
+ (unsigned) bios.status_flags,
+ (char *) (dp + 1));
+ }
+ break;
+ default:
+ grub_printf ("/UnknownBIOS(%x)", (unsigned) subtype);
+ break;
+ }
+ break;
+
+ default:
+ grub_printf ("/UnknownType(%x,%x)\n",
+ (unsigned) type,
+ (unsigned) subtype);
+ return;
+ break;
+ }
+
+ if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
+ break;
+
+ dp = (grub_efi_device_path_t *) ((char *) dp + len);
+ }
+}
diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
new file mode 100644
index 0000000..1b0a872
--- /dev/null
+++ b/grub-core/kern/efi/init.c
@@ -0,0 +1,130 @@
+/* init.c - generic EFI initialization and finalization */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,2007 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/efi/efi.h>
+#include <grub/efi/console.h>
+#include <grub/efi/disk.h>
+#include <grub/term.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/mm.h>
+#include <grub/kernel.h>
+
+void
+grub_efi_init (void)
+{
+ /* First of all, initialize the console so that GRUB can display
+ messages. */
+ grub_console_init ();
+
+ /* Initialize the memory management system. */
+ grub_efi_mm_init ();
+
+ efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer,
+ 0, 0, 0, NULL);
+
+ grub_efidisk_init ();
+}
+
+void
+grub_efi_set_prefix (void)
+{
+ grub_efi_loaded_image_t *image = NULL;
+ char *device = NULL;
+ char *path = NULL;
+
+ {
+ char *pptr = NULL;
+ if (grub_prefix[0] == '(')
+ {
+ pptr = grub_strrchr (grub_prefix, ')');
+ if (pptr)
+ {
+ device = grub_strndup (grub_prefix + 1, pptr - grub_prefix - 1);
+ pptr++;
+ }
+ }
+ if (!pptr)
+ pptr = grub_prefix;
+ if (pptr[0])
+ path = grub_strdup (pptr);
+ }
+
+ if ((!device || device[0] == ',' || !device[0]) || !path)
+ image = grub_efi_get_loaded_image (grub_efi_image_handle);
+ if (image)
+ {
+ if (!device)
+ device = grub_efidisk_get_device_name (image->device_handle);
+ else if (device[0] == ',' || !device[0])
+ {
+ /* We have a partition, but still need to fill in the drive. */
+ char *image_device, *comma, *new_device;
+
+ image_device = grub_efidisk_get_device_name (image->device_handle);
+ comma = grub_strchr (image_device, ',');
+ if (comma)
+ {
+ char *drive = grub_strndup (image_device, comma - image_device);
+ new_device = grub_xasprintf ("%s%s", drive, device);
+ grub_free (drive);
+ }
+ else
+ new_device = grub_xasprintf ("%s%s", image_device, device);
+
+ grub_free (image_device);
+ grub_free (device);
+ device = new_device;
+ }
+ }
+
+ if (image && !path)
+ {
+ char *p;
+
+ path = grub_efi_get_filename (image->file_path);
+
+ /* Get the directory. */
+ p = grub_strrchr (path, '/');
+ if (p)
+ *p = '\0';
+ }
+
+ if (device && path)
+ {
+ char *prefix;
+
+ prefix = grub_xasprintf ("(%s)%s", device, path);
+ if (prefix)
+ {
+ grub_env_set ("prefix", prefix);
+ grub_free (prefix);
+ }
+ }
+
+ grub_free (device);
+ grub_free (path);
+}
+
+void
+grub_efi_fini (void)
+{
+ grub_efidisk_fini ();
+ grub_console_fini ();
+}
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
new file mode 100644
index 0000000..8b9e6ec
--- /dev/null
+++ b/grub-core/kern/efi/mm.c
@@ -0,0 +1,453 @@
+/* mm.c - generic EFI memory management */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+
+#define NEXT_MEMORY_DESCRIPTOR(desc, size) \
+ ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
+
+#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12)
+#define PAGES_TO_BYTES(pages) ((pages) << 12)
+
+/* The size of a memory map obtained from the firmware. This must be
+ a multiplier of 4KB. */
+#define MEMORY_MAP_SIZE 0x3000
+
+/* The minimum and maximum heap size for GRUB itself. */
+#define MIN_HEAP_SIZE 0x100000
+#define MAX_HEAP_SIZE (1600 * 0x100000)
+
+static void *finish_mmap_buf = 0;
+static grub_efi_uintn_t finish_mmap_size = 0;
+static grub_efi_uintn_t finish_key = 0;
+static grub_efi_uintn_t finish_desc_size;
+static grub_efi_uint32_t finish_desc_version;
+int grub_efi_is_finished = 0;
+
+/* Allocate pages. Return the pointer to the first of allocated pages. */
+void *
+grub_efi_allocate_pages (grub_efi_physical_address_t address,
+ grub_efi_uintn_t pages)
+{
+ grub_efi_allocate_type_t type;
+ grub_efi_status_t status;
+ grub_efi_boot_services_t *b;
+
+#if 1
+ /* Limit the memory access to less than 4GB for 32-bit platforms. */
+ if (address > 0xffffffff)
+ return 0;
+#endif
+
+#if 1
+ if (address == 0)
+ {
+ type = GRUB_EFI_ALLOCATE_MAX_ADDRESS;
+ address = 0xffffffff;
+ }
+ else
+ type = GRUB_EFI_ALLOCATE_ADDRESS;
+#else
+ if (address == 0)
+ type = GRUB_EFI_ALLOCATE_ANY_PAGES;
+ else
+ type = GRUB_EFI_ALLOCATE_ADDRESS;
+#endif
+
+ b = grub_efi_system_table->boot_services;
+ status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address);
+ if (status != GRUB_EFI_SUCCESS)
+ return 0;
+
+ if (address == 0)
+ {
+ /* Uggh, the address 0 was allocated... This is too annoying,
+ so reallocate another one. */
+ address = 0xffffffff;
+ status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address);
+ grub_efi_free_pages (0, pages);
+ if (status != GRUB_EFI_SUCCESS)
+ return 0;
+ }
+
+ return (void *) ((grub_addr_t) address);
+}
+
+/* Free pages starting from ADDRESS. */
+void
+grub_efi_free_pages (grub_efi_physical_address_t address,
+ grub_efi_uintn_t pages)
+{
+ grub_efi_boot_services_t *b;
+
+ b = grub_efi_system_table->boot_services;
+ efi_call_2 (b->free_pages, address, pages);
+}
+
+grub_err_t
+grub_efi_finish_boot_services (grub_efi_uintn_t *outbuf_size, void *outbuf,
+ grub_efi_uintn_t *map_key,
+ grub_efi_uintn_t *efi_desc_size,
+ grub_efi_uint32_t *efi_desc_version)
+{
+ grub_efi_boot_services_t *b;
+ grub_efi_status_t status;
+
+ if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
+ &finish_desc_size, &finish_desc_version) < 0)
+ return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
+
+ if (outbuf && *outbuf_size < finish_mmap_size)
+ return grub_error (GRUB_ERR_IO, "memory map buffer is too small");
+
+ finish_mmap_buf = grub_malloc (finish_mmap_size);
+ if (!finish_mmap_buf)
+ return grub_errno;
+
+ if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
+ &finish_desc_size, &finish_desc_version) <= 0)
+ return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
+
+ b = grub_efi_system_table->boot_services;
+ status = efi_call_2 (b->exit_boot_services, grub_efi_image_handle,
+ finish_key);
+ if (status != GRUB_EFI_SUCCESS)
+ return grub_error (GRUB_ERR_IO, "couldn't terminate EFI services");
+
+ grub_efi_is_finished = 1;
+ if (outbuf_size)
+ *outbuf_size = finish_mmap_size;
+ if (outbuf)
+ grub_memcpy (outbuf, finish_mmap_buf, finish_mmap_size);
+ if (map_key)
+ *map_key = finish_key;
+ if (efi_desc_size)
+ *efi_desc_size = finish_desc_size;
+ if (efi_desc_version)
+ *efi_desc_version = finish_desc_version;
+
+ return GRUB_ERR_NONE;
+}
+
+/* Get the memory map as defined in the EFI spec. Return 1 if successful,
+ return 0 if partial, or return -1 if an error occurs. */
+int
+grub_efi_get_memory_map (grub_efi_uintn_t *memory_map_size,
+ grub_efi_memory_descriptor_t *memory_map,
+ grub_efi_uintn_t *map_key,
+ grub_efi_uintn_t *descriptor_size,
+ grub_efi_uint32_t *descriptor_version)
+{
+ grub_efi_status_t status;
+ grub_efi_boot_services_t *b;
+ grub_efi_uintn_t key;
+ grub_efi_uint32_t version;
+
+ if (grub_efi_is_finished)
+ {
+ int ret = 1;
+ if (*memory_map_size < finish_mmap_size)
+ {
+ grub_memcpy (memory_map, finish_mmap_buf, *memory_map_size);
+ ret = 0;
+ }
+ else
+ {
+ grub_memcpy (memory_map, finish_mmap_buf, finish_mmap_size);
+ ret = 1;
+ }
+ *memory_map_size = finish_mmap_size;
+ if (map_key)
+ *map_key = finish_key;
+ if (descriptor_size)
+ *descriptor_size = finish_desc_size;
+ if (descriptor_version)
+ *descriptor_version = finish_desc_version;
+ return ret;
+ }
+
+ /* Allow some parameters to be missing. */
+ if (! map_key)
+ map_key = &key;
+ if (! descriptor_version)
+ descriptor_version = &version;
+
+ b = grub_efi_system_table->boot_services;
+ status = efi_call_5 (b->get_memory_map, memory_map_size, memory_map, map_key,
+ descriptor_size, descriptor_version);
+ if (status == GRUB_EFI_SUCCESS)
+ return 1;
+ else if (status == GRUB_EFI_BUFFER_TOO_SMALL)
+ return 0;
+ else
+ return -1;
+}
+
+/* Sort the memory map in place. */
+static void
+sort_memory_map (grub_efi_memory_descriptor_t *memory_map,
+ grub_efi_uintn_t desc_size,
+ grub_efi_memory_descriptor_t *memory_map_end)
+{
+ grub_efi_memory_descriptor_t *d1;
+ grub_efi_memory_descriptor_t *d2;
+
+ for (d1 = memory_map;
+ d1 < memory_map_end;
+ d1 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size))
+ {
+ grub_efi_memory_descriptor_t *max_desc = d1;
+
+ for (d2 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size);
+ d2 < memory_map_end;
+ d2 = NEXT_MEMORY_DESCRIPTOR (d2, desc_size))
+ {
+ if (max_desc->num_pages < d2->num_pages)
+ max_desc = d2;
+ }
+
+ if (max_desc != d1)
+ {
+ grub_efi_memory_descriptor_t tmp;
+
+ tmp = *d1;
+ *d1 = *max_desc;
+ *max_desc = tmp;
+ }
+ }
+}
+
+/* Filter the descriptors. GRUB needs only available memory. */
+static grub_efi_memory_descriptor_t *
+filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
+ grub_efi_memory_descriptor_t *filtered_memory_map,
+ grub_efi_uintn_t desc_size,
+ grub_efi_memory_descriptor_t *memory_map_end)
+{
+ grub_efi_memory_descriptor_t *desc;
+ grub_efi_memory_descriptor_t *filtered_desc;
+
+ for (desc = memory_map, filtered_desc = filtered_memory_map;
+ desc < memory_map_end;
+ desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
+ {
+ if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
+#if 1
+ && desc->physical_start <= 0xffffffff
+#endif
+ && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000
+ && desc->num_pages != 0)
+ {
+ grub_memcpy (filtered_desc, desc, desc_size);
+
+ /* Avoid less than 1MB, because some loaders seem to be confused. */
+ if (desc->physical_start < 0x100000)
+ {
+ desc->num_pages -= BYTES_TO_PAGES (0x100000
+ - desc->physical_start);
+ desc->physical_start = 0x100000;
+ }
+
+#if 1
+ if (BYTES_TO_PAGES (filtered_desc->physical_start)
+ + filtered_desc->num_pages
+ > BYTES_TO_PAGES (0x100000000LL))
+ filtered_desc->num_pages
+ = (BYTES_TO_PAGES (0x100000000LL)
+ - BYTES_TO_PAGES (filtered_desc->physical_start));
+#endif
+
+ if (filtered_desc->num_pages == 0)
+ continue;
+
+ filtered_desc = NEXT_MEMORY_DESCRIPTOR (filtered_desc, desc_size);
+ }
+ }
+
+ return filtered_desc;
+}
+
+/* Return the total number of pages. */
+static grub_efi_uint64_t
+get_total_pages (grub_efi_memory_descriptor_t *memory_map,
+ grub_efi_uintn_t desc_size,
+ grub_efi_memory_descriptor_t *memory_map_end)
+{
+ grub_efi_memory_descriptor_t *desc;
+ grub_efi_uint64_t total = 0;
+
+ for (desc = memory_map;
+ desc < memory_map_end;
+ desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
+ total += desc->num_pages;
+
+ return total;
+}
+
+/* Add memory regions. */
+static void
+add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
+ grub_efi_uintn_t desc_size,
+ grub_efi_memory_descriptor_t *memory_map_end,
+ grub_efi_uint64_t required_pages)
+{
+ grub_efi_memory_descriptor_t *desc;
+
+ for (desc = memory_map;
+ desc < memory_map_end;
+ desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
+ {
+ grub_efi_uint64_t pages;
+ grub_efi_physical_address_t start;
+ void *addr;
+
+ start = desc->physical_start;
+ pages = desc->num_pages;
+ if (pages > required_pages)
+ {
+ start += PAGES_TO_BYTES (pages - required_pages);
+ pages = required_pages;
+ }
+
+ addr = grub_efi_allocate_pages (start, pages);
+ if (! addr)
+ grub_fatal ("cannot allocate conventional memory %p with %u pages",
+ (void *) ((grub_addr_t) start),
+ (unsigned) pages);
+
+ grub_mm_init_region (addr, PAGES_TO_BYTES (pages));
+
+ required_pages -= pages;
+ if (required_pages == 0)
+ break;
+ }
+
+ if (required_pages > 0)
+ grub_fatal ("too little memory");
+}
+
+#if 0
+/* Print the memory map. */
+static void
+print_memory_map (grub_efi_memory_descriptor_t *memory_map,
+ grub_efi_uintn_t desc_size,
+ grub_efi_memory_descriptor_t *memory_map_end)
+{
+ grub_efi_memory_descriptor_t *desc;
+ int i;
+
+ for (desc = memory_map, i = 0;
+ desc < memory_map_end;
+ desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size), i++)
+ {
+ grub_printf ("MD: t=%x, p=%llx, v=%llx, n=%llx, a=%llx\n",
+ desc->type, desc->physical_start, desc->virtual_start,
+ desc->num_pages, desc->attribute);
+ }
+}
+#endif
+
+void
+grub_efi_mm_init (void)
+{
+ grub_efi_memory_descriptor_t *memory_map;
+ grub_efi_memory_descriptor_t *memory_map_end;
+ grub_efi_memory_descriptor_t *filtered_memory_map;
+ grub_efi_memory_descriptor_t *filtered_memory_map_end;
+ grub_efi_uintn_t map_size;
+ grub_efi_uintn_t desc_size;
+ grub_efi_uint64_t total_pages;
+ grub_efi_uint64_t required_pages;
+ int mm_status;
+
+ /* Prepare a memory region to store two memory maps. */
+ memory_map = grub_efi_allocate_pages (0,
+ 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
+ if (! memory_map)
+ grub_fatal ("cannot allocate memory");
+
+ /* Obtain descriptors for available memory. */
+ map_size = MEMORY_MAP_SIZE;
+
+ mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0);
+
+ if (mm_status == 0)
+ {
+ grub_efi_free_pages
+ ((grub_efi_physical_address_t) ((grub_addr_t) memory_map),
+ 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
+
+ /* Freeing/allocating operations may increase memory map size. */
+ map_size += desc_size * 32;
+
+ memory_map = grub_efi_allocate_pages (0, 2 * BYTES_TO_PAGES (map_size));
+ if (! memory_map)
+ grub_fatal ("cannot allocate memory");
+
+ mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0,
+ &desc_size, 0);
+ }
+
+ if (mm_status < 0)
+ grub_fatal ("cannot get memory map");
+
+ memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size);
+
+ filtered_memory_map = memory_map_end;
+
+ filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map,
+ desc_size, memory_map_end);
+
+ /* By default, request a quarter of the available memory. */
+ total_pages = get_total_pages (filtered_memory_map, desc_size,
+ filtered_memory_map_end);
+ required_pages = (total_pages >> 2);
+ if (required_pages < BYTES_TO_PAGES (MIN_HEAP_SIZE))
+ required_pages = BYTES_TO_PAGES (MIN_HEAP_SIZE);
+ else if (required_pages > BYTES_TO_PAGES (MAX_HEAP_SIZE))
+ required_pages = BYTES_TO_PAGES (MAX_HEAP_SIZE);
+
+ /* Sort the filtered descriptors, so that GRUB can allocate pages
+ from smaller regions. */
+ sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end);
+
+ /* Allocate memory regions for GRUB's memory management. */
+ add_memory_regions (filtered_memory_map, desc_size,
+ filtered_memory_map_end, required_pages);
+
+#if 0
+ /* For debug. */
+ map_size = MEMORY_MAP_SIZE;
+
+ if (grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0) < 0)
+ grub_fatal ("cannot get memory map");
+
+ grub_printf ("printing memory map\n");
+ print_memory_map (memory_map, desc_size,
+ NEXT_MEMORY_DESCRIPTOR (memory_map, map_size));
+ grub_abort ();
+#endif
+
+ /* Release the memory maps. */
+ grub_efi_free_pages ((grub_addr_t) memory_map,
+ 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
+}
diff --git a/grub-core/kern/elf.c b/grub-core/kern/elf.c
new file mode 100644
index 0000000..9c7b8ce
--- /dev/null
+++ b/grub-core/kern/elf.c
@@ -0,0 +1,488 @@
+/* elf.c - load ELF files */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/err.h>
+#include <grub/elf.h>
+#include <grub/elfload.h>
+#include <grub/file.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/dl.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Check if EHDR is a valid ELF header. */
+static grub_err_t
+grub_elf_check_header (grub_elf_t elf)
+{
+ Elf32_Ehdr *e = &elf->ehdr.ehdr32;
+
+ 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");
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_elf_close (grub_elf_t elf)
+{
+ grub_file_t file = elf->file;
+
+ grub_free (elf->phdrs);
+ grub_free (elf);
+
+ if (file)
+ grub_file_close (file);
+
+ return grub_errno;
+}
+
+grub_elf_t
+grub_elf_file (grub_file_t file)
+{
+ grub_elf_t elf;
+
+ elf = grub_zalloc (sizeof (*elf));
+ if (! elf)
+ return 0;
+
+ elf->file = file;
+
+ if (grub_file_seek (elf->file, 0) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (elf->file, &elf->ehdr, sizeof (elf->ehdr))
+ != sizeof (elf->ehdr))
+ {
+ grub_error_push ();
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read ELF header");
+ goto fail;
+ }
+
+ if (grub_elf_check_header (elf))
+ goto fail;
+
+ return elf;
+
+fail:
+ grub_free (elf->phdrs);
+ grub_free (elf);
+ return 0;
+}
+
+grub_elf_t
+grub_elf_open (const char *name)
+{
+ grub_file_t file;
+ grub_elf_t elf;
+
+ file = grub_file_open (name);
+ if (! file)
+ return 0;
+
+ elf = grub_elf_file (file);
+ if (! elf)
+ grub_file_close (file);
+
+ return elf;
+}
+
+
+/* 32-bit */
+
+int
+grub_elf_is_elf32 (grub_elf_t elf)
+{
+ return elf->ehdr.ehdr32.e_ident[EI_CLASS] == ELFCLASS32;
+}
+
+static grub_err_t
+grub_elf32_load_phdrs (grub_elf_t elf)
+{
+ grub_ssize_t phdrs_size;
+
+ phdrs_size = elf->ehdr.ehdr32.e_phnum * elf->ehdr.ehdr32.e_phentsize;
+
+ grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
+ (unsigned long long) elf->ehdr.ehdr32.e_phoff,
+ (unsigned long) phdrs_size);
+
+ elf->phdrs = grub_malloc (phdrs_size);
+ if (! elf->phdrs)
+ return grub_errno;
+
+ if ((grub_file_seek (elf->file, elf->ehdr.ehdr32.e_phoff) == (grub_off_t) -1)
+ || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size))
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_READ_ERROR, "cannot read program headers");
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_elf32_phdr_iterate (grub_elf_t elf,
+ int NESTED_FUNC_ATTR (*hook) (grub_elf_t, Elf32_Phdr *, void *),
+ void *hook_arg)
+{
+ Elf32_Phdr *phdrs;
+ unsigned int i;
+
+ if (! elf->phdrs)
+ if (grub_elf32_load_phdrs (elf))
+ return grub_errno;
+ phdrs = elf->phdrs;
+
+ for (i = 0; i < elf->ehdr.ehdr32.e_phnum; i++)
+ {
+ Elf32_Phdr *phdr = phdrs + i;
+ grub_dprintf ("elf",
+ "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
+ "filesz %lx\n",
+ i, phdr->p_type,
+ (unsigned long) phdr->p_paddr,
+ (unsigned long) phdr->p_memsz,
+ (unsigned long) phdr->p_filesz);
+ if (hook (elf, phdr, hook_arg))
+ break;
+ }
+
+ return grub_errno;
+}
+
+/* Calculate the amount of memory spanned by the segments. */
+grub_size_t
+grub_elf32_size (grub_elf_t elf, Elf32_Addr *base, grub_uint32_t *max_align)
+{
+ Elf32_Addr segments_start = (Elf32_Addr) -1;
+ Elf32_Addr segments_end = 0;
+ int nr_phdrs = 0;
+ grub_uint32_t curr_align = 1;
+
+ /* Run through the program headers to calculate the total memory size we
+ * should claim. */
+ auto int NESTED_FUNC_ATTR calcsize (grub_elf_t _elf, Elf32_Phdr *phdr, void *_arg);
+ int NESTED_FUNC_ATTR calcsize (grub_elf_t _elf __attribute__ ((unused)),
+ Elf32_Phdr *phdr,
+ void *_arg __attribute__ ((unused)))
+ {
+ /* Only consider loadable segments. */
+ if (phdr->p_type != PT_LOAD)
+ return 0;
+ nr_phdrs++;
+ if (phdr->p_paddr < segments_start)
+ segments_start = phdr->p_paddr;
+ if (phdr->p_paddr + phdr->p_memsz > segments_end)
+ segments_end = phdr->p_paddr + phdr->p_memsz;
+ if (curr_align < phdr->p_align)
+ curr_align = phdr->p_align;
+ return 0;
+ }
+
+ grub_elf32_phdr_iterate (elf, calcsize, 0);
+
+ if (base)
+ *base = 0;
+
+ if (nr_phdrs == 0)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "no program headers present");
+ return 0;
+ }
+
+ if (segments_end < segments_start)
+ {
+ /* Very bad addresses. */
+ grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
+ return 0;
+ }
+
+ if (base)
+ *base = segments_start;
+ if (max_align)
+ *max_align = curr_align;
+ return segments_end - segments_start;
+}
+
+/* Load every loadable segment into memory specified by `_load_hook'. */
+grub_err_t
+grub_elf32_load (grub_elf_t _elf, grub_elf32_load_hook_t _load_hook,
+ grub_addr_t *base, grub_size_t *size)
+{
+ grub_addr_t load_base = (grub_addr_t) -1ULL;
+ grub_size_t load_size = 0;
+ grub_err_t err;
+
+ auto int NESTED_FUNC_ATTR grub_elf32_load_segment (grub_elf_t elf, Elf32_Phdr *phdr, void *hook);
+ int NESTED_FUNC_ATTR grub_elf32_load_segment (grub_elf_t elf, Elf32_Phdr *phdr, void *hook)
+ {
+ grub_elf32_load_hook_t load_hook = (grub_elf32_load_hook_t) hook;
+ grub_addr_t load_addr;
+ int do_load = 1;
+
+ load_addr = phdr->p_paddr;
+ if (load_hook && load_hook (phdr, &load_addr, &do_load))
+ return 1;
+
+ if (! do_load)
+ return 0;
+
+ if (load_addr < load_base)
+ load_base = load_addr;
+
+ grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
+ (unsigned long long) load_addr,
+ (unsigned long long) phdr->p_memsz);
+
+ if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1)
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "invalid offset in program header");
+ }
+
+ if (phdr->p_filesz)
+ {
+ grub_ssize_t read;
+ read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz);
+ if (read != (grub_ssize_t) phdr->p_filesz)
+ {
+ /* XXX How can we free memory from `load_hook'? */
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "couldn't read segment from file: "
+ "wanted 0x%lx bytes; read 0x%lx bytes",
+ phdr->p_filesz, read);
+ }
+ }
+
+ if (phdr->p_filesz < phdr->p_memsz)
+ grub_memset ((void *) (long) (load_addr + phdr->p_filesz),
+ 0, phdr->p_memsz - phdr->p_filesz);
+
+ load_size += phdr->p_memsz;
+
+ return 0;
+ }
+
+ err = grub_elf32_phdr_iterate (_elf, grub_elf32_load_segment, _load_hook);
+
+ if (base)
+ *base = load_base;
+ if (size)
+ *size = load_size;
+
+ return err;
+}
+
+
+/* 64-bit */
+
+int
+grub_elf_is_elf64 (grub_elf_t elf)
+{
+ return elf->ehdr.ehdr64.e_ident[EI_CLASS] == ELFCLASS64;
+}
+
+static grub_err_t
+grub_elf64_load_phdrs (grub_elf_t elf)
+{
+ grub_ssize_t phdrs_size;
+
+ phdrs_size = elf->ehdr.ehdr64.e_phnum * elf->ehdr.ehdr64.e_phentsize;
+
+ grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
+ (unsigned long long) elf->ehdr.ehdr64.e_phoff,
+ (unsigned long) phdrs_size);
+
+ elf->phdrs = grub_malloc (phdrs_size);
+ if (! elf->phdrs)
+ return grub_errno;
+
+ if ((grub_file_seek (elf->file, elf->ehdr.ehdr64.e_phoff) == (grub_off_t) -1)
+ || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size))
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_READ_ERROR, "cannot read program headers");
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_elf64_phdr_iterate (grub_elf_t elf,
+ int NESTED_FUNC_ATTR (*hook) (grub_elf_t, Elf64_Phdr *, void *),
+ void *hook_arg)
+{
+ Elf64_Phdr *phdrs;
+ unsigned int i;
+
+ if (! elf->phdrs)
+ if (grub_elf64_load_phdrs (elf))
+ return grub_errno;
+ phdrs = elf->phdrs;
+
+ for (i = 0; i < elf->ehdr.ehdr64.e_phnum; i++)
+ {
+ Elf64_Phdr *phdr = phdrs + i;
+ grub_dprintf ("elf",
+ "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
+ "filesz %lx\n",
+ i, phdr->p_type,
+ (unsigned long) phdr->p_paddr,
+ (unsigned long) phdr->p_memsz,
+ (unsigned long) phdr->p_filesz);
+ if (hook (elf, phdr, hook_arg))
+ break;
+ }
+
+ return grub_errno;
+}
+
+/* Calculate the amount of memory spanned by the segments. */
+grub_size_t
+grub_elf64_size (grub_elf_t elf, Elf64_Addr *base, grub_uint64_t *max_align)
+{
+ Elf64_Addr segments_start = (Elf64_Addr) -1;
+ Elf64_Addr segments_end = 0;
+ int nr_phdrs = 0;
+ grub_uint64_t curr_align = 1;
+
+ /* Run through the program headers to calculate the total memory size we
+ * should claim. */
+ auto int NESTED_FUNC_ATTR calcsize (grub_elf_t _elf, Elf64_Phdr *phdr, void *_arg);
+ int NESTED_FUNC_ATTR calcsize (grub_elf_t _elf __attribute__ ((unused)),
+ Elf64_Phdr *phdr,
+ void *_arg __attribute__ ((unused)))
+ {
+ /* Only consider loadable segments. */
+ if (phdr->p_type != PT_LOAD)
+ return 0;
+ nr_phdrs++;
+ if (phdr->p_paddr < segments_start)
+ segments_start = phdr->p_paddr;
+ if (phdr->p_paddr + phdr->p_memsz > segments_end)
+ segments_end = phdr->p_paddr + phdr->p_memsz;
+ if (curr_align < phdr->p_align)
+ curr_align = phdr->p_align;
+ return 0;
+ }
+
+ grub_elf64_phdr_iterate (elf, calcsize, 0);
+
+ if (base)
+ *base = 0;
+
+ if (nr_phdrs == 0)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "no program headers present");
+ return 0;
+ }
+
+ if (segments_end < segments_start)
+ {
+ /* Very bad addresses. */
+ grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
+ return 0;
+ }
+
+ if (base)
+ *base = segments_start;
+ if (max_align)
+ *max_align = curr_align;
+ return segments_end - segments_start;
+}
+
+/* Load every loadable segment into memory specified by `_load_hook'. */
+grub_err_t
+grub_elf64_load (grub_elf_t _elf, grub_elf64_load_hook_t _load_hook,
+ grub_addr_t *base, grub_size_t *size)
+{
+ grub_addr_t load_base = (grub_addr_t) -1ULL;
+ grub_size_t load_size = 0;
+ grub_err_t err;
+
+ auto int NESTED_FUNC_ATTR grub_elf64_load_segment (grub_elf_t elf, Elf64_Phdr *phdr,
+ void *hook);
+ int NESTED_FUNC_ATTR grub_elf64_load_segment (grub_elf_t elf, Elf64_Phdr *phdr, void *hook)
+ {
+ grub_elf64_load_hook_t load_hook = (grub_elf64_load_hook_t) hook;
+ grub_addr_t load_addr;
+ int do_load = 1;
+
+ load_addr = phdr->p_paddr;
+ if (load_hook && load_hook (phdr, &load_addr, &do_load))
+ return 1;
+
+ if (! do_load)
+ return 0;
+
+ if (load_addr < load_base)
+ load_base = load_addr;
+
+ grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
+ (unsigned long long) load_addr,
+ (unsigned long long) phdr->p_memsz);
+
+ if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1)
+ {
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "invalid offset in program header");
+ }
+
+ if (phdr->p_filesz)
+ {
+ grub_ssize_t read;
+ read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz);
+ if (read != (grub_ssize_t) phdr->p_filesz)
+ {
+ /* XXX How can we free memory from `load_hook'? */
+ grub_error_push ();
+ return grub_error (GRUB_ERR_BAD_OS,
+ "couldn't read segment from file: "
+ "wanted 0x%lx bytes; read 0x%lx bytes",
+ phdr->p_filesz, read);
+ }
+ }
+
+ if (phdr->p_filesz < phdr->p_memsz)
+ grub_memset ((void *) (long) (load_addr + phdr->p_filesz),
+ 0, phdr->p_memsz - phdr->p_filesz);
+
+ load_size += phdr->p_memsz;
+
+ return 0;
+ }
+
+ err = grub_elf64_phdr_iterate (_elf, grub_elf64_load_segment, _load_hook);
+
+ if (base)
+ *base = load_base;
+ if (size)
+ *size = load_size;
+
+ return err;
+}
diff --git a/grub-core/kern/emu/cache.S b/grub-core/kern/emu/cache.S
new file mode 100644
index 0000000..abd81c9
--- /dev/null
+++ b/grub-core/kern/emu/cache.S
@@ -0,0 +1,28 @@
+#ifndef GRUB_MACHINE_EMU
+#error "This source is only meant for grub-emu platform"
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+/* Nothing is necessary. */
+#elif defined(__sparc__)
+#include "../sparc64/cache.S"
+#elif defined(__mips__)
+/* On MIPS we must go through standard functions. */
+#include <grub/symbol.h>
+
+FUNCTION (grub_cpu_flush_cache)
+FUNCTION (grub_arch_sync_caches)
+ .set nomacro
+ .set noreorder
+ lui $t0, %hi(_flush_cache)
+ addui $t0, $t0, %lo(_flush_cache)
+ move $a3, $zero
+ jr $t0
+ nop
+ .set reorder
+ .set macro
+#elif defined(__powerpc__)
+#include "../powerpc/cache.S"
+#else
+#error "No target cpu type is defined"
+#endif
diff --git a/grub-core/kern/emu/console.c b/grub-core/kern/emu/console.c
new file mode 100644
index 0000000..7fd1fc0
--- /dev/null
+++ b/grub-core/kern/emu/console.c
@@ -0,0 +1,293 @@
+/* console.c -- Ncurses console for GRUB. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2005,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/>.
+ */
+
+#include <config.h>
+#include <config-util.h>
+
+/* For compatibility. */
+#ifndef A_NORMAL
+# define A_NORMAL 0
+#endif /* ! A_NORMAL */
+#ifndef A_STANDOUT
+# define A_STANDOUT 0
+#endif /* ! A_STANDOUT */
+
+#include <grub/emu/console.h>
+#include <grub/term.h>
+#include <grub/types.h>
+
+#if defined(HAVE_NCURSES_CURSES_H)
+# include <ncurses/curses.h>
+#elif defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+# include <curses.h>
+#else
+#error What the hell?
+#endif
+
+static int grub_console_attr = A_NORMAL;
+
+grub_uint8_t grub_console_cur_color = 7;
+
+static const grub_uint8_t grub_console_standard_color = 0x7;
+
+#define NUM_COLORS 8
+
+static grub_uint8_t color_map[NUM_COLORS] =
+{
+ COLOR_BLACK,
+ COLOR_BLUE,
+ COLOR_GREEN,
+ COLOR_CYAN,
+ COLOR_RED,
+ COLOR_MAGENTA,
+ COLOR_YELLOW,
+ COLOR_WHITE
+};
+
+static int use_color;
+
+static void
+grub_ncurses_putchar (struct grub_term_output *term __attribute__ ((unused)),
+ const struct grub_unicode_glyph *c)
+{
+ addch (c->base | grub_console_attr);
+}
+
+static void
+grub_ncurses_setcolorstate (struct grub_term_output *term,
+ grub_term_color_state state)
+{
+ switch (state)
+ {
+ case GRUB_TERM_COLOR_STANDARD:
+ grub_console_cur_color = grub_console_standard_color;
+ grub_console_attr = A_NORMAL;
+ break;
+ case GRUB_TERM_COLOR_NORMAL:
+ grub_console_cur_color = term->normal_color;
+ grub_console_attr = A_NORMAL;
+ break;
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ grub_console_cur_color = term->highlight_color;
+ grub_console_attr = A_STANDOUT;
+ break;
+ default:
+ break;
+ }
+
+ if (use_color)
+ {
+ grub_uint8_t fg, bg;
+
+ fg = (grub_console_cur_color & 7);
+ bg = (grub_console_cur_color >> 4) & 7;
+
+ grub_console_attr = (grub_console_cur_color & 8) ? A_BOLD : A_NORMAL;
+ color_set ((bg << 3) + fg, 0);
+ }
+}
+
+static int
+grub_ncurses_getkey (struct grub_term_input *term __attribute__ ((unused)))
+{
+ int c;
+
+ wtimeout (stdscr, 100);
+ c = getch ();
+
+ switch (c)
+ {
+ case ERR:
+ return -1;
+ case KEY_LEFT:
+ c = GRUB_TERM_KEY_LEFT;
+ break;
+
+ case KEY_RIGHT:
+ c = GRUB_TERM_KEY_RIGHT;
+ break;
+
+ case KEY_UP:
+ c = GRUB_TERM_KEY_UP;
+ break;
+
+ case KEY_DOWN:
+ c = GRUB_TERM_KEY_DOWN;
+ break;
+
+ case KEY_IC:
+ c = 24;
+ break;
+
+ case KEY_DC:
+ c = GRUB_TERM_KEY_DC;
+ break;
+
+ case KEY_BACKSPACE:
+ /* XXX: For some reason ncurses on xterm does not return
+ KEY_BACKSPACE. */
+ case 127:
+ c = '\b';
+ break;
+
+ case KEY_HOME:
+ c = GRUB_TERM_KEY_HOME;
+ break;
+
+ case KEY_END:
+ c = GRUB_TERM_KEY_END;
+ break;
+
+ case KEY_NPAGE:
+ c = GRUB_TERM_KEY_NPAGE;
+ break;
+
+ case KEY_PPAGE:
+ c = GRUB_TERM_KEY_PPAGE;
+ break;
+ }
+
+ return c;
+}
+
+static grub_uint16_t
+grub_ncurses_getxy (struct grub_term_output *term __attribute__ ((unused)))
+{
+ int x;
+ int y;
+
+ getyx (stdscr, y, x);
+
+ return (x << 8) | y;
+}
+
+static grub_uint16_t
+grub_ncurses_getwh (struct grub_term_output *term __attribute__ ((unused)))
+{
+ int x;
+ int y;
+
+ getmaxyx (stdscr, y, x);
+
+ return (x << 8) | y;
+}
+
+static void
+grub_ncurses_gotoxy (struct grub_term_output *term __attribute__ ((unused)),
+ grub_uint8_t x, grub_uint8_t y)
+{
+ move (y, x);
+}
+
+static void
+grub_ncurses_cls (struct grub_term_output *term __attribute__ ((unused)))
+{
+ clear ();
+ refresh ();
+}
+
+static void
+grub_ncurses_setcursor (struct grub_term_output *term __attribute__ ((unused)),
+ int on)
+{
+ curs_set (on ? 1 : 0);
+}
+
+static void
+grub_ncurses_refresh (struct grub_term_output *term __attribute__ ((unused)))
+{
+ refresh ();
+}
+
+static grub_err_t
+grub_ncurses_init (struct grub_term_output *term __attribute__ ((unused)))
+{
+ initscr ();
+ raw ();
+ noecho ();
+ scrollok (stdscr, TRUE);
+
+ nonl ();
+ intrflush (stdscr, FALSE);
+ keypad (stdscr, TRUE);
+
+ if (has_colors ())
+ {
+ start_color ();
+
+ if ((COLORS >= NUM_COLORS) && (COLOR_PAIRS >= NUM_COLORS * NUM_COLORS))
+ {
+ int i, j, n;
+
+ n = 0;
+ for (i = 0; i < NUM_COLORS; i++)
+ for (j = 0; j < NUM_COLORS; j++)
+ init_pair(n++, color_map[j], color_map[i]);
+
+ use_color = 1;
+ }
+ }
+
+ return 0;
+}
+
+static grub_err_t
+grub_ncurses_fini (struct grub_term_output *term __attribute__ ((unused)))
+{
+ endwin ();
+ return 0;
+}
+
+
+static struct grub_term_input grub_ncurses_term_input =
+ {
+ .name = "console",
+ .getkey = grub_ncurses_getkey,
+ };
+
+static struct grub_term_output grub_ncurses_term_output =
+ {
+ .name = "console",
+ .init = grub_ncurses_init,
+ .fini = grub_ncurses_fini,
+ .putchar = grub_ncurses_putchar,
+ .getxy = grub_ncurses_getxy,
+ .getwh = grub_ncurses_getwh,
+ .gotoxy = grub_ncurses_gotoxy,
+ .cls = grub_ncurses_cls,
+ .setcolorstate = grub_ncurses_setcolorstate,
+ .setcursor = grub_ncurses_setcursor,
+ .refresh = grub_ncurses_refresh,
+ .flags = GRUB_TERM_CODE_TYPE_ASCII
+ };
+
+void
+grub_console_init (void)
+{
+ grub_term_register_output ("console", &grub_ncurses_term_output);
+ grub_term_register_input ("console", &grub_ncurses_term_input);
+}
+
+void
+grub_console_fini (void)
+{
+ grub_ncurses_fini (&grub_ncurses_term_output);
+}
diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c
new file mode 100644
index 0000000..70bcae7
--- /dev/null
+++ b/grub-core/kern/emu/full.c
@@ -0,0 +1,71 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <grub/dl.h>
+#include <grub/env.h>
+#include <grub/kernel.h>
+#include <grub/misc.h>
+#include <grub/emu/misc.h>
+#include <grub/disk.h>
+
+void
+grub_register_exported_symbols (void)
+{
+}
+
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+ (void) ehdr;
+ return GRUB_ERR_BAD_MODULE;
+}
+
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+{
+ (void) mod;
+ (void) ehdr;
+ return GRUB_ERR_BAD_MODULE;
+}
+
+void
+grub_emu_init (void)
+{
+ grub_no_autoload = 1;
+}
+
+#ifdef GRUB_LINKER_HAVE_INIT
+void
+grub_arch_dl_init_linker (void)
+{
+}
+#endif
+
+void
+grub_emu_post_init (void)
+{
+ grub_lvm_fini ();
+ grub_mdraid09_fini ();
+ grub_mdraid1x_fini ();
+ grub_raid_fini ();
+ grub_raid_init ();
+ grub_mdraid09_init ();
+ grub_mdraid1x_init ();
+ grub_lvm_init ();
+}
diff --git a/grub-core/kern/emu/getroot.c b/grub-core/kern/emu/getroot.c
new file mode 100644
index 0000000..a274f3c
--- /dev/null
+++ b/grub-core/kern/emu/getroot.c
@@ -0,0 +1,932 @@
+/* getroot.c - Get root device */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,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 <config-util.h>
+#include <config.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <grub/util/misc.h>
+
+#ifdef __GNU__
+#include <hurd.h>
+#include <hurd/lookup.h>
+#include <hurd/fs.h>
+#include <sys/mman.h>
+#endif
+
+#ifdef __linux__
+# include <sys/types.h>
+# include <sys/wait.h>
+#endif
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+# include <grub/util/libzfs.h>
+# include <grub/util/libnvpair.h>
+#endif
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/emu/getroot.h>
+
+static void
+strip_extra_slashes (char *dir)
+{
+ char *p = dir;
+
+ while ((p = strchr (p, '/')) != 0)
+ {
+ if (p[1] == '/')
+ {
+ memmove (p, p + 1, strlen (p));
+ continue;
+ }
+ else if (p[1] == '\0')
+ {
+ if (p > dir)
+ p[0] = '\0';
+ break;
+ }
+
+ p++;
+ }
+}
+
+static char *
+xgetcwd (void)
+{
+ size_t size = 10;
+ char *path;
+
+ path = xmalloc (size);
+ while (! getcwd (path, size))
+ {
+ size <<= 1;
+ path = xrealloc (path, size);
+ }
+
+ return path;
+}
+
+#ifdef __linux__
+
+struct mountinfo_entry
+{
+ int id;
+ int major, minor;
+ char enc_root[PATH_MAX], enc_path[PATH_MAX];
+ char fstype[PATH_MAX], device[PATH_MAX];
+};
+
+/* Statting something on a btrfs filesystem always returns a virtual device
+ major/minor pair rather than the real underlying device, because btrfs
+ can span multiple underlying devices (and even if it's currently only
+ using a single device it can be dynamically extended onto another). We
+ can't deal with the multiple-device case yet, but in the meantime, we can
+ at least cope with the single-device case by scanning
+ /proc/self/mountinfo. */
+char *
+grub_find_root_device_from_mountinfo (const char *dir, char **relroot)
+{
+ FILE *fp;
+ char *buf = NULL;
+ size_t len = 0;
+ char *ret = NULL;
+ int entry_len = 0, entry_max = 4;
+ struct mountinfo_entry *entries;
+ struct mountinfo_entry parent_entry = { 0, 0, 0, "", "", "", "" };
+ int i;
+
+ if (! *dir)
+ dir = "/";
+ if (relroot)
+ *relroot = NULL;
+
+ fp = fopen ("/proc/self/mountinfo", "r");
+ if (! fp)
+ return NULL; /* fall through to other methods */
+
+ entries = xmalloc (entry_max * sizeof (*entries));
+
+ /* First, build a list of relevant visible mounts. */
+ while (getline (&buf, &len, fp) > 0)
+ {
+ struct mountinfo_entry entry;
+ int count;
+ size_t enc_path_len;
+ const char *sep;
+
+ if (sscanf (buf, "%d %d %u:%u %s %s%n",
+ &entry.id, &parent_entry.id, &entry.major, &entry.minor,
+ entry.enc_root, entry.enc_path, &count) < 6)
+ continue;
+
+ enc_path_len = strlen (entry.enc_path);
+ /* Check that enc_path is a prefix of dir. The prefix must either be
+ the entire string, or end with a slash, or be immediately followed
+ by a slash. */
+ if (strncmp (dir, entry.enc_path, enc_path_len) != 0 ||
+ (enc_path_len && dir[enc_path_len - 1] != '/' &&
+ dir[enc_path_len] && dir[enc_path_len] != '/'))
+ continue;
+
+ sep = strstr (buf + count, " - ");
+ if (!sep)
+ continue;
+
+ sep += sizeof (" - ") - 1;
+ if (sscanf (sep, "%s %s", entry.fstype, entry.device) != 2)
+ continue;
+
+ /* Using the mount IDs, find out where this fits in the list of
+ visible mount entries we've seen so far. There are three
+ interesting cases. Firstly, it may be inserted at the end: this is
+ the usual case of /foo/bar being mounted after /foo. Secondly, it
+ may be inserted at the start: for example, this can happen for
+ filesystems that are mounted before / and later moved under it.
+ Thirdly, it may occlude part or all of the existing filesystem
+ tree, in which case the end of the list needs to be pruned and this
+ new entry will be inserted at the end. */
+ if (entry_len >= entry_max)
+ {
+ entry_max <<= 1;
+ entries = xrealloc (entries, entry_max * sizeof (*entries));
+ }
+
+ if (!entry_len)
+ {
+ /* Initialise list. */
+ entry_len = 2;
+ entries[0] = parent_entry;
+ entries[1] = entry;
+ }
+ else
+ {
+ for (i = entry_len - 1; i >= 0; i--)
+ {
+ if (entries[i].id == parent_entry.id)
+ {
+ /* Insert at end, pruning anything previously above this. */
+ entry_len = i + 2;
+ entries[i + 1] = entry;
+ break;
+ }
+ else if (i == 0 && entries[i].id == entry.id)
+ {
+ /* Insert at start. */
+ entry_len++;
+ memmove (entries + 1, entries,
+ (entry_len - 1) * sizeof (*entries));
+ entries[0] = parent_entry;
+ entries[1] = entry;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Now scan visible mounts for the ones we're interested in. */
+ for (i = entry_len - 1; i >= 0; i--)
+ {
+ if (!*entries[i].device)
+ continue;
+
+ ret = strdup (entries[i].device);
+ if (relroot)
+ *relroot = strdup (entries[i].enc_root);
+ break;
+ }
+
+ free (buf);
+ free (entries);
+ fclose (fp);
+ return ret;
+}
+
+#endif /* __linux__ */
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+static char *
+find_root_device_from_libzfs (const char *dir)
+{
+ char *device = NULL;
+ char *poolname;
+ char *poolfs;
+
+ grub_find_zpool_from_dir (dir, &poolname, &poolfs);
+ if (! poolname)
+ return NULL;
+
+ {
+ zpool_handle_t *zpool;
+ libzfs_handle_t *libzfs;
+ nvlist_t *config, *vdev_tree;
+ nvlist_t **children, **path;
+ unsigned int nvlist_count;
+ unsigned int i;
+
+ libzfs = grub_get_libzfs_handle ();
+ if (! libzfs)
+ return NULL;
+
+ zpool = zpool_open (libzfs, poolname);
+ config = zpool_get_config (zpool, NULL);
+
+ if (nvlist_lookup_nvlist (config, "vdev_tree", &vdev_tree) != 0)
+ error (1, errno, "nvlist_lookup_nvlist (\"vdev_tree\")");
+
+ if (nvlist_lookup_nvlist_array (vdev_tree, "children", &children, &nvlist_count) != 0)
+ error (1, errno, "nvlist_lookup_nvlist_array (\"children\")");
+ assert (nvlist_count > 0);
+
+ while (nvlist_lookup_nvlist_array (children[0], "children",
+ &children, &nvlist_count) == 0)
+ assert (nvlist_count > 0);
+
+ for (i = 0; i < nvlist_count; i++)
+ {
+ if (nvlist_lookup_string (children[i], "path", &device) != 0)
+ error (1, errno, "nvlist_lookup_string (\"path\")");
+
+ struct stat st;
+ if (stat (device, &st) == 0)
+ {
+ device = xstrdup (device);
+ break;
+ }
+
+ device = NULL;
+ }
+
+ zpool_close (zpool);
+ }
+
+ free (poolname);
+ if (poolfs)
+ free (poolfs);
+
+ return device;
+}
+#endif
+
+#ifdef __MINGW32__
+
+char *
+grub_find_device (const char *dir __attribute__ ((unused)),
+ dev_t dev __attribute__ ((unused)))
+{
+ return 0;
+}
+
+#elif ! defined(__CYGWIN__)
+
+char *
+grub_find_device (const char *dir, dev_t dev)
+{
+ DIR *dp;
+ char *saved_cwd;
+ struct dirent *ent;
+
+ if (! dir)
+ {
+#ifdef __CYGWIN__
+ return NULL;
+#else
+ dir = "/dev";
+#endif
+ }
+
+ dp = opendir (dir);
+ if (! dp)
+ return 0;
+
+ saved_cwd = xgetcwd ();
+
+ grub_util_info ("changing current directory to %s", dir);
+ if (chdir (dir) < 0)
+ {
+ free (saved_cwd);
+ closedir (dp);
+ return 0;
+ }
+
+ while ((ent = readdir (dp)) != 0)
+ {
+ struct stat st;
+
+ /* Avoid:
+ - dotfiles (like "/dev/.tmp.md0") since they could be duplicates.
+ - dotdirs (like "/dev/.static") since they could contain duplicates. */
+ if (ent->d_name[0] == '.')
+ continue;
+
+ if (lstat (ent->d_name, &st) < 0)
+ /* Ignore any error. */
+ continue;
+
+ if (S_ISLNK (st.st_mode)) {
+#ifdef __linux__
+ if (strcmp (dir, "mapper") == 0) {
+ /* Follow symbolic links under /dev/mapper/; the canonical name
+ may be something like /dev/dm-0, but the names under
+ /dev/mapper/ are more human-readable and so we prefer them if
+ we can get them. */
+ if (stat (ent->d_name, &st) < 0)
+ continue;
+ } else
+#endif /* __linux__ */
+ /* Don't follow other symbolic links. */
+ continue;
+ }
+
+ if (S_ISDIR (st.st_mode))
+ {
+ /* Find it recursively. */
+ char *res;
+
+ res = grub_find_device (ent->d_name, dev);
+
+ if (res)
+ {
+ if (chdir (saved_cwd) < 0)
+ grub_util_error ("cannot restore the original directory");
+
+ free (saved_cwd);
+ closedir (dp);
+ return res;
+ }
+ }
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
+ if (S_ISCHR (st.st_mode) && st.st_rdev == dev)
+#else
+ if (S_ISBLK (st.st_mode) && st.st_rdev == dev)
+#endif
+ {
+#ifdef __linux__
+ /* Skip device names like /dev/dm-0, which are short-hand aliases
+ to more descriptive device names, e.g. those under /dev/mapper */
+ if (ent->d_name[0] == 'd' &&
+ ent->d_name[1] == 'm' &&
+ ent->d_name[2] == '-' &&
+ ent->d_name[3] >= '0' &&
+ ent->d_name[3] <= '9')
+ continue;
+#endif
+
+ /* Found! */
+ char *res;
+ char *cwd;
+#if defined(__NetBSD__)
+ /* Convert this block device to its character (raw) device. */
+ const char *template = "%s/r%s";
+#else
+ /* Keep the device name as it is. */
+ const char *template = "%s/%s";
+#endif
+
+ cwd = xgetcwd ();
+ res = xmalloc (strlen (cwd) + strlen (ent->d_name) + 3);
+ sprintf (res, template, cwd, ent->d_name);
+ strip_extra_slashes (res);
+ free (cwd);
+
+ /* /dev/root is not a real block device keep looking, takes care
+ of situation where root filesystem is on the same partition as
+ grub files */
+
+ if (strcmp(res, "/dev/root") == 0)
+ continue;
+
+ if (chdir (saved_cwd) < 0)
+ grub_util_error ("cannot restore the original directory");
+
+ free (saved_cwd);
+ closedir (dp);
+ return res;
+ }
+ }
+
+ if (chdir (saved_cwd) < 0)
+ grub_util_error ("cannot restore the original directory");
+
+ free (saved_cwd);
+ closedir (dp);
+ return 0;
+}
+
+#else /* __CYGWIN__ */
+
+/* Read drive/partition serial number from mbr/boot sector,
+ return 0 on read error, ~0 on unknown serial. */
+static unsigned
+get_bootsec_serial (const char *os_dev, int mbr)
+{
+ /* Read boot sector. */
+ int fd = open (os_dev, O_RDONLY);
+ if (fd < 0)
+ return 0;
+ unsigned char buf[0x200];
+ int n = read (fd, buf, sizeof (buf));
+ close (fd);
+ if (n != sizeof(buf))
+ return 0;
+
+ /* Check signature. */
+ if (!(buf[0x1fe] == 0x55 && buf[0x1ff] == 0xaa))
+ return ~0;
+
+ /* Serial number offset depends on boot sector type. */
+ if (mbr)
+ n = 0x1b8;
+ else if (memcmp (buf + 0x03, "NTFS", 4) == 0)
+ n = 0x048;
+ else if (memcmp (buf + 0x52, "FAT32", 5) == 0)
+ n = 0x043;
+ else if (memcmp (buf + 0x36, "FAT", 3) == 0)
+ n = 0x027;
+ else
+ return ~0;
+
+ unsigned serial = *(unsigned *)(buf + n);
+ if (serial == 0)
+ return ~0;
+ return serial;
+}
+
+char *
+grub_find_device (const char *path, dev_t dev)
+{
+ /* No root device for /cygdrive. */
+ if (dev == (DEV_CYGDRIVE_MAJOR << 16))
+ return 0;
+
+ /* Convert to full POSIX and Win32 path. */
+ char fullpath[PATH_MAX], winpath[PATH_MAX];
+ cygwin_conv_to_full_posix_path (path, fullpath);
+ cygwin_conv_to_full_win32_path (fullpath, winpath);
+
+ /* If identical, this is no real filesystem path. */
+ if (strcmp (fullpath, winpath) == 0)
+ return 0;
+
+ /* Check for floppy drive letter. */
+ if (winpath[0] && winpath[1] == ':' && strchr ("AaBb", winpath[0]))
+ return xstrdup (strchr ("Aa", winpath[0]) ? "/dev/fd0" : "/dev/fd1");
+
+ /* Cygwin returns the partition serial number in stat.st_dev.
+ This is never identical to the device number of the emulated
+ /dev/sdXN device, so above grub_find_device () does not work.
+ Search the partition with the same serial in boot sector instead. */
+ char devpath[sizeof ("/dev/sda15") + 13]; /* Size + Paranoia. */
+ int d;
+ for (d = 'a'; d <= 'z'; d++)
+ {
+ sprintf (devpath, "/dev/sd%c", d);
+ if (get_bootsec_serial (devpath, 1) == 0)
+ continue;
+ int p;
+ for (p = 1; p <= 15; p++)
+ {
+ sprintf (devpath, "/dev/sd%c%d", d, p);
+ unsigned ser = get_bootsec_serial (devpath, 0);
+ if (ser == 0)
+ break;
+ if (ser != (unsigned)~0 && dev == (dev_t)ser)
+ return xstrdup (devpath);
+ }
+ }
+ return 0;
+}
+
+#endif /* __CYGWIN__ */
+
+char *
+grub_guess_root_device (const char *dir)
+{
+ char *os_dev = NULL;
+#ifdef __GNU__
+ file_t file;
+ mach_port_t *ports;
+ int *ints;
+ loff_t *offsets;
+ char *data;
+ error_t err;
+ mach_msg_type_number_t num_ports = 0, num_ints = 0, num_offsets = 0, data_len = 0;
+ size_t name_len;
+
+ file = file_name_lookup (dir, 0, 0);
+ if (file == MACH_PORT_NULL)
+ return 0;
+
+ err = file_get_storage_info (file,
+ &ports, &num_ports,
+ &ints, &num_ints,
+ &offsets, &num_offsets,
+ &data, &data_len);
+
+ if (num_ints < 1)
+ grub_util_error ("Storage info for `%s' does not include type", dir);
+ if (ints[0] != STORAGE_DEVICE)
+ grub_util_error ("Filesystem of `%s' is not stored on local disk", dir);
+
+ if (num_ints < 5)
+ grub_util_error ("Storage info for `%s' does not include name", dir);
+ name_len = ints[4];
+ if (name_len < data_len)
+ grub_util_error ("Bogus name length for storage info for `%s'", dir);
+ if (data[name_len - 1] != '\0')
+ grub_util_error ("Storage name for `%s' not NUL-terminated", dir);
+
+ os_dev = xmalloc (strlen ("/dev/") + data_len);
+ memcpy (os_dev, "/dev/", strlen ("/dev/"));
+ memcpy (os_dev + strlen ("/dev/"), data, data_len);
+
+ if (ports && num_ports > 0)
+ {
+ mach_msg_type_number_t i;
+ for (i = 0; i < num_ports; i++)
+ {
+ mach_port_t port = ports[i];
+ if (port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self(), port);
+ }
+ munmap ((caddr_t) ports, num_ports * sizeof (*ports));
+ }
+
+ if (ints && num_ints > 0)
+ munmap ((caddr_t) ints, num_ints * sizeof (*ints));
+ if (offsets && num_offsets > 0)
+ munmap ((caddr_t) offsets, num_offsets * sizeof (*offsets));
+ if (data && data_len > 0)
+ munmap (data, data_len);
+ mach_port_deallocate (mach_task_self (), file);
+#else /* !__GNU__ */
+ struct stat st;
+ dev_t dev;
+
+#ifdef __linux__
+ if (!os_dev)
+ os_dev = grub_find_root_device_from_mountinfo (dir, NULL);
+#endif /* __linux__ */
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ if (!os_dev)
+ os_dev = find_root_device_from_libzfs (dir);
+#endif
+
+ if (os_dev)
+ {
+ if (stat (os_dev, &st) >= 0)
+ dev = st.st_rdev;
+ else
+ grub_util_error ("cannot stat `%s'", os_dev);
+ free (os_dev);
+ }
+ else
+ {
+ if (stat (dir, &st) >= 0)
+ dev = st.st_dev;
+ else
+ grub_util_error ("cannot stat `%s'", dir);
+ }
+
+#ifdef __CYGWIN__
+ /* Cygwin specific function. */
+ os_dev = grub_find_device (dir, dev);
+
+#else
+
+ /* This might be truly slow, but is there any better way? */
+ os_dev = grub_find_device ("/dev", dev);
+#endif
+#endif /* !__GNU__ */
+
+ return os_dev;
+}
+
+static int
+grub_util_is_dmraid (const char *os_dev)
+{
+ if (! strncmp (os_dev, "/dev/mapper/nvidia_", 19))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/isw_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/hpt37x_", 19))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/hpt45x_", 19))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/via_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/lsi_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/pdc_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/jmicron_", 20))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/asr_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/sil_", 16))
+ return 1;
+ else if (! strncmp (os_dev, "/dev/mapper/ddf1_", 17))
+ return 1;
+
+ return 0;
+}
+
+int
+grub_util_get_dev_abstraction (const char *os_dev __attribute__((unused)))
+{
+#ifdef __linux__
+ /* User explicitly claims that this drive is visible by BIOS. */
+ if (grub_util_biosdisk_is_present (os_dev))
+ return GRUB_DEV_ABSTRACTION_NONE;
+
+ /* Check for LVM. */
+ if (!strncmp (os_dev, "/dev/mapper/", 12)
+ && ! grub_util_is_dmraid (os_dev)
+ && strncmp (os_dev, "/dev/mapper/mpath", 17) != 0)
+ return GRUB_DEV_ABSTRACTION_LVM;
+
+ /* Check for RAID. */
+ if (!strncmp (os_dev, "/dev/md", 7))
+ return GRUB_DEV_ABSTRACTION_RAID;
+#endif
+
+ /* No abstraction found. */
+ return GRUB_DEV_ABSTRACTION_NONE;
+}
+
+#ifdef __linux__
+static char *
+get_mdadm_uuid (const char *os_dev)
+{
+ int mdadm_pipe[2];
+ pid_t mdadm_pid;
+ char *name = NULL;
+
+ if (pipe (mdadm_pipe) < 0)
+ {
+ grub_util_warn ("Unable to create pipe for mdadm: %s", strerror (errno));
+ return NULL;
+ }
+
+ mdadm_pid = fork ();
+ if (mdadm_pid < 0)
+ grub_util_warn ("Unable to fork mdadm: %s", strerror (errno));
+ else if (mdadm_pid == 0)
+ {
+ /* Child. */
+ char *argv[5];
+
+ close (mdadm_pipe[0]);
+ dup2 (mdadm_pipe[1], STDOUT_FILENO);
+ close (mdadm_pipe[1]);
+
+ /* execvp has inconvenient types, hence the casts. None of these
+ strings will actually be modified. */
+ argv[0] = (char *) "mdadm";
+ argv[1] = (char *) "--detail";
+ argv[2] = (char *) "--export";
+ argv[3] = (char *) os_dev;
+ argv[4] = NULL;
+ execvp ("mdadm", argv);
+ exit (127);
+ }
+ else
+ {
+ /* Parent. Read mdadm's output. */
+ FILE *mdadm;
+ char *buf = NULL;
+ size_t len = 0;
+
+ close (mdadm_pipe[1]);
+ mdadm = fdopen (mdadm_pipe[0], "r");
+ if (! mdadm)
+ {
+ grub_util_warn ("Unable to open stream from mdadm: %s",
+ strerror (errno));
+ goto out;
+ }
+
+ while (getline (&buf, &len, mdadm) > 0)
+ {
+ if (strncmp (buf, "MD_UUID=", sizeof ("MD_UUID=") - 1) == 0)
+ {
+ char *name_start, *ptri, *ptro;
+ size_t name_len;
+
+ free (name);
+ name_start = buf + sizeof ("MD_UUID=") - 1;
+ ptro = name = xmalloc (strlen (name_start) + 1);
+ for (ptri = name_start; *ptri && *ptri != '\n' && *ptri != '\r';
+ ptri++)
+ if ((*ptri >= '0' && *ptri <= '9')
+ || (*ptri >= 'a' && *ptri <= 'f')
+ || (*ptri >= 'A' && *ptri <= 'F'))
+ *ptro++ = *ptri;
+ *ptro = 0;
+ }
+ }
+
+out:
+ close (mdadm_pipe[0]);
+ waitpid (mdadm_pid, NULL, 0);
+ }
+
+ return name;
+}
+#endif /* __linux__ */
+
+char *
+grub_util_get_grub_dev (const char *os_dev)
+{
+ char *grub_dev = NULL;
+
+ switch (grub_util_get_dev_abstraction (os_dev))
+ {
+ case GRUB_DEV_ABSTRACTION_LVM:
+
+ {
+ unsigned short i, len;
+ grub_size_t offset = sizeof ("/dev/mapper/") - 1;
+
+ len = strlen (os_dev) - offset + 1;
+ grub_dev = xmalloc (len);
+
+ for (i = 0; i < len; i++, offset++)
+ {
+ grub_dev[i] = os_dev[offset];
+ if (os_dev[offset] == '-' && os_dev[offset + 1] == '-')
+ offset++;
+ }
+ }
+
+ break;
+
+ case GRUB_DEV_ABSTRACTION_RAID:
+
+ if (os_dev[7] == '_' && os_dev[8] == 'd')
+ {
+ /* This a partitionable RAID device of the form /dev/md_dNNpMM. */
+
+ char *p, *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md_d") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md%s", p);
+ free (p);
+ }
+ else if (os_dev[7] == '/' && os_dev[8] == 'd')
+ {
+ /* This a partitionable RAID device of the form /dev/md/dNNpMM. */
+
+ char *p, *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md/d") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md%s", p);
+ free (p);
+ }
+ else if (os_dev[7] >= '0' && os_dev[7] <= '9')
+ {
+ char *p , *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md%s", p);
+ free (p);
+ }
+ else if (os_dev[7] == '/' && os_dev[8] >= '0' && os_dev[8] <= '9')
+ {
+ char *p , *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md/") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md%s", p);
+ free (p);
+ }
+ else if (os_dev[7] == '/')
+ {
+ /* mdraid 1.x with a free name. */
+ char *p , *q;
+
+ p = strdup (os_dev + sizeof ("/dev/md/") - 1);
+
+ q = strchr (p, 'p');
+ if (q)
+ *q = ',';
+
+ grub_dev = xasprintf ("md/%s", p);
+ free (p);
+ }
+ else
+ grub_util_error ("unknown kind of RAID device `%s'", os_dev);
+
+#ifdef __linux__
+ {
+ char *mdadm_name = get_mdadm_uuid (os_dev);
+ struct stat st;
+
+ if (mdadm_name)
+ {
+ const char *q;
+
+ for (q = os_dev + strlen (os_dev) - 1; q >= os_dev
+ && grub_isdigit (*q); q--);
+
+ if (q >= os_dev && *q == 'p')
+ {
+ free (grub_dev);
+ grub_dev = xasprintf ("mduuid/%s,%s", mdadm_name, q + 1);
+ goto done;
+ }
+ free (grub_dev);
+ grub_dev = xasprintf ("mduuid/%s", mdadm_name);
+
+ done:
+ free (mdadm_name);
+ }
+ }
+#endif /* __linux__ */
+
+ break;
+
+ default: /* GRUB_DEV_ABSTRACTION_NONE */
+ grub_dev = grub_util_biosdisk_get_grub_dev (os_dev);
+ }
+
+ return grub_dev;
+}
+
+const char *
+grub_util_check_block_device (const char *blk_dev)
+{
+ struct stat st;
+
+ if (stat (blk_dev, &st) < 0)
+ grub_util_error ("cannot stat `%s'", blk_dev);
+
+ if (S_ISBLK (st.st_mode))
+ return (blk_dev);
+ else
+ return 0;
+}
+
+const char *
+grub_util_check_char_device (const char *blk_dev)
+{
+ struct stat st;
+
+ if (stat (blk_dev, &st) < 0)
+ grub_util_error ("cannot stat `%s'", blk_dev);
+
+ if (S_ISCHR (st.st_mode))
+ return (blk_dev);
+ else
+ return 0;
+}
diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c
new file mode 100644
index 0000000..feb2a8f
--- /dev/null
+++ b/grub-core/kern/emu/hostdisk.c
@@ -0,0 +1,1776 @@
+/* hostdisk.c - emulate biosdisk */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,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/disk.h>
+#include <grub/partition.h>
+#include <grub/msdos_partition.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/emu/misc.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifdef __linux__
+# include <sys/ioctl.h> /* ioctl */
+# if !defined(__GLIBC__) || \
+ ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
+/* Maybe libc doesn't have large file support. */
+# include <linux/unistd.h> /* _llseek */
+# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
+# ifndef BLKFLSBUF
+# define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
+# endif /* ! BLKFLSBUF */
+# include <sys/ioctl.h> /* ioctl */
+# ifndef HDIO_GETGEO
+# define HDIO_GETGEO 0x0301 /* get device geometry */
+/* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
+ defined. */
+struct hd_geometry
+{
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+# endif /* ! HDIO_GETGEO */
+# ifndef BLKGETSIZE64
+# define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
+# endif /* ! BLKGETSIZE64 */
+# ifndef MAJOR
+# ifndef MINORBITS
+# define MINORBITS 8
+# endif /* ! MINORBITS */
+# define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
+# endif /* ! MAJOR */
+# ifndef FLOPPY_MAJOR
+# define FLOPPY_MAJOR 2
+# endif /* ! FLOPPY_MAJOR */
+# ifndef LOOP_MAJOR
+# define LOOP_MAJOR 7
+# endif /* ! LOOP_MAJOR */
+#endif /* __linux__ */
+
+#ifdef __CYGWIN__
+# include <sys/ioctl.h>
+# include <cygwin/fs.h> /* BLKGETSIZE64 */
+# include <cygwin/hdreg.h> /* HDIO_GETGEO */
+# define MAJOR(dev) ((unsigned) ((dev) >> 16))
+# define FLOPPY_MAJOR 2
+#endif
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+# include <sys/disk.h> /* DIOCGMEDIASIZE */
+# include <sys/param.h>
+# include <sys/sysctl.h>
+# define MAJOR(dev) major(dev)
+# define FLOPPY_MAJOR 2
+#endif
+
+#if defined(__APPLE__)
+# include <sys/disk.h>
+#endif
+
+#ifdef HAVE_DEVICE_MAPPER
+# include <libdevmapper.h>
+#endif
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <libgeom.h>
+#elif defined(__NetBSD__)
+# define HAVE_DIOCGDINFO
+# include <sys/ioctl.h>
+# include <sys/disklabel.h> /* struct disklabel */
+#else /* !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */
+# undef HAVE_DIOCGDINFO
+#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
+
+#if defined(__NetBSD__)
+# ifdef HAVE_GETRAWPARTITION
+# include <util.h> /* getrawpartition */
+# endif /* HAVE_GETRAWPARTITION */
+# include <sys/fdio.h>
+# ifndef FLOPPY_MAJOR
+# define FLOPPY_MAJOR 2
+# endif /* ! FLOPPY_MAJOR */
+# ifndef RAW_FLOPPY_MAJOR
+# define RAW_FLOPPY_MAJOR 9
+# endif /* ! RAW_FLOPPY_MAJOR */
+#endif /* defined(__NetBSD__) */
+
+struct
+{
+ char *drive;
+ char *device;
+} map[256];
+
+struct grub_util_biosdisk_data
+{
+ char *dev;
+ int access_mode;
+ int fd;
+ int is_disk;
+};
+
+#ifdef __linux__
+/* Check if we have devfs support. */
+static int
+have_devfs (void)
+{
+ static int dev_devfsd_exists = -1;
+
+ if (dev_devfsd_exists < 0)
+ {
+ struct stat st;
+
+ dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
+ }
+
+ return dev_devfsd_exists;
+}
+#endif /* __linux__ */
+
+#if defined(__NetBSD__)
+/* Adjust device driver parameters. This function should be called just
+ after successfully opening the device. For now, it simply prevents the
+ floppy driver from retrying operations on failure, as otherwise the
+ driver takes a while to abort when there is no floppy in the drive. */
+static void
+configure_device_driver (int fd)
+{
+ struct stat st;
+
+ if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
+ return;
+ if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
+ {
+ int floppy_opts;
+
+ if (ioctl (fd, FDIOCGETOPTS, &floppy_opts) == -1)
+ return;
+ floppy_opts |= FDOPT_NORETRY;
+ if (ioctl (fd, FDIOCSETOPTS, &floppy_opts) == -1)
+ return;
+ }
+}
+#endif /* defined(__NetBSD__) */
+
+static int
+find_grub_drive (const char *name)
+{
+ unsigned int i;
+
+ if (name)
+ {
+ for (i = 0; i < ARRAY_SIZE (map); i++)
+ if (map[i].drive && ! strcmp (map[i].drive, name))
+ return i;
+ }
+
+ return -1;
+}
+
+static int
+find_free_slot (void)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
+ if (! map[i].drive)
+ return i;
+
+ return -1;
+}
+
+static int
+grub_util_biosdisk_iterate (int (*hook) (const char *name))
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
+ if (map[i].drive && hook (map[i].drive))
+ return 1;
+
+ return 0;
+}
+
+static grub_err_t
+grub_util_biosdisk_open (const char *name, grub_disk_t disk)
+{
+ int drive;
+ struct stat st;
+ struct grub_util_biosdisk_data *data;
+
+ drive = find_grub_drive (name);
+ if (drive < 0)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ "no mapping exists for `%s'", name);
+
+ disk->id = drive;
+ disk->data = data = xmalloc (sizeof (struct grub_util_biosdisk_data));
+ data->dev = NULL;
+ data->access_mode = 0;
+ data->fd = -1;
+ data->is_disk = 0;
+
+ /* Get the size. */
+#if defined(__MINGW32__)
+ {
+ grub_uint64_t size;
+
+ size = grub_util_get_disk_size (map[drive].device);
+
+ if (size % 512)
+ grub_util_error ("unaligned device size");
+
+ disk->total_sectors = size >> 9;
+
+ grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
+
+ return GRUB_ERR_NONE;
+ }
+#elif defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \
+ defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
+ {
+# if defined(__NetBSD__)
+ struct disklabel label;
+# else
+ unsigned long long nr;
+# endif
+ int fd;
+
+ fd = open (map[drive].device, O_RDONLY);
+ if (fd == -1)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot open `%s' while attempting to get disk size", map[drive].device);
+
+# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
+ if (fstat (fd, &st) < 0 || ! S_ISCHR (st.st_mode))
+# else
+ if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
+# endif
+ {
+ close (fd);
+ goto fail;
+ }
+ data->is_disk = 1;
+
+# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (ioctl (fd, DIOCGMEDIASIZE, &nr))
+# elif defined(__APPLE__)
+ if (ioctl (fd, DKIOCGETBLOCKCOUNT, &nr))
+# elif defined(__NetBSD__)
+ configure_device_driver (fd);
+ if (ioctl (fd, DIOCGDINFO, &label) == -1)
+# else
+ if (ioctl (fd, BLKGETSIZE64, &nr))
+# endif
+ {
+ close (fd);
+ goto fail;
+ }
+
+ close (fd);
+
+# if defined (__APPLE__)
+ disk->total_sectors = nr;
+# elif defined(__NetBSD__)
+ disk->total_sectors = label.d_secperunit;
+# else
+ disk->total_sectors = nr / 512;
+
+ if (nr % 512)
+ grub_util_error ("unaligned device size");
+# endif
+
+ grub_util_info ("the size of %s is %llu", name, disk->total_sectors);
+
+ return GRUB_ERR_NONE;
+ }
+
+ fail:
+ /* In GNU/Hurd, stat() will return the right size. */
+#elif !defined (__GNU__)
+# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
+#endif
+ if (stat (map[drive].device, &st) < 0)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot stat `%s'", map[drive].device);
+
+ disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
+
+ grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
+
+ return GRUB_ERR_NONE;
+}
+
+#ifdef HAVE_DEVICE_MAPPER
+static int
+device_is_mapped (const char *dev)
+{
+ struct stat st;
+
+ if (stat (dev, &st) < 0)
+ return 0;
+
+ return dm_is_dm_major (major (st.st_rdev));
+}
+#endif /* HAVE_DEVICE_MAPPER */
+
+#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
+/* FIXME: geom actually gives us the whole container hierarchy.
+ It can be used more efficiently than this. */
+static void
+follow_geom_up (const char *name, grub_disk_addr_t *off_out, char **name_out)
+{
+ struct gmesh mesh;
+ struct gclass *class;
+ int error;
+ struct ggeom *geom;
+
+ grub_util_info ("following geom '%s'", name);
+
+ error = geom_gettree (&mesh);
+ if (error != 0)
+ grub_util_error ("couldn't open geom");
+
+ LIST_FOREACH (class, &mesh.lg_class, lg_class)
+ if (strcasecmp (class->lg_name, "part") == 0)
+ break;
+ if (!class)
+ grub_util_error ("couldn't open geom part");
+
+ LIST_FOREACH (geom, &class->lg_geom, lg_geom)
+ {
+ struct gprovider *provider;
+ LIST_FOREACH (provider, &geom->lg_provider, lg_provider)
+ if (strcmp (provider->lg_name, name) == 0)
+ {
+ char *name_tmp = xstrdup (geom->lg_name);
+ grub_disk_addr_t off = 0;
+ struct gconfig *config;
+ grub_util_info ("geom '%s' has parent '%s'", name, geom->lg_name);
+
+ follow_geom_up (name_tmp, &off, name_out);
+ free (name_tmp);
+ LIST_FOREACH (config, &provider->lg_config, lg_config)
+ if (strcasecmp (config->lg_name, "start") == 0)
+ off += strtoull (config->lg_val, 0, 10);
+ if (off_out)
+ *off_out = off;
+ return;
+ }
+ }
+ grub_util_info ("geom '%s' has no parent", name);
+ if (name_out)
+ *name_out = xstrdup (name);
+ if (off_out)
+ *off_out = 0;
+}
+
+static grub_disk_addr_t
+find_partition_start (const char *dev)
+{
+ grub_disk_addr_t out;
+ if (strncmp (dev, "/dev/", sizeof ("/dev/") - 1) != 0)
+ return 0;
+ follow_geom_up (dev + sizeof ("/dev/") - 1, &out, NULL);
+
+ return out;
+}
+#elif defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO)
+static grub_disk_addr_t
+find_partition_start (const char *dev)
+{
+ int fd;
+# if !defined(HAVE_DIOCGDINFO)
+ struct hd_geometry hdg;
+# else /* defined(HAVE_DIOCGDINFO) */
+ struct disklabel label;
+ int p_index;
+# endif /* !defined(HAVE_DIOCGDINFO) */
+
+# ifdef HAVE_DEVICE_MAPPER
+ if (grub_device_mapper_supported () && device_is_mapped (dev)) {
+ struct dm_task *task = NULL;
+ grub_uint64_t start, length;
+ char *target_type, *params, *space;
+ grub_disk_addr_t partition_start;
+
+ /* If any device-mapper operation fails, we fall back silently to
+ HDIO_GETGEO. */
+ task = dm_task_create (DM_DEVICE_TABLE);
+ if (! task)
+ {
+ grub_dprintf ("hostdisk", "dm_task_create failed\n");
+ goto devmapper_fail;
+ }
+
+ if (! dm_task_set_name (task, dev))
+ {
+ grub_dprintf ("hostdisk", "dm_task_set_name failed\n");
+ goto devmapper_fail;
+ }
+
+ if (! dm_task_run (task))
+ {
+ grub_dprintf ("hostdisk", "dm_task_run failed\n");
+ goto devmapper_fail;
+ }
+
+ dm_get_next_target (task, NULL, &start, &length, &target_type, &params);
+ if (! target_type)
+ {
+ grub_dprintf ("hostdisk", "no dm target\n");
+ goto devmapper_fail;
+ }
+ if (strcmp (target_type, "linear") != 0)
+ {
+ grub_dprintf ("hostdisk", "ignoring dm target %s (not linear)\n",
+ target_type);
+ goto devmapper_fail;
+ }
+ if (! params)
+ {
+ grub_dprintf ("hostdisk", "no dm params\n");
+ goto devmapper_fail;
+ }
+
+ /* The params string for a linear target looks like this:
+ DEVICE-NAME START-SECTOR
+ Parse this out. */
+ space = strchr (params, ' ');
+ if (! space)
+ goto devmapper_fail;
+ errno = 0;
+ partition_start = strtoull (space + 1, NULL, 10);
+ if (errno == 0)
+ {
+ grub_dprintf ("hostdisk", "dm %s starts at %llu\n",
+ dev, (unsigned long long) partition_start);
+ dm_task_destroy (task);
+ return partition_start;
+ }
+
+devmapper_fail:
+ if (task)
+ dm_task_destroy (task);
+ }
+# endif /* HAVE_DEVICE_MAPPER */
+
+ fd = open (dev, O_RDONLY);
+ if (fd == -1)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE,
+# if !defined(HAVE_DIOCGDINFO)
+ "cannot open `%s' while attempting to get disk geometry", dev);
+# else /* defined(HAVE_DIOCGDINFO) */
+ "cannot open `%s' while attempting to get disk label", dev);
+# endif /* !defined(HAVE_DIOCGDINFO) */
+ return 0;
+ }
+
+# if !defined(HAVE_DIOCGDINFO)
+ if (ioctl (fd, HDIO_GETGEO, &hdg))
+# else /* defined(HAVE_DIOCGDINFO) */
+# if defined(__NetBSD__)
+ configure_device_driver (fd);
+# endif /* defined(__NetBSD__) */
+ if (ioctl (fd, DIOCGDINFO, &label) == -1)
+# endif /* !defined(HAVE_DIOCGDINFO) */
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE,
+# if !defined(HAVE_DIOCGDINFO)
+ "cannot get disk geometry of `%s'", dev);
+# else /* defined(HAVE_DIOCGDINFO) */
+ "cannot get disk label of `%s'", dev);
+# endif /* !defined(HAVE_DIOCGDINFO) */
+ close (fd);
+ return 0;
+ }
+
+ close (fd);
+
+# if !defined(HAVE_DIOCGDINFO)
+ return hdg.start;
+# else /* defined(HAVE_DIOCGDINFO) */
+ if (dev[0])
+ p_index = dev[strlen(dev) - 1] - 'a';
+ else
+ p_index = -1;
+
+ if (p_index >= label.d_npartitions || p_index < 0)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE,
+ "no disk label entry for `%s'", dev);
+ return 0;
+ }
+ return (grub_disk_addr_t) label.d_partitions[p_index].p_offset;
+# endif /* !defined(HAVE_DIOCGDINFO) */
+}
+#endif /* __linux__ || __CYGWIN__ || HAVE_DIOCGDINFO */
+
+#ifdef __linux__
+/* Cache of partition start sectors for each disk. */
+struct linux_partition_cache
+{
+ struct linux_partition_cache *next;
+ char *dev;
+ unsigned long start;
+ int partno;
+};
+
+struct linux_partition_cache *linux_partition_cache_list;
+
+static int
+linux_find_partition (char *dev, grub_disk_addr_t sector)
+{
+ size_t len = strlen (dev);
+ const char *format;
+ char *p;
+ int i;
+ char real_dev[PATH_MAX];
+ struct linux_partition_cache *cache;
+
+ strcpy(real_dev, dev);
+
+ if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
+ {
+ p = real_dev + len - 4;
+ format = "part%d";
+ }
+ else if (real_dev[len - 1] >= '0' && real_dev[len - 1] <= '9')
+ {
+ p = real_dev + len;
+ format = "p%d";
+ }
+ else
+ {
+ p = real_dev + len;
+ format = "%d";
+ }
+
+ for (cache = linux_partition_cache_list; cache; cache = cache->next)
+ {
+ if (strcmp (cache->dev, dev) == 0 && cache->start == sector)
+ {
+ sprintf (p, format, cache->partno);
+ strcpy (dev, real_dev);
+ return 1;
+ }
+ }
+
+ for (i = 1; i < 10000; i++)
+ {
+ int fd;
+ grub_disk_addr_t start;
+
+ sprintf (p, format, i);
+
+ fd = open (real_dev, O_RDONLY);
+ if (fd == -1)
+ continue;
+ close (fd);
+
+ start = find_partition_start (real_dev);
+ /* We don't care about errors here. */
+ grub_errno = GRUB_ERR_NONE;
+
+ if (start == sector)
+ {
+ struct linux_partition_cache *new_cache_item;
+
+ new_cache_item = xmalloc (sizeof *new_cache_item);
+ new_cache_item->dev = xstrdup (dev);
+ new_cache_item->start = start;
+ new_cache_item->partno = i;
+ grub_list_push (GRUB_AS_LIST_P (&linux_partition_cache_list),
+ GRUB_AS_LIST (new_cache_item));
+
+ strcpy (dev, real_dev);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+#endif /* __linux__ */
+
+static int
+open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags)
+{
+ int fd;
+ struct grub_util_biosdisk_data *data = disk->data;
+
+#ifdef O_LARGEFILE
+ flags |= O_LARGEFILE;
+#endif
+#ifdef O_SYNC
+ flags |= O_SYNC;
+#endif
+#ifdef O_FSYNC
+ flags |= O_FSYNC;
+#endif
+#ifdef O_BINARY
+ flags |= O_BINARY;
+#endif
+
+#ifdef __linux__
+ /* Linux has a bug that the disk cache for a whole disk is not consistent
+ with the one for a partition of the disk. */
+ {
+ int is_partition = 0;
+ char dev[PATH_MAX];
+ grub_disk_addr_t part_start = 0;
+
+ part_start = grub_partition_get_start (disk->partition);
+
+ strcpy (dev, map[disk->id].device);
+ if (disk->partition && sector >= part_start
+ && strncmp (map[disk->id].device, "/dev/", 5) == 0)
+ is_partition = linux_find_partition (dev, part_start);
+
+ if (data->dev && strcmp (data->dev, dev) == 0 &&
+ data->access_mode == (flags & O_ACCMODE))
+ {
+ grub_dprintf ("hostdisk", "reusing open device `%s'\n", dev);
+ fd = data->fd;
+ }
+ else
+ {
+ free (data->dev);
+ if (data->fd != -1)
+ {
+ if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
+ {
+ fsync (data->fd);
+#ifdef __linux__
+ if (data->is_disk)
+ ioctl (data->fd, BLKFLSBUF, 0);
+#endif
+ }
+
+ close (data->fd);
+ data->fd = -1;
+ }
+
+ /* Open the partition. */
+ grub_dprintf ("hostdisk", "opening the device `%s' in open_device()\n", dev);
+ fd = open (dev, flags);
+ if (fd < 0)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
+ return -1;
+ }
+
+ data->dev = xstrdup (dev);
+ data->access_mode = (flags & O_ACCMODE);
+ data->fd = fd;
+ }
+
+ if (is_partition)
+ sector -= part_start;
+ }
+#else /* ! __linux__ */
+#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
+ int sysctl_flags, sysctl_oldflags;
+ size_t sysctl_size = sizeof (sysctl_flags);
+
+ if (sysctlbyname ("kern.geom.debugflags", &sysctl_oldflags, &sysctl_size, NULL, 0))
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot get current flags of sysctl kern.geom.debugflags");
+ return -1;
+ }
+ sysctl_flags = sysctl_oldflags | 0x10;
+ if (! (sysctl_oldflags & 0x10)
+ && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_flags, sysctl_size))
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags of sysctl kern.geom.debugflags");
+ return -1;
+ }
+#endif
+
+ if (data->dev && strcmp (data->dev, map[disk->id].device) == 0 &&
+ data->access_mode == (flags & O_ACCMODE))
+ {
+ grub_dprintf ("hostdisk", "reusing open device `%s'\n", data->dev);
+ fd = data->fd;
+ }
+ else
+ {
+ free (data->dev);
+ if (data->fd != -1)
+ {
+ if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
+ {
+ fsync (data->fd);
+#ifdef __linux__
+ if (data->is_disk)
+ ioctl (data->fd, BLKFLSBUF, 0);
+#endif
+ }
+ close (data->fd);
+ data->fd = -1;
+ }
+
+ fd = open (map[disk->id].device, flags);
+ if (fd >= 0)
+ {
+ data->dev = xstrdup (map[disk->id].device);
+ data->access_mode = (flags & O_ACCMODE);
+ data->fd = fd;
+ }
+ }
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (! (sysctl_oldflags & 0x10)
+ && sysctlbyname ("kern.geom.debugflags", NULL , 0, &sysctl_oldflags, sysctl_size))
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot set flags back to the old value for sysctl kern.geom.debugflags");
+ return -1;
+ }
+#endif
+
+#if defined(__APPLE__)
+ /* If we can't have exclusive access, try shared access */
+ if (fd < 0)
+ fd = open(map[disk->id].device, flags | O_SHLOCK);
+#endif
+
+ if (fd < 0)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s' in open_device()", map[disk->id].device);
+ return -1;
+ }
+#endif /* ! __linux__ */
+
+#if defined(__NetBSD__)
+ configure_device_driver (fd);
+#endif /* defined(__NetBSD__) */
+
+#if defined(__linux__) && (!defined(__GLIBC__) || \
+ ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
+ /* Maybe libc doesn't have large file support. */
+ {
+ loff_t offset, result;
+ static int _llseek (uint filedes, ulong hi, ulong lo,
+ loff_t *res, uint wh);
+ _syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
+ loff_t *, res, uint, wh);
+
+ offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
+ if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
+ close (fd);
+ return -1;
+ }
+ }
+#else
+ {
+ off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id].device);
+ close (fd);
+ return -1;
+ }
+ }
+#endif
+
+ return fd;
+}
+
+/* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
+ error occurs, otherwise return LEN. */
+static ssize_t
+nread (int fd, char *buf, size_t len)
+{
+ ssize_t size = len;
+
+ while (len)
+ {
+ ssize_t ret = read (fd, buf, len);
+
+ if (ret <= 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ return ret;
+ }
+
+ len -= ret;
+ buf += ret;
+ }
+
+ return size;
+}
+
+/* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
+ error occurs, otherwise return LEN. */
+static ssize_t
+nwrite (int fd, const char *buf, size_t len)
+{
+ ssize_t size = len;
+
+ while (len)
+ {
+ ssize_t ret = write (fd, buf, len);
+
+ if (ret <= 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ return ret;
+ }
+
+ len -= ret;
+ buf += ret;
+ }
+
+ return size;
+}
+
+static grub_err_t
+grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
+ grub_size_t size, char *buf)
+{
+ int fd;
+
+ /* Split pre-partition and partition reads. */
+ if (disk->partition && sector < disk->partition->start
+ && sector + size > disk->partition->start)
+ {
+ grub_err_t err;
+ err = grub_util_biosdisk_read (disk, sector,
+ disk->partition->start - sector,
+ buf);
+ if (err)
+ return err;
+
+ return grub_util_biosdisk_read (disk, disk->partition->start,
+ size - (disk->partition->start - sector),
+ buf + ((disk->partition->start - sector)
+ << GRUB_DISK_SECTOR_BITS));
+ }
+
+ fd = open_device (disk, sector, O_RDONLY);
+ if (fd < 0)
+ return grub_errno;
+
+#ifdef __linux__
+ if (sector == 0 && size > 1)
+ {
+ /* Work around a bug in Linux ez remapping. Linux remaps all
+ sectors that are read together with the MBR in one read. It
+ should only remap the MBR, so we split the read in two
+ parts. -jochen */
+ if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
+ {
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id].device);
+ return grub_errno;
+ }
+
+ buf += GRUB_DISK_SECTOR_SIZE;
+ size--;
+ }
+#endif /* __linux__ */
+
+ if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
+ != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
+ grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id].device);
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
+ grub_size_t size, const char *buf)
+{
+ int fd;
+
+ /* Split pre-partition and partition writes. */
+ if (disk->partition && sector < disk->partition->start
+ && sector + size > disk->partition->start)
+ {
+ grub_err_t err;
+ err = grub_util_biosdisk_write (disk, sector,
+ disk->partition->start - sector,
+ buf);
+ if (err)
+ return err;
+
+ return grub_util_biosdisk_write (disk, disk->partition->start,
+ size - (disk->partition->start - sector),
+ buf + ((disk->partition->start - sector)
+ << GRUB_DISK_SECTOR_BITS));
+ }
+
+ fd = open_device (disk, sector, O_WRONLY);
+ if (fd < 0)
+ return grub_errno;
+
+ if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
+ != (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
+ grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id].device);
+
+ return grub_errno;
+}
+
+grub_err_t
+grub_util_biosdisk_flush (struct grub_disk *disk)
+{
+ struct grub_util_biosdisk_data *data = disk->data;
+
+ if (disk->dev->id != GRUB_DISK_DEVICE_BIOSDISK_ID)
+ return GRUB_ERR_NONE;
+ if (data->fd == -1)
+ {
+ data->fd = open_device (disk, 0, O_RDONLY);
+ if (data->fd < 0)
+ return grub_errno;
+ }
+ fsync (data->fd);
+#ifdef __linux__
+ if (data->is_disk)
+ ioctl (data->fd, BLKFLSBUF, 0);
+#endif
+ return GRUB_ERR_NONE;
+}
+
+static void
+grub_util_biosdisk_close (struct grub_disk *disk)
+{
+ struct grub_util_biosdisk_data *data = disk->data;
+
+ free (data->dev);
+ if (data->fd != -1)
+ {
+ if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY)
+ grub_util_biosdisk_flush (disk);
+ close (data->fd);
+ }
+ free (data);
+}
+
+static struct grub_disk_dev grub_util_biosdisk_dev =
+ {
+ .name = "biosdisk",
+ .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
+ .iterate = grub_util_biosdisk_iterate,
+ .open = grub_util_biosdisk_open,
+ .close = grub_util_biosdisk_close,
+ .read = grub_util_biosdisk_read,
+ .write = grub_util_biosdisk_write,
+ .next = 0
+ };
+
+static void
+read_device_map (const char *dev_map)
+{
+ FILE *fp;
+ char buf[1024]; /* XXX */
+ int lineno = 0;
+ struct stat st;
+
+ auto void show_error (const char *msg);
+ void show_error (const char *msg)
+ {
+ grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
+ }
+
+ fp = fopen (dev_map, "r");
+ if (! fp)
+ {
+ grub_util_info (_("cannot open `%s'"), dev_map);
+ return;
+ }
+
+ while (fgets (buf, sizeof (buf), fp))
+ {
+ char *p = buf;
+ char *e;
+ int drive;
+
+ lineno++;
+
+ /* Skip leading spaces. */
+ while (*p && isspace (*p))
+ p++;
+
+ /* If the first character is `#' or NUL, skip this line. */
+ if (*p == '\0' || *p == '#')
+ continue;
+
+ if (*p != '(')
+ show_error ("No open parenthesis found");
+
+ p++;
+ /* Find a free slot. */
+ drive = find_free_slot ();
+ if (drive < 0)
+ show_error ("Map table size exceeded");
+
+ e = p;
+ p = strchr (p, ')');
+ if (! p)
+ show_error ("No close parenthesis found");
+
+ map[drive].drive = xmalloc (p - e + sizeof ('\0'));
+ strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
+ map[drive].drive[p - e] = '\0';
+
+ p++;
+ /* Skip leading spaces. */
+ while (*p && isspace (*p))
+ p++;
+
+ if (*p == '\0')
+ show_error ("No filename found");
+
+ /* NUL-terminate the filename. */
+ e = p;
+ while (*e && ! isspace (*e))
+ e++;
+ *e = '\0';
+
+#ifdef __MINGW32__
+ (void) st;
+ if (grub_util_get_disk_size (p) == -1LL)
+#else
+ if (stat (p, &st) == -1)
+#endif
+ {
+ free (map[drive].drive);
+ map[drive].drive = NULL;
+ grub_util_info ("Cannot stat `%s', skipping", p);
+ continue;
+ }
+
+#ifdef __linux__
+ /* On Linux, the devfs uses symbolic links horribly, and that
+ confuses the interface very much, so use realpath to expand
+ symbolic links. Leave /dev/mapper/ alone, though. */
+ if (strncmp (p, "/dev/mapper/", 12) != 0)
+ {
+ map[drive].device = xmalloc (PATH_MAX);
+ if (! realpath (p, map[drive].device))
+ grub_util_error ("cannot get the real path of `%s'", p);
+ }
+ else
+#endif
+ map[drive].device = xstrdup (p);
+ }
+
+ fclose (fp);
+}
+
+void
+grub_util_biosdisk_init (const char *dev_map)
+{
+ read_device_map (dev_map);
+ grub_disk_dev_register (&grub_util_biosdisk_dev);
+}
+
+void
+grub_util_biosdisk_fini (void)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
+ {
+ if (map[i].drive)
+ free (map[i].drive);
+ if (map[i].device)
+ free (map[i].device);
+ map[i].drive = map[i].device = NULL;
+ }
+
+ grub_disk_dev_unregister (&grub_util_biosdisk_dev);
+}
+
+/*
+ * Note: we do not use the new partition naming scheme as dos_part does not
+ * necessarily correspond to an msdos partition.
+ */
+static char *
+make_device_name (int drive, int dos_part, int bsd_part)
+{
+ char *ret;
+ char *dos_part_str = NULL;
+ char *bsd_part_str = NULL;
+
+ if (dos_part >= 0)
+ dos_part_str = xasprintf (",%d", dos_part + 1);
+
+ if (bsd_part >= 0)
+ bsd_part_str = xasprintf (",%d", bsd_part + 1);
+
+ ret = xasprintf ("%s%s%s", map[drive].drive,
+ dos_part_str ? : "",
+ bsd_part_str ? : "");
+
+ if (dos_part_str)
+ free (dos_part_str);
+
+ if (bsd_part_str)
+ free (bsd_part_str);
+
+ return ret;
+}
+
+static char *
+convert_system_partition_to_system_disk (const char *os_dev, struct stat *st)
+{
+#if defined(__linux__)
+ char *path = xmalloc (PATH_MAX);
+ if (! realpath (os_dev, path))
+ return NULL;
+
+ if (strncmp ("/dev/", path, 5) == 0)
+ {
+ char *p = path + 5;
+
+ /* If this is an IDE disk. */
+ if (strncmp ("ide/", p, 4) == 0)
+ {
+ p = strstr (p, "part");
+ if (p)
+ strcpy (p, "disc");
+
+ return path;
+ }
+
+ /* If this is a SCSI disk. */
+ if (strncmp ("scsi/", p, 5) == 0)
+ {
+ p = strstr (p, "part");
+ if (p)
+ strcpy (p, "disc");
+
+ return path;
+ }
+
+ /* If this is a DAC960 disk. */
+ if (strncmp ("rd/c", p, 4) == 0)
+ {
+ /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+
+ /* If this is a Mylex AcceleRAID Array. */
+ if (strncmp ("rs/c", p, 4) == 0)
+ {
+ /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+ /* If this is a CCISS disk. */
+ if (strncmp ("cciss/c", p, sizeof ("cciss/c") - 1) == 0)
+ {
+ /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+
+ /* If this is a Compaq Intelligent Drive Array. */
+ if (strncmp ("ida/c", p, sizeof ("ida/c") - 1) == 0)
+ {
+ /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+
+ /* If this is an I2O disk. */
+ if (strncmp ("i2o/hd", p, sizeof ("i2o/hd") - 1) == 0)
+ {
+ /* /dev/i2o/hd[a-z]([0-9]+)? */
+ p[sizeof ("i2o/hda") - 1] = '\0';
+ return path;
+ }
+
+ /* If this is a MultiMediaCard (MMC). */
+ if (strncmp ("mmcblk", p, sizeof ("mmcblk") - 1) == 0)
+ {
+ /* /dev/mmcblk[0-9]+(p[0-9]+)? */
+ p = strchr (p, 'p');
+ if (p)
+ *p = '\0';
+
+ return path;
+ }
+
+ if (strncmp ("md", p, 2) == 0
+ && p[2] >= '0' && p[2] <= '9')
+ {
+ char *ptr = p + 2;
+ while (*ptr >= '0' && *ptr <= '9')
+ ptr++;
+ *ptr = 0;
+ return path;
+ }
+
+ /* If this is an IDE, SCSI or Virtio disk. */
+ if (strncmp ("vdisk", p, 5) == 0
+ && p[5] >= 'a' && p[5] <= 'z')
+ {
+ /* /dev/vdisk[a-z][0-9]* */
+ p[6] = '\0';
+ return path;
+ }
+ if ((strncmp ("hd", p, 2) == 0
+ || strncmp ("vd", p, 2) == 0
+ || strncmp ("sd", p, 2) == 0)
+ && p[2] >= 'a' && p[2] <= 'z')
+ {
+ char *pp = p + 2;
+ while (*pp >= 'a' && *pp <= 'z')
+ pp++;
+ /* /dev/[hsv]d[a-z]+[0-9]* */
+ *pp = '\0';
+ return path;
+ }
+
+ /* If this is a Xen virtual block device. */
+ if ((strncmp ("xvd", p, 3) == 0) && p[3] >= 'a' && p[3] <= 'z')
+ {
+ char *pp = p + 3;
+ while (*pp >= 'a' && *pp <= 'z')
+ pp++;
+ /* /dev/xvd[a-z]+[0-9]* */
+ *pp = '\0';
+ return path;
+ }
+
+#ifdef HAVE_DEVICE_MAPPER
+ /* If this is a DM-RAID device.
+ Compare os_dev rather than path here, since nodes under
+ /dev/mapper/ are often symlinks. */
+ if ((strncmp ("/dev/mapper/", os_dev, 12) == 0))
+ {
+ struct dm_tree *tree;
+ uint32_t maj, min;
+ struct dm_tree_node *node = NULL, *child;
+ void *handle;
+ const char *node_uuid, *mapper_name = NULL, *child_uuid, *child_name;
+
+ tree = dm_tree_create ();
+ if (! tree)
+ {
+ grub_dprintf ("hostdisk", "dm_tree_create failed\n");
+ goto devmapper_out;
+ }
+
+ maj = major (st->st_rdev);
+ min = minor (st->st_rdev);
+ if (! dm_tree_add_dev (tree, maj, min))
+ {
+ grub_dprintf ("hostdisk", "dm_tree_add_dev failed\n");
+ goto devmapper_out;
+ }
+
+ node = dm_tree_find_node (tree, maj, min);
+ if (! node)
+ {
+ grub_dprintf ("hostdisk", "dm_tree_find_node failed\n");
+ goto devmapper_out;
+ }
+ node_uuid = dm_tree_node_get_uuid (node);
+ if (! node_uuid)
+ {
+ grub_dprintf ("hostdisk", "%s has no DM uuid\n", path);
+ node = NULL;
+ goto devmapper_out;
+ }
+ else if (strncmp (node_uuid, "DMRAID-", 7) != 0)
+ {
+ grub_dprintf ("hostdisk", "%s is not DM-RAID\n", path);
+ node = NULL;
+ goto devmapper_out;
+ }
+
+ handle = NULL;
+ /* Counter-intuitively, device-mapper refers to the disk-like
+ device containing a DM-RAID partition device as a "child" of
+ the partition device. */
+ child = dm_tree_next_child (&handle, node, 0);
+ if (! child)
+ {
+ grub_dprintf ("hostdisk", "%s has no DM children\n", path);
+ goto devmapper_out;
+ }
+ child_uuid = dm_tree_node_get_uuid (child);
+ if (! child_uuid)
+ {
+ grub_dprintf ("hostdisk", "%s child has no DM uuid\n", path);
+ goto devmapper_out;
+ }
+ else if (strncmp (child_uuid, "DMRAID-", 7) != 0)
+ {
+ grub_dprintf ("hostdisk", "%s child is not DM-RAID\n", path);
+ goto devmapper_out;
+ }
+ child_name = dm_tree_node_get_name (child);
+ if (! child_name)
+ {
+ grub_dprintf ("hostdisk", "%s child has no DM name\n", path);
+ goto devmapper_out;
+ }
+ mapper_name = child_name;
+
+devmapper_out:
+ if (! mapper_name && node)
+ {
+ /* This is a DM-RAID disk, not a partition. */
+ mapper_name = dm_tree_node_get_name (node);
+ if (! mapper_name)
+ grub_dprintf ("hostdisk", "%s has no DM name\n", path);
+ }
+ if (tree)
+ dm_tree_free (tree);
+ free (path);
+ if (mapper_name)
+ return xasprintf ("/dev/mapper/%s", mapper_name);
+ else
+ return NULL;
+ }
+#endif /* HAVE_DEVICE_MAPPER */
+ }
+
+ return path;
+
+#elif defined(__GNU__)
+ char *path = xstrdup (os_dev);
+ if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
+ {
+ char *p = strchr (path + 7, 's');
+ if (p)
+ *p = '\0';
+ }
+ return path;
+
+#elif defined(__CYGWIN__)
+ char *path = xstrdup (os_dev);
+ if (strncmp ("/dev/sd", path, 7) == 0 && 'a' <= path[7] && path[7] <= 'z')
+ path[8] = 0;
+ return path;
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ char *out, *out2;
+ if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
+ return xstrdup (os_dev);
+ follow_geom_up (os_dev + sizeof ("/dev/") - 1, NULL, &out);
+
+ out2 = xasprintf ("/dev/%s", out);
+ free (out);
+
+ return out2;
+#elif defined(__APPLE__)
+ char *path = xstrdup (os_dev);
+ if (strncmp ("/dev/", path, 5) == 0)
+ {
+ char *p;
+ for (p = path + 5; *p; ++p)
+ if (grub_isdigit(*p))
+ {
+ p = strpbrk (p, "sp");
+ if (p)
+ *p = '\0';
+ break;
+ }
+ }
+ return path;
+
+#elif defined(__NetBSD__)
+ /* NetBSD uses "/dev/r[a-z]+[0-9][a-z]". */
+ char *path = xstrdup (os_dev);
+ if (strncmp ("/dev/r", path, sizeof("/dev/r") - 1) == 0 &&
+ (path[sizeof("/dev/r") - 1] >= 'a' && path[sizeof("/dev/r") - 1] <= 'z') &&
+ strncmp ("fd", path + sizeof("/dev/r") - 1, sizeof("fd") - 1) != 0) /* not a floppy device name */
+ {
+ char *p;
+ for (p = path + sizeof("/dev/r"); *p >= 'a' && *p <= 'z'; p++);
+ if (grub_isdigit(*p))
+ {
+ p++;
+ if ((*p >= 'a' && *p <= 'z') && (*(p+1) == '\0'))
+ {
+ /* path matches the required regular expression and
+ p points to its last character. */
+ int rawpart = -1;
+# ifdef HAVE_GETRAWPARTITION
+ rawpart = getrawpartition();
+# endif /* HAVE_GETRAWPARTITION */
+ if (rawpart >= 0)
+ *p = 'a' + rawpart;
+ }
+ }
+ }
+ return path;
+
+#else
+# warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
+ return xstrdup (os_dev);
+#endif
+}
+
+#if defined(__linux__) || defined(__CYGWIN__)
+static int
+device_is_wholedisk (const char *os_dev)
+{
+ int len = strlen (os_dev);
+
+ if (os_dev[len - 1] < '0' || os_dev[len - 1] > '9')
+ return 1;
+ return 0;
+}
+#endif
+
+#if defined(__NetBSD__)
+/* Try to determine whether a given device name corresponds to a whole disk.
+ This function should give in most cases a definite answer, but it may
+ actually give an approximate one in the following sense: if the return
+ value is 0 then the device name does not correspond to a whole disk. */
+static int
+device_is_wholedisk (const char *os_dev)
+{
+ int len = strlen (os_dev);
+ int rawpart = -1;
+
+# ifdef HAVE_GETRAWPARTITION
+ rawpart = getrawpartition();
+# endif /* HAVE_GETRAWPARTITION */
+ if (rawpart < 0)
+ return 1;
+ return (os_dev[len - 1] == ('a' + rawpart));
+}
+#endif /* defined(__NetBSD__) */
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+static int
+device_is_wholedisk (const char *os_dev)
+{
+ const char *p;
+
+ if (strncmp (os_dev, "/dev/", sizeof ("/dev/") - 1) != 0)
+ return 0;
+
+ for (p = os_dev + sizeof ("/dev/") - 1; *p; ++p)
+ if (grub_isdigit (*p))
+ {
+ if (strchr (p, 's'))
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+#endif /* defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
+
+static int
+find_system_device (const char *os_dev, struct stat *st, int convert, int add)
+{
+ unsigned int i;
+ char *os_disk;
+
+ if (convert)
+ os_disk = convert_system_partition_to_system_disk (os_dev, st);
+ else
+ os_disk = xstrdup (os_dev);
+ if (! os_disk)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE (map); i++)
+ if (! map[i].device)
+ break;
+ else if (strcmp (map[i].device, os_disk) == 0)
+ {
+ free (os_disk);
+ return i;
+ }
+
+ if (!add)
+ return -1;
+
+ if (i == ARRAY_SIZE (map))
+ grub_util_error (_("device count exceeds limit"));
+
+ map[i].device = os_disk;
+ map[i].drive = xstrdup (os_disk);
+
+ return i;
+}
+
+int
+grub_util_biosdisk_is_present (const char *os_dev)
+{
+ struct stat st;
+
+ if (stat (os_dev, &st) < 0)
+ return 0;
+
+ return find_system_device (os_dev, &st, 1, 0) != -1;
+}
+
+char *
+grub_util_biosdisk_get_grub_dev (const char *os_dev)
+{
+ struct stat st;
+ int drive;
+
+ if (stat (os_dev, &st) < 0)
+ {
+ grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
+ grub_util_info ("cannot stat `%s'", os_dev);
+ return 0;
+ }
+
+ drive = find_system_device (os_dev, &st, 1, 1);
+ if (drive < 0)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ "no mapping exists for `%s'", os_dev);
+ grub_util_info ("no mapping exists for `%s'", os_dev);
+ return 0;
+ }
+
+ if (grub_strcmp (os_dev,
+ convert_system_partition_to_system_disk (os_dev, &st)) == 0)
+ return make_device_name (drive, -1, -1);
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__NetBSD__)
+ if (! S_ISCHR (st.st_mode))
+#else
+ if (! S_ISBLK (st.st_mode))
+#endif
+ return make_device_name (drive, -1, -1);
+
+#if defined(__linux__) || defined(__CYGWIN__) || defined(HAVE_DIOCGDINFO) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+
+ /* Linux counts partitions uniformly, whether a BSD partition or a DOS
+ partition, so mapping them to GRUB devices is not trivial.
+ Here, get the start sector of a partition by HDIO_GETGEO, and
+ compare it with each partition GRUB recognizes.
+
+ Cygwin /dev/sdXN emulation uses Windows partition mapping. It
+ does not count the extended partition and missing primary
+ partitions. Use same method as on Linux here.
+
+ For NetBSD and FreeBSD, proceed as for Linux, except that the start
+ sector is obtained from the disk label. */
+ {
+ char *name, *partname;
+ grub_disk_t disk;
+ grub_disk_addr_t start;
+ auto int find_partition (grub_disk_t dsk,
+ const grub_partition_t partition);
+
+ int find_partition (grub_disk_t dsk __attribute__ ((unused)),
+ const grub_partition_t partition)
+ {
+ grub_disk_addr_t part_start = 0;
+ grub_util_info ("Partition %d starts from %lu",
+ partition->number, partition->start);
+
+ part_start = grub_partition_get_start (partition);
+
+ if (start == part_start)
+ {
+ partname = grub_partition_get_name (partition);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ name = make_device_name (drive, -1, -1);
+
+# if !defined(HAVE_DIOCGDINFO)
+ if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
+ return name;
+# else /* defined(HAVE_DIOCGDINFO) */
+ /* Since os_dev and convert_system_partition_to_system_disk (os_dev) are
+ * different, we know that os_dev cannot be a floppy device. */
+# endif /* !defined(HAVE_DIOCGDINFO) */
+
+ start = find_partition_start (os_dev);
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ free (name);
+ return 0;
+ }
+
+ grub_util_info ("%s starts from %lu", os_dev, start);
+
+ if (start == 0 && device_is_wholedisk (os_dev))
+ return name;
+
+ grub_util_info ("opening the device %s", name);
+ disk = grub_disk_open (name);
+ free (name);
+
+ if (! disk)
+ {
+ /* We already know that the partition exists. Given that we already
+ checked the device map above, we can only get
+ GRUB_ERR_UNKNOWN_DEVICE at this point if the disk does not exist.
+ This can happen on Xen, where disk images in the host can be
+ assigned to devices that have partition-like names in the guest
+ but are really more like disks. */
+ if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
+ {
+ grub_util_warn
+ ("disk does not exist, so falling back to partition device %s",
+ os_dev);
+
+ drive = find_system_device (os_dev, &st, 0, 1);
+ if (drive < 0)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ "no mapping exists for `%s'", os_dev);
+ return 0;
+ }
+
+ return make_device_name (drive, -1, -1);
+ }
+ else
+ return 0;
+ }
+
+ partname = NULL;
+ grub_partition_iterate (disk, find_partition);
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_disk_close (disk);
+ return 0;
+ }
+
+ if (partname == NULL)
+ {
+ grub_disk_close (disk);
+ grub_error (GRUB_ERR_BAD_DEVICE,
+ "cannot find the partition of `%s'", os_dev);
+ return 0;
+ }
+
+ name = grub_xasprintf ("%s,%s", disk->name, partname);
+ free (partname);
+ return name;
+ }
+
+#elif defined(__GNU__)
+ /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
+ {
+ char *p;
+ int dos_part = -1;
+ int bsd_part = -1;
+
+ p = strrchr (os_dev, 's');
+ if (p)
+ {
+ long int n;
+ char *q;
+
+ p++;
+ n = strtol (p, &q, 10);
+ if (p != q && n != GRUB_LONG_MIN && n != GRUB_LONG_MAX)
+ {
+ dos_part = (int) n - 1;
+
+ if (*q >= 'a' && *q <= 'g')
+ bsd_part = *q - 'a';
+ }
+ }
+
+ return make_device_name (drive, dos_part, bsd_part);
+ }
+
+#else
+# warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
+ return make_device_name (drive, -1, -1);
+#endif
+}
+
+const char *
+grub_util_biosdisk_get_osdev (grub_disk_t disk)
+{
+ return map[disk->id].device;
+}
+
+int
+grub_util_biosdisk_is_floppy (grub_disk_t disk)
+{
+ struct stat st;
+ int fd;
+
+ fd = open (map[disk->id].device, O_RDONLY);
+ /* Shouldn't happen. */
+ if (fd == -1)
+ return 0;
+
+ /* Shouldn't happen either. */
+ if (fstat (fd, &st) < 0)
+ return 0;
+
+#if defined(__NetBSD__)
+ if (major(st.st_rdev) == RAW_FLOPPY_MAJOR)
+ return 1;
+#endif
+
+#if defined(FLOPPY_MAJOR)
+ if (major(st.st_rdev) == FLOPPY_MAJOR)
+#else
+ /* Some kernels (e.g. kFreeBSD) don't have a static major number
+ for floppies, but they still use a "fd[0-9]" pathname. */
+ if (map[disk->id].device[5] == 'f'
+ && map[disk->id].device[6] == 'd'
+ && map[disk->id].device[7] >= '0'
+ && map[disk->id].device[7] <= '9')
+#endif
+ return 1;
+
+ return 0;
+}
diff --git a/grub-core/kern/emu/hostfs.c b/grub-core/kern/emu/hostfs.c
new file mode 100644
index 0000000..501ad46
--- /dev/null
+++ b/grub-core/kern/emu/hostfs.c
@@ -0,0 +1,175 @@
+/* hostfs.c - Dummy filesystem to provide access to the hosts filesystem */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/>.
+ */
+#define _BSD_SOURCE
+#include <grub/fs.h>
+#include <grub/file.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/dl.h>
+#include <grub/util/misc.h>
+
+#include <dirent.h>
+#include <stdio.h>
+#include <errno.h>
+
+
+/* dirent.d_type is a BSD extension, not part of POSIX */
+#include <sys/stat.h>
+#include <string.h>
+
+static int
+is_dir (const char *path, const char *name)
+{
+ int len1 = strlen(path);
+ int len2 = strlen(name);
+
+ char pathname[len1 + 1 + len2 + 1 + 13];
+ strcpy (pathname, path);
+
+ /* Avoid UNC-path "//name" on Cygwin. */
+ if (len1 > 0 && pathname[len1 - 1] != '/')
+ strcat (pathname, "/");
+
+ strcat (pathname, name);
+
+ struct stat st;
+ if (stat (pathname, &st))
+ return 0;
+ return S_ISDIR (st.st_mode);
+}
+
+static grub_err_t
+grub_hostfs_dir (grub_device_t device, const char *path,
+ int (*hook) (const char *filename,
+ const struct grub_dirhook_info *info))
+{
+ DIR *dir;
+
+ /* Check if the disk is our dummy disk. */
+ if (grub_strcmp (device->disk->name, "host"))
+ return grub_error (GRUB_ERR_BAD_FS, "not a hostfs");
+
+ dir = opendir (path);
+ if (! dir)
+ return grub_error (GRUB_ERR_BAD_FILENAME,
+ "can't open the hostfs directory `%s'", path);
+
+ while (1)
+ {
+ struct dirent *de;
+ struct grub_dirhook_info info;
+ grub_memset (&info, 0, sizeof (info));
+
+ de = readdir (dir);
+ if (! de)
+ break;
+
+ info.dir = !! is_dir (path, de->d_name);
+ hook (de->d_name, &info);
+
+ }
+
+ closedir (dir);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Open a file named NAME and initialize FILE. */
+static grub_err_t
+grub_hostfs_open (struct grub_file *file, const char *name)
+{
+ FILE *f;
+
+ f = fopen (name, "rb");
+ if (! f)
+ return grub_error (GRUB_ERR_BAD_FILENAME,
+ "can't open `%s'", name);
+ file->data = f;
+
+#ifdef __MINGW32__
+ file->size = grub_util_get_disk_size (name);
+#else
+ fseeko (f, 0, SEEK_END);
+ file->size = ftello (f);
+ fseeko (f, 0, SEEK_SET);
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_ssize_t
+grub_hostfs_read (grub_file_t file, char *buf, grub_size_t len)
+{
+ FILE *f;
+
+ f = (FILE *) file->data;
+ if (fseeko (f, file->offset, SEEK_SET) != 0)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "fseeko: %s", strerror (errno));
+ return -1;
+ }
+
+ unsigned int s = fread (buf, 1, len, f);
+ if (s != len)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, "fread: %s", strerror (errno));
+
+ return (signed) s;
+}
+
+static grub_err_t
+grub_hostfs_close (grub_file_t file)
+{
+ FILE *f;
+
+ f = (FILE *) file->data;
+ fclose (f);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_hostfs_label (grub_device_t device __attribute ((unused)),
+ char **label __attribute ((unused)))
+{
+ *label = 0;
+ return GRUB_ERR_NONE;
+}
+
+static struct grub_fs grub_hostfs_fs =
+ {
+ .name = "hostfs",
+ .dir = grub_hostfs_dir,
+ .open = grub_hostfs_open,
+ .read = grub_hostfs_read,
+ .close = grub_hostfs_close,
+ .label = grub_hostfs_label,
+ .next = 0
+ };
+
+
+
+GRUB_MOD_INIT(hostfs)
+{
+ grub_fs_register (&grub_hostfs_fs);
+}
+
+GRUB_MOD_FINI(hostfs)
+{
+ grub_fs_unregister (&grub_hostfs_fs);
+}
diff --git a/grub-core/kern/emu/lite.c b/grub-core/kern/emu/lite.c
new file mode 100644
index 0000000..32e12a0
--- /dev/null
+++ b/grub-core/kern/emu/lite.c
@@ -0,0 +1,45 @@
+#include <config.h>
+#include <grub/emu/misc.h>
+
+#ifndef GRUB_MACHINE_EMU
+#error "This source is only meant for grub-emu platform"
+#endif
+
+#if defined(__i386__)
+#include "../i386/dl.c"
+#elif defined(__x86_64__)
+#include "../x86_64/dl.c"
+#elif defined(__sparc__)
+#include "../sparc64/dl.c"
+#elif defined(__mips__)
+#include "../mips/dl.c"
+#elif defined(__powerpc__)
+#include "../powerpc/dl.c"
+#else
+#error "No target cpu type is defined"
+#endif
+
+/* grub-emu-lite supports dynamic module loading, so it won't have any
+ embedded modules. */
+void
+grub_init_all (void)
+{
+ return;
+}
+
+void
+grub_fini_all (void)
+{
+ return;
+}
+
+void
+grub_emu_init (void)
+{
+ return;
+}
+
+void
+grub_emu_post_init (void)
+{
+}
diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
new file mode 100644
index 0000000..0a76459
--- /dev/null
+++ b/grub-core/kern/emu/main.c
@@ -0,0 +1,259 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,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 <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/setjmp.h>
+#include <grub/fs.h>
+#include <grub/emu/hostdisk.h>
+#include <grub/time.h>
+#include <grub/emu/console.h>
+#include <grub/emu/misc.h>
+#include <grub/kernel.h>
+#include <grub/normal.h>
+#include <grub/emu/getroot.h>
+#include <grub/env.h>
+#include <grub/partition.h>
+#include <grub/i18n.h>
+
+#include "progname.h"
+
+#define ENABLE_RELOCATABLE 0
+
+/* Used for going back to the main function. */
+static jmp_buf main_env;
+
+/* Store the prefix specified by an argument. */
+static char *prefix = NULL;
+
+int grub_no_autoload;
+
+grub_addr_t
+grub_arch_modules_addr (void)
+{
+ return 0;
+}
+
+void
+grub_reboot (void)
+{
+ longjmp (main_env, 1);
+}
+
+void
+grub_machine_init (void)
+{
+}
+
+void
+grub_machine_set_prefix (void)
+{
+ grub_env_set ("prefix", prefix);
+ free (prefix);
+ prefix = 0;
+}
+
+void
+grub_machine_fini (void)
+{
+ grub_console_fini ();
+}
+
+
+
+static struct option options[] =
+ {
+ {"root-device", required_argument, 0, 'r'},
+ {"device-map", required_argument, 0, 'm'},
+ {"directory", required_argument, 0, 'd'},
+ {"hold", optional_argument, 0, 'H'},
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {"verbose", no_argument, 0, 'v'},
+ { 0, 0, 0, 0 }
+ };
+
+static int
+usage (int status)
+{
+ if (status)
+ fprintf (stderr,
+ "Try `%s --help' for more information.\n", program_name);
+ else
+ printf (
+ "Usage: %s [OPTION]...\n"
+ "\n"
+ "GRUB emulator.\n"
+ "\n"
+ " -r, --root-device=DEV use DEV as the root device [default=guessed]\n"
+ " -m, --device-map=FILE use FILE as the device map [default=%s]\n"
+ " -d, --directory=DIR use GRUB files in the directory DIR [default=%s]\n"
+ " -v, --verbose print verbose messages\n"
+ " -H, --hold[=SECONDS] wait until a debugger will attach\n"
+ " -h, --help display this message and exit\n"
+ " -V, --version print version information and exit\n"
+ "\n"
+ "Report bugs to <%s>.\n", program_name, DEFAULT_DEVICE_MAP, DEFAULT_DIRECTORY, PACKAGE_BUGREPORT);
+ return status;
+}
+
+
+void grub_hostfs_init (void);
+void grub_hostfs_fini (void);
+void grub_host_init (void);
+void grub_host_fini (void);
+void grub_emu_init (void);
+
+int
+main (int argc, char *argv[])
+{
+ char *root_dev = 0;
+ char *dir = DEFAULT_DIRECTORY;
+ char *dev_map = DEFAULT_DEVICE_MAP;
+ volatile int hold = 0;
+ int opt;
+
+ set_program_name (argv[0]);
+
+ while ((opt = getopt_long (argc, argv, "r:d:m:vH:hV", options, 0)) != -1)
+ switch (opt)
+ {
+ case 'r':
+ root_dev = optarg;
+ break;
+ case 'd':
+ dir = optarg;
+ break;
+ case 'm':
+ dev_map = optarg;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'H':
+ hold = (optarg ? atoi (optarg) : -1);
+ break;
+ case 'h':
+ return usage (0);
+ case 'V':
+ printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
+ return 0;
+ default:
+ return usage (1);
+ }
+
+ if (optind < argc)
+ {
+ fprintf (stderr, "Unknown extra argument `%s'.\n", argv[optind]);
+ return usage (1);
+ }
+
+ /* Wait until the ARGS.HOLD variable is cleared by an attached debugger. */
+ if (hold && verbosity > 0)
+ printf ("Run \"gdb %s %d\", and set ARGS.HOLD to zero.\n",
+ program_name, (int) getpid ());
+ while (hold)
+ {
+ if (hold > 0)
+ hold--;
+
+ sleep (1);
+ }
+
+ signal (SIGINT, SIG_IGN);
+ grub_emu_init ();
+ grub_console_init ();
+ grub_host_init ();
+
+ /* XXX: This is a bit unportable. */
+ grub_util_biosdisk_init (dev_map);
+
+ grub_init_all ();
+
+ grub_hostfs_init ();
+
+ grub_emu_post_init ();
+
+ /* Make sure that there is a root device. */
+ if (! root_dev)
+ {
+ char *device_name = grub_guess_root_device (dir);
+ if (! device_name)
+ grub_util_error ("cannot find a device for %s", dir);
+
+ root_dev = grub_util_get_grub_dev (device_name);
+ if (! root_dev)
+ {
+ grub_util_info ("guessing the root device failed, because of `%s'",
+ grub_errmsg);
+ grub_util_error ("cannot guess the root device. Specify the option `--root-device'");
+ }
+ }
+
+ if (strcmp (root_dev, "host") == 0)
+ dir = xstrdup (dir);
+ else
+ dir = grub_make_system_path_relative_to_its_root (dir);
+ prefix = xmalloc (strlen (root_dev) + 2 + strlen (dir) + 1);
+ sprintf (prefix, "(%s)%s", root_dev, dir);
+ free (dir);
+
+ /* Start GRUB! */
+ if (setjmp (main_env) == 0)
+ grub_main ();
+
+ grub_fini_all ();
+ grub_hostfs_fini ();
+ grub_host_fini ();
+
+ grub_machine_fini ();
+
+ return 0;
+}
+
+#ifdef __MINGW32__
+
+void
+grub_millisleep (grub_uint32_t ms)
+{
+ Sleep (ms);
+}
+
+#else
+
+void
+grub_millisleep (grub_uint32_t ms)
+{
+ struct timespec ts;
+
+ ts.tv_sec = ms / 1000;
+ ts.tv_nsec = (ms % 1000) * 1000000;
+ nanosleep (&ts, NULL);
+}
+
+#endif
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
new file mode 100644
index 0000000..44c40b0
--- /dev/null
+++ b/grub-core/kern/emu/misc.c
@@ -0,0 +1,537 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005,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 <config-util.h>
+#include <config.h>
+
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/env.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/i18n.h>
+#include <grub/time.h>
+#include <grub/emu/misc.h>
+
+#ifdef HAVE_DEVICE_MAPPER
+# include <libdevmapper.h>
+#endif
+
+#ifdef HAVE_LIBZFS
+# include <grub/util/libzfs.h>
+#endif
+
+#ifdef HAVE_LIBNVPAIR
+# include <grub/util/libnvpair.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_MOUNT_H
+# include <sys/mount.h>
+#endif
+
+#ifdef HAVE_SYS_MNTTAB_H
+# include <stdio.h> /* Needed by sys/mnttab.h. */
+# include <sys/mnttab.h>
+#endif
+
+#ifdef HAVE_SYS_MKDEV_H
+# include <sys/mkdev.h> /* makedev */
+#endif
+
+int verbosity;
+
+void
+grub_util_warn (const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf (stderr, _("%s: warn:"), program_name);
+ fprintf (stderr, " ");
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fprintf (stderr, ".\n");
+ fflush (stderr);
+}
+
+void
+grub_util_info (const char *fmt, ...)
+{
+ if (verbosity > 0)
+ {
+ va_list ap;
+
+ fprintf (stderr, _("%s: info:"), program_name);
+ fprintf (stderr, " ");
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fprintf (stderr, ".\n");
+ fflush (stderr);
+ }
+}
+
+void
+grub_util_error (const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf (stderr, _("%s: error:"), program_name);
+ fprintf (stderr, " ");
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fprintf (stderr, ".\n");
+ exit (1);
+}
+
+void *
+xmalloc (grub_size_t size)
+{
+ void *p;
+
+ p = malloc (size);
+ if (! p)
+ grub_util_error ("out of memory");
+
+ return p;
+}
+
+void *
+xrealloc (void *ptr, grub_size_t size)
+{
+ ptr = realloc (ptr, size);
+ if (! ptr)
+ grub_util_error ("out of memory");
+
+ return ptr;
+}
+
+char *
+xstrdup (const char *str)
+{
+ size_t len;
+ char *newstr;
+
+ len = strlen (str);
+ newstr = (char *) xmalloc (len + 1);
+ memcpy (newstr, str, len + 1);
+
+ return newstr;
+}
+
+#ifndef HAVE_VASPRINTF
+
+int
+vasprintf (char **buf, const char *fmt, va_list ap)
+{
+ /* Should be large enough. */
+ *buf = xmalloc (512);
+
+ return vsnprintf (*buf, 512, fmt, ap);
+}
+
+#endif
+
+#ifndef HAVE_ASPRINTF
+
+int
+asprintf (char **buf, const char *fmt, ...)
+{
+ int status;
+ va_list ap;
+
+ va_start (ap, fmt);
+ status = vasprintf (buf, fmt, ap);
+ va_end (ap);
+
+ return status;
+}
+
+#endif
+
+char *
+xasprintf (const char *fmt, ...)
+{
+ va_list ap;
+ char *result;
+
+ va_start (ap, fmt);
+ if (vasprintf (&result, fmt, ap) < 0)
+ {
+ if (errno == ENOMEM)
+ grub_util_error ("out of memory");
+ return NULL;
+ }
+
+ return result;
+}
+
+void
+grub_exit (void)
+{
+ exit (1);
+}
+
+grub_uint64_t
+grub_get_time_ms (void)
+{
+ struct timeval tv;
+
+ gettimeofday (&tv, 0);
+
+ return (tv.tv_sec * 1000 + tv.tv_usec / 1000);
+}
+
+grub_uint32_t
+grub_get_rtc (void)
+{
+ struct timeval tv;
+
+ gettimeofday (&tv, 0);
+
+ return (tv.tv_sec * GRUB_TICKS_PER_SECOND
+ + (((tv.tv_sec % GRUB_TICKS_PER_SECOND) * 1000000 + tv.tv_usec)
+ * GRUB_TICKS_PER_SECOND / 1000000));
+}
+
+char *
+canonicalize_file_name (const char *path)
+{
+ char *ret;
+#ifdef PATH_MAX
+ ret = xmalloc (PATH_MAX);
+ if (!realpath (path, ret))
+ return NULL;
+#else
+ ret = realpath (path, NULL);
+#endif
+ return ret;
+}
+
+#ifdef __CYGWIN__
+/* Convert POSIX path to Win32 path,
+ remove drive letter, replace backslashes. */
+static char *
+get_win32_path (const char *path)
+{
+ char winpath[PATH_MAX];
+ if (cygwin_conv_path (CCP_POSIX_TO_WIN_A, path, winpath, sizeof(winpath)))
+ grub_util_error ("cygwin_conv_path() failed");
+
+ int len = strlen (winpath);
+ int offs = (len > 2 && winpath[1] == ':' ? 2 : 0);
+
+ int i;
+ for (i = offs; i < len; i++)
+ if (winpath[i] == '\\')
+ winpath[i] = '/';
+ return xstrdup (winpath + offs);
+}
+#endif
+
+#ifdef HAVE_LIBZFS
+static libzfs_handle_t *__libzfs_handle;
+
+static void
+fini_libzfs (void)
+{
+ libzfs_fini (__libzfs_handle);
+}
+
+libzfs_handle_t *
+grub_get_libzfs_handle (void)
+{
+ if (! __libzfs_handle)
+ {
+ __libzfs_handle = libzfs_init ();
+
+ if (__libzfs_handle)
+ atexit (fini_libzfs);
+ }
+
+ return __libzfs_handle;
+}
+#endif /* HAVE_LIBZFS */
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+/* ZFS has similar problems to those of btrfs (see above). */
+void
+grub_find_zpool_from_dir (const char *dir, char **poolname, char **poolfs)
+{
+ char *slash;
+
+ *poolname = *poolfs = NULL;
+
+#if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) && defined(HAVE_STRUCT_STATFS_F_MNTFROMNAME)
+ /* FreeBSD and GNU/kFreeBSD. */
+ {
+ struct statfs mnt;
+
+ if (statfs (dir, &mnt) != 0)
+ return;
+
+ if (strcmp (mnt.f_fstypename, "zfs") != 0)
+ return;
+
+ *poolname = xstrdup (mnt.f_mntfromname);
+ }
+#elif defined(HAVE_GETEXTMNTENT)
+ /* Solaris. */
+ {
+ struct stat st;
+ struct extmnttab mnt;
+
+ if (stat (dir, &st) != 0)
+ return;
+
+ FILE *mnttab = fopen ("/etc/mnttab", "r");
+ if (! mnttab)
+ return;
+
+ while (getextmntent (mnttab, &mnt, sizeof (mnt)) == 0)
+ {
+ if (makedev (mnt.mnt_major, mnt.mnt_minor) == st.st_dev
+ && !strcmp (mnt.mnt_fstype, "zfs"))
+ {
+ *poolname = xstrdup (mnt.mnt_special);
+ break;
+ }
+ }
+
+ fclose (mnttab);
+ }
+#endif
+
+ if (! *poolname)
+ return;
+
+ slash = strchr (*poolname, '/');
+ if (slash)
+ {
+ *slash = '\0';
+ *poolfs = xstrdup (slash + 1);
+ }
+ else
+ *poolfs = xstrdup ("");
+}
+#endif
+
+/* This function never prints trailing slashes (so that its output
+ can be appended a slash unconditionally). */
+char *
+grub_make_system_path_relative_to_its_root (const char *path)
+{
+ struct stat st;
+ char *p, *buf, *buf2, *buf3, *ret;
+ uintptr_t offset = 0;
+ dev_t num;
+ size_t len;
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ char *poolfs = NULL;
+#endif
+
+ /* canonicalize. */
+ p = canonicalize_file_name (path);
+ if (p == NULL)
+ grub_util_error ("failed to get canonical path of %s", path);
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ /* For ZFS sub-pool filesystems, could be extended to others (btrfs?). */
+ {
+ char *dummy;
+ grub_find_zpool_from_dir (p, &dummy, &poolfs);
+ }
+#endif
+
+ len = strlen (p) + 1;
+ buf = xstrdup (p);
+ free (p);
+
+ if (stat (buf, &st) < 0)
+ grub_util_error ("cannot stat %s: %s", buf, strerror (errno));
+
+ buf2 = xstrdup (buf);
+ num = st.st_dev;
+
+ /* This loop sets offset to the number of chars of the root
+ directory we're inspecting. */
+ while (1)
+ {
+ p = strrchr (buf, '/');
+ if (p == NULL)
+ /* This should never happen. */
+ grub_util_error ("FIXME: no / in buf. (make_system_path_relative_to_its_root)");
+ if (p != buf)
+ *p = 0;
+ else
+ *++p = 0;
+
+ if (stat (buf, &st) < 0)
+ grub_util_error ("cannot stat %s: %s", buf, strerror (errno));
+
+ /* buf is another filesystem; we found it. */
+ if (st.st_dev != num)
+ {
+ /* offset == 0 means path given is the mount point.
+ This works around special-casing of "/" in Un*x. This function never
+ prints trailing slashes (so that its output can be appended a slash
+ unconditionally). Each slash in is considered a preceding slash, and
+ therefore the root directory is an empty string. */
+ if (offset == 0)
+ {
+ free (buf);
+#ifdef __linux__
+ {
+ char *bind;
+ grub_free (grub_find_root_device_from_mountinfo (buf2, &bind));
+ if (bind && bind[0] && bind[1])
+ {
+ buf3 = bind;
+ goto parsedir;
+ }
+ grub_free (bind);
+ }
+#endif
+ free (buf2);
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ if (poolfs)
+ return xasprintf ("/%s/@", poolfs);
+#endif
+ return xstrdup ("");
+ }
+ else
+ break;
+ }
+
+ offset = p - buf;
+ /* offset == 1 means root directory. */
+ if (offset == 1)
+ {
+ /* Include leading slash. */
+ offset = 0;
+ break;
+ }
+ }
+ free (buf);
+ buf3 = xstrdup (buf2 + offset);
+ buf2[offset] = 0;
+#ifdef __linux__
+ {
+ char *bind;
+ grub_free (grub_find_root_device_from_mountinfo (buf2, &bind));
+ if (bind && bind[0] && bind[1])
+ {
+ char *temp = buf3;
+ buf3 = grub_xasprintf ("%s%s%s", bind, buf3[0] == '/' ?"":"/", buf3);
+ grub_free (temp);
+ }
+ grub_free (bind);
+ }
+#endif
+
+ free (buf2);
+
+#ifdef __CYGWIN__
+ if (st.st_dev != (DEV_CYGDRIVE_MAJOR << 16))
+ {
+ /* Reached some mount point not below /cygdrive.
+ GRUB does not know Cygwin's emulated mounts,
+ convert to Win32 path. */
+ grub_util_info ("Cygwin path = %s\n", buf3);
+ char * temp = get_win32_path (buf3);
+ free (buf3);
+ buf3 = temp;
+ }
+#endif
+
+ parsedir:
+ /* Remove trailing slashes, return empty string if root directory. */
+ len = strlen (buf3);
+ while (len > 0 && buf3[len - 1] == '/')
+ {
+ buf3[len - 1] = '\0';
+ len--;
+ }
+
+#if defined(HAVE_LIBZFS) && defined(HAVE_LIBNVPAIR)
+ if (poolfs)
+ {
+ ret = xasprintf ("/%s/@%s", poolfs, buf3);
+ free (buf3);
+ }
+ else
+#endif
+ ret = buf3;
+
+ return ret;
+}
+
+#ifdef HAVE_DEVICE_MAPPER
+static void device_mapper_null_log (int level __attribute__ ((unused)),
+ const char *file __attribute__ ((unused)),
+ int line __attribute__ ((unused)),
+ int dm_errno __attribute__ ((unused)),
+ const char *f __attribute__ ((unused)),
+ ...)
+{
+}
+
+int
+grub_device_mapper_supported (void)
+{
+ static int supported = -1;
+
+ if (supported == -1)
+ {
+ struct dm_task *dmt;
+
+ /* Suppress annoying log messages. */
+ dm_log_with_errno_init (&device_mapper_null_log);
+
+ dmt = dm_task_create (DM_DEVICE_VERSION);
+ supported = (dmt != NULL);
+ if (dmt)
+ dm_task_destroy (dmt);
+
+ /* Restore the original logger. */
+ dm_log_with_errno_init (NULL);
+ }
+
+ return supported;
+}
+#endif /* HAVE_DEVICE_MAPPER */
diff --git a/grub-core/kern/emu/mm.c b/grub-core/kern/emu/mm.c
new file mode 100644
index 0000000..6a70efb
--- /dev/null
+++ b/grub-core/kern/emu/mm.c
@@ -0,0 +1,87 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005,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 <config-util.h>
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <stdlib.h>
+#include <string.h>
+
+void *
+grub_malloc (grub_size_t size)
+{
+ void *ret;
+ ret = malloc (size);
+ if (!ret)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+ return ret;
+}
+
+void *
+grub_zalloc (grub_size_t size)
+{
+ void *ret;
+
+ ret = grub_malloc (size);
+ if (!ret)
+ return NULL;
+ memset (ret, 0, size);
+ return ret;
+}
+
+void
+grub_free (void *ptr)
+{
+ free (ptr);
+}
+
+void *
+grub_realloc (void *ptr, grub_size_t size)
+{
+ void *ret;
+ ret = realloc (ptr, size);
+ if (!ret)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+ return ret;
+}
+
+void *
+grub_memalign (grub_size_t align, grub_size_t size)
+{
+ void *p;
+
+#if defined(HAVE_POSIX_MEMALIGN)
+ if (align < sizeof (void *))
+ align = sizeof (void *);
+ if (posix_memalign (&p, align, size) != 0)
+ p = 0;
+#elif defined(HAVE_MEMALIGN)
+ p = memalign (align, size);
+#else
+ (void) align;
+ (void) size;
+ grub_util_error ("grub_memalign is not supported");
+#endif
+
+ if (!p)
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+
+ return p;
+}
diff --git a/grub-core/kern/emu/time.c b/grub-core/kern/emu/time.c
new file mode 100644
index 0000000..5da8092
--- /dev/null
+++ b/grub-core/kern/emu/time.c
@@ -0,0 +1,46 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/datetime.h>
+#include <time.h>
+
+grub_err_t
+grub_get_datetime (struct grub_datetime *datetime)
+{
+ struct tm *mytm;
+ time_t mytime;
+
+ mytime = time (&mytime);
+ mytm = gmtime (&mytime);
+
+ datetime->year = mytm->tm_year + 1900;
+ datetime->month = mytm->tm_mon + 1;
+ datetime->day = mytm->tm_mday;
+ datetime->hour = mytm->tm_hour;
+ datetime->minute = mytm->tm_min;
+ datetime->second = mytm->tm_sec;
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_set_datetime (struct grub_datetime *datetime __attribute__ ((unused)))
+{
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "no clock setting routine available");
+}
diff --git a/grub-core/kern/env.c b/grub-core/kern/env.c
new file mode 100644
index 0000000..8f843a8
--- /dev/null
+++ b/grub-core/kern/env.c
@@ -0,0 +1,262 @@
+/* env.c - Environment variables */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/env.h>
+#include <grub/env_private.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+/* The initial context. */
+static struct grub_env_context initial_context;
+
+/* The current context. */
+struct grub_env_context *grub_current_context = &initial_context;
+
+/* Return the hash representation of the string S. */
+static unsigned int
+grub_env_hashval (const char *s)
+{
+ unsigned int i = 0;
+
+ /* XXX: This can be done much more efficiently. */
+ while (*s)
+ i += 5 * *(s++);
+
+ return i % HASHSZ;
+}
+
+struct grub_env_var *
+grub_env_find (const char *name)
+{
+ struct grub_env_var *var;
+ int idx = grub_env_hashval (name);
+
+ /* Look for the variable in the current context. */
+ for (var = grub_current_context->vars[idx]; var; var = var->next)
+ if (grub_strcmp (var->name, name) == 0)
+ return var;
+
+ return 0;
+}
+
+static void
+grub_env_insert (struct grub_env_context *context,
+ struct grub_env_var *var)
+{
+ int idx = grub_env_hashval (var->name);
+
+ /* Insert the variable into the hashtable. */
+ var->prevp = &context->vars[idx];
+ var->next = context->vars[idx];
+ if (var->next)
+ var->next->prevp = &(var->next);
+ context->vars[idx] = var;
+}
+
+static void
+grub_env_remove (struct grub_env_var *var)
+{
+ /* Remove the entry from the variable table. */
+ *var->prevp = var->next;
+ if (var->next)
+ var->next->prevp = var->prevp;
+}
+
+grub_err_t
+grub_env_set (const char *name, const char *val)
+{
+ struct grub_env_var *var;
+
+ /* If the variable does already exist, just update the variable. */
+ var = grub_env_find (name);
+ if (var)
+ {
+ char *old = var->value;
+
+ if (var->write_hook)
+ var->value = var->write_hook (var, val);
+ else
+ var->value = grub_strdup (val);
+
+ if (! var->value)
+ {
+ var->value = old;
+ return grub_errno;
+ }
+
+ grub_free (old);
+ return GRUB_ERR_NONE;
+ }
+
+ /* The variable does not exist, so create a new one. */
+ var = grub_zalloc (sizeof (*var));
+ if (! var)
+ return grub_errno;
+
+ /* This is not necessary. But leave this for readability. */
+ var->global = 0;
+
+ var->name = grub_strdup (name);
+ if (! var->name)
+ goto fail;
+
+ var->value = grub_strdup (val);
+ if (! var->value)
+ goto fail;
+
+ grub_env_insert (grub_current_context, var);
+
+ return GRUB_ERR_NONE;
+
+ fail:
+ grub_free (var->name);
+ grub_free (var->value);
+ grub_free (var);
+
+ return grub_errno;
+}
+
+char *
+grub_env_get (const char *name)
+{
+ struct grub_env_var *var;
+
+ var = grub_env_find (name);
+ if (! var)
+ return 0;
+
+ if (var->read_hook)
+ return var->read_hook (var, var->value);
+
+ return var->value;
+}
+
+void
+grub_env_unset (const char *name)
+{
+ struct grub_env_var *var;
+
+ var = grub_env_find (name);
+ if (! var)
+ return;
+
+ if (var->read_hook || var->write_hook)
+ {
+ grub_env_set (name, "");
+ return;
+ }
+
+ grub_env_remove (var);
+
+ grub_free (var->name);
+ grub_free (var->value);
+ grub_free (var);
+}
+
+void
+grub_env_iterate (int (*func) (struct grub_env_var *var))
+{
+ struct grub_env_sorted_var *sorted_list = 0;
+ struct grub_env_sorted_var *sorted_var;
+ int i;
+
+ /* Add variables associated with this context into a sorted list. */
+ for (i = 0; i < HASHSZ; i++)
+ {
+ struct grub_env_var *var;
+
+ for (var = grub_current_context->vars[i]; var; var = var->next)
+ {
+ struct grub_env_sorted_var *p, **q;
+
+ sorted_var = grub_malloc (sizeof (*sorted_var));
+ if (! sorted_var)
+ goto fail;
+
+ sorted_var->var = var;
+
+ for (q = &sorted_list, p = *q; p; q = &((*q)->next), p = *q)
+ {
+ if (grub_strcmp (p->var->name, var->name) > 0)
+ break;
+ }
+
+ sorted_var->next = *q;
+ *q = sorted_var;
+ }
+ }
+
+ /* Iterate FUNC on the sorted list. */
+ for (sorted_var = sorted_list; sorted_var; sorted_var = sorted_var->next)
+ if (func (sorted_var->var))
+ break;
+
+ fail:
+
+ /* Free the sorted list. */
+ for (sorted_var = sorted_list; sorted_var; )
+ {
+ struct grub_env_sorted_var *tmp = sorted_var->next;
+
+ grub_free (sorted_var);
+ sorted_var = tmp;
+ }
+}
+
+grub_err_t
+grub_register_variable_hook (const char *name,
+ grub_env_read_hook_t read_hook,
+ grub_env_write_hook_t write_hook)
+{
+ struct grub_env_var *var = grub_env_find (name);
+
+ if (! var)
+ {
+ if (grub_env_set (name, "") != GRUB_ERR_NONE)
+ return grub_errno;
+
+ var = grub_env_find (name);
+ /* XXX Insert an assertion? */
+ }
+
+ var->read_hook = read_hook;
+ var->write_hook = write_hook;
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_env_export (const char *name)
+{
+ struct grub_env_var *var;
+
+ var = grub_env_find (name);
+ if (! var)
+ {
+ grub_err_t err;
+
+ err = grub_env_set (name, "");
+ if (err)
+ return err;
+ var = grub_env_find (name);
+ }
+ var->global = 1;
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c
new file mode 100644
index 0000000..74f3915
--- /dev/null
+++ b/grub-core/kern/err.c
@@ -0,0 +1,139 @@
+/* err.c - error handling routines */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2005,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/>.
+ */
+
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <stdarg.h>
+#include <grub/i18n.h>
+
+#define GRUB_MAX_ERRMSG 256
+#define GRUB_ERROR_STACK_SIZE 10
+
+grub_err_t grub_errno;
+char grub_errmsg[GRUB_MAX_ERRMSG];
+int grub_err_printed_errors;
+
+static struct
+{
+ grub_err_t errno;
+ char errmsg[GRUB_MAX_ERRMSG];
+} grub_error_stack_items[GRUB_ERROR_STACK_SIZE];
+
+static int grub_error_stack_pos;
+static int grub_error_stack_assert;
+
+grub_err_t
+grub_error (grub_err_t n, const char *fmt, ...)
+{
+ va_list ap;
+
+ grub_errno = n;
+
+ va_start (ap, fmt);
+ grub_vsnprintf (grub_errmsg, sizeof (grub_errmsg), _(fmt), ap);
+ va_end (ap);
+
+ return n;
+}
+
+void
+grub_fatal (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ grub_vprintf (_(fmt), ap);
+ va_end (ap);
+
+ grub_abort ();
+}
+
+void
+grub_error_push (void)
+{
+ /* Only add items to stack, if there is enough room. */
+ if (grub_error_stack_pos < GRUB_ERROR_STACK_SIZE)
+ {
+ /* Copy active error message to stack. */
+ grub_error_stack_items[grub_error_stack_pos].errno = grub_errno;
+ grub_memcpy (grub_error_stack_items[grub_error_stack_pos].errmsg,
+ grub_errmsg,
+ sizeof (grub_errmsg));
+
+ /* Advance to next error stack position. */
+ grub_error_stack_pos++;
+ }
+ else
+ {
+ /* There is no room for new error message. Discard new error message
+ and mark error stack assertion flag. */
+ grub_error_stack_assert = 1;
+ }
+
+ /* Allow further operation of other components by resetting
+ active errno to GRUB_ERR_NONE. */
+ grub_errno = GRUB_ERR_NONE;
+}
+
+int
+grub_error_pop (void)
+{
+ if (grub_error_stack_pos > 0)
+ {
+ /* Pop error message from error stack to current active error. */
+ grub_error_stack_pos--;
+
+ grub_errno = grub_error_stack_items[grub_error_stack_pos].errno;
+ grub_memcpy (grub_errmsg,
+ grub_error_stack_items[grub_error_stack_pos].errmsg,
+ sizeof (grub_errmsg));
+
+ return 1;
+ }
+ else
+ {
+ /* There is no more items on error stack, reset to no error state. */
+ grub_errno = GRUB_ERR_NONE;
+
+ return 0;
+ }
+}
+
+void
+grub_print_error (void)
+{
+ /* Print error messages in reverse order. First print active error message
+ and then empty error stack. */
+ do
+ {
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_err_printf (_("error: %s.\n"), grub_errmsg);
+ grub_err_printed_errors++;
+ }
+ }
+ while (grub_error_pop ());
+
+ /* If there was an assert while using error stack, report about it. */
+ if (grub_error_stack_assert)
+ {
+ grub_err_printf ("assert: error stack overflow detected!\n");
+ grub_error_stack_assert = 0;
+ }
+}
diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c
new file mode 100644
index 0000000..9d5a51c
--- /dev/null
+++ b/grub-core/kern/file.c
@@ -0,0 +1,186 @@
+/* file.c - file I/O functions */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2006,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/misc.h>
+#include <grub/err.h>
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/fs.h>
+#include <grub/device.h>
+
+grub_file_filter_t grub_file_filters_all[GRUB_FILE_FILTER_MAX];
+grub_file_filter_t grub_file_filters_enabled[GRUB_FILE_FILTER_MAX];
+
+/* Get the device part of the filename NAME. It is enclosed by parentheses. */
+char *
+grub_file_get_device_name (const char *name)
+{
+ if (name[0] == '(')
+ {
+ char *p = grub_strchr (name, ')');
+ char *ret;
+
+ if (! p)
+ {
+ grub_error (GRUB_ERR_BAD_FILENAME, "missing `)'");
+ return 0;
+ }
+
+ ret = (char *) grub_malloc (p - name);
+ if (! ret)
+ return 0;
+
+ grub_memcpy (ret, name + 1, p - name - 1);
+ ret[p - name - 1] = '\0';
+ return ret;
+ }
+
+ return 0;
+}
+
+grub_file_t
+grub_file_open (const char *name)
+{
+ grub_device_t device = 0;
+ grub_file_t file = 0, last_file = 0;
+ char *device_name;
+ char *file_name;
+ grub_file_filter_id_t filter;
+
+ device_name = grub_file_get_device_name (name);
+ if (grub_errno)
+ goto fail;
+
+ /* Get the file part of NAME. */
+ file_name = (name[0] == '(') ? grub_strchr (name, ')') : NULL;
+ if (file_name)
+ file_name++;
+ else
+ file_name = (char *) name;
+
+ device = grub_device_open (device_name);
+ grub_free (device_name);
+ if (! device)
+ goto fail;
+
+ file = (grub_file_t) grub_zalloc (sizeof (*file));
+ if (! file)
+ goto fail;
+
+ file->device = device;
+
+ if (device->disk && file_name[0] != '/')
+ /* This is a block list. */
+ file->fs = &grub_fs_blocklist;
+ else
+ {
+ file->fs = grub_fs_probe (device);
+ if (! file->fs)
+ goto fail;
+ }
+
+ if ((file->fs->open) (file, file_name) != GRUB_ERR_NONE)
+ goto fail;
+
+ for (filter = 0; file && filter < ARRAY_SIZE (grub_file_filters_enabled);
+ filter++)
+ if (grub_file_filters_enabled[filter])
+ {
+ last_file = file;
+ file = grub_file_filters_enabled[filter] (file);
+ }
+ if (!file)
+ grub_file_close (last_file);
+
+ grub_memcpy (grub_file_filters_enabled, grub_file_filters_all,
+ sizeof (grub_file_filters_enabled));
+
+ return file;
+
+ fail:
+ if (device)
+ grub_device_close (device);
+
+ /* if (net) grub_net_close (net); */
+
+ grub_free (file);
+
+ grub_memcpy (grub_file_filters_enabled, grub_file_filters_all,
+ sizeof (grub_file_filters_enabled));
+
+ return 0;
+}
+
+grub_ssize_t
+grub_file_read (grub_file_t file, void *buf, grub_size_t len)
+{
+ grub_ssize_t res;
+
+ if (file->offset > file->size)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "attempt to read past the end of file");
+ return -1;
+ }
+
+ if (len == 0 || len > file->size - file->offset)
+ len = file->size - file->offset;
+
+ /* Prevent an overflow. */
+ if ((grub_ssize_t) len < 0)
+ len >>= 1;
+
+ if (len == 0)
+ return 0;
+
+ res = (file->fs->read) (file, buf, len);
+ if (res > 0)
+ file->offset += res;
+
+ return res;
+}
+
+grub_err_t
+grub_file_close (grub_file_t file)
+{
+ if (file->fs->close)
+ (file->fs->close) (file);
+
+ if (file->device)
+ grub_device_close (file->device);
+ grub_free (file);
+ return grub_errno;
+}
+
+grub_off_t
+grub_file_seek (grub_file_t file, grub_off_t offset)
+{
+ grub_off_t old;
+
+ if (offset > file->size)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "attempt to seek outside of the file");
+ return -1;
+ }
+
+ old = file->offset;
+ file->offset = offset;
+ return old;
+}
diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c
new file mode 100644
index 0000000..cf800f4
--- /dev/null
+++ b/grub-core/kern/fs.c
@@ -0,0 +1,231 @@
+/* fs.c - filesystem manager */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2005,2007 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/disk.h>
+#include <grub/net.h>
+#include <grub/fs.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/mm.h>
+#include <grub/term.h>
+
+grub_fs_t grub_fs_list = 0;
+
+grub_fs_autoload_hook_t grub_fs_autoload_hook = 0;
+
+grub_fs_t
+grub_fs_probe (grub_device_t device)
+{
+ grub_fs_t p;
+ auto int dummy_func (const char *filename,
+ const struct grub_dirhook_info *info);
+
+ int dummy_func (const char *filename __attribute__ ((unused)),
+ const struct grub_dirhook_info *info __attribute__ ((unused)))
+ {
+ return 1;
+ }
+
+ if (device->disk)
+ {
+ /* Make it sure not to have an infinite recursive calls. */
+ static int count = 0;
+
+ for (p = grub_fs_list; p; p = p->next)
+ {
+ grub_dprintf ("fs", "Detecting %s...\n", p->name);
+ (p->dir) (device, "/", dummy_func);
+ if (grub_errno == GRUB_ERR_NONE)
+ return p;
+
+ grub_error_push ();
+ grub_dprintf ("fs", "%s detection failed.\n", p->name);
+ grub_error_pop ();
+
+ if (grub_errno != GRUB_ERR_BAD_FS)
+ return 0;
+
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ /* Let's load modules automatically. */
+ if (grub_fs_autoload_hook && count == 0)
+ {
+ count++;
+
+ while (grub_fs_autoload_hook ())
+ {
+ p = grub_fs_list;
+
+ (p->dir) (device, "/", dummy_func);
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ count--;
+ return p;
+ }
+
+ if (grub_errno != GRUB_ERR_BAD_FS)
+ {
+ count--;
+ return 0;
+ }
+
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ count--;
+ }
+ }
+ else if (device->net->fs)
+ return device->net->fs;
+
+ grub_error (GRUB_ERR_UNKNOWN_FS, "unknown filesystem");
+ return 0;
+}
+
+
+
+/* Block list support routines. */
+
+struct grub_fs_block
+{
+ grub_disk_addr_t offset;
+ unsigned long length;
+};
+
+static grub_err_t
+grub_fs_blocklist_open (grub_file_t file, const char *name)
+{
+ char *p = (char *) name;
+ unsigned num = 0;
+ unsigned i;
+ grub_disk_t disk = file->device->disk;
+ struct grub_fs_block *blocks;
+
+ /* First, count the number of blocks. */
+ do
+ {
+ num++;
+ p = grub_strchr (p, ',');
+ if (p)
+ p++;
+ }
+ while (p);
+
+ /* Allocate a block list. */
+ blocks = grub_zalloc (sizeof (struct grub_fs_block) * (num + 1));
+ if (! blocks)
+ return 0;
+
+ file->size = 0;
+ p = (char *) name;
+ for (i = 0; i < num; i++)
+ {
+ if (*p != '+')
+ {
+ blocks[i].offset = grub_strtoull (p, &p, 0);
+ if (grub_errno != GRUB_ERR_NONE || *p != '+')
+ {
+ grub_error (GRUB_ERR_BAD_FILENAME,
+ "invalid file name `%s'", name);
+ goto fail;
+ }
+ }
+
+ p++;
+ blocks[i].length = grub_strtoul (p, &p, 0);
+ if (grub_errno != GRUB_ERR_NONE
+ || blocks[i].length == 0
+ || (*p && *p != ',' && ! grub_isspace (*p)))
+ {
+ grub_error (GRUB_ERR_BAD_FILENAME,
+ "invalid file name `%s'", name);
+ goto fail;
+ }
+
+ if (disk->total_sectors < blocks[i].offset + blocks[i].length)
+ {
+ grub_error (GRUB_ERR_BAD_FILENAME, "beyond the total sectors");
+ goto fail;
+ }
+
+ file->size += (blocks[i].length << GRUB_DISK_SECTOR_BITS);
+ p++;
+ }
+
+ file->data = blocks;
+
+ return GRUB_ERR_NONE;
+
+ fail:
+ grub_free (blocks);
+ return grub_errno;
+}
+
+static grub_ssize_t
+grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len)
+{
+ struct grub_fs_block *p;
+ grub_disk_addr_t sector;
+ grub_off_t offset;
+ grub_ssize_t ret = 0;
+
+ if (len > file->size - file->offset)
+ len = file->size - file->offset;
+
+ sector = (file->offset >> GRUB_DISK_SECTOR_BITS);
+ offset = (file->offset & (GRUB_DISK_SECTOR_SIZE - 1));
+ for (p = file->data; p->length && len > 0; p++)
+ {
+ if (sector < p->length)
+ {
+ grub_size_t size;
+
+ size = len;
+ if (((size + offset + GRUB_DISK_SECTOR_SIZE - 1)
+ >> GRUB_DISK_SECTOR_BITS) > p->length - sector)
+ size = ((p->length - sector) << GRUB_DISK_SECTOR_BITS) - offset;
+
+ if (grub_disk_read (file->device->disk, p->offset + sector, offset,
+ size, buf) != GRUB_ERR_NONE)
+ return -1;
+
+ ret += size;
+ len -= size;
+ sector -= ((size + offset) >> GRUB_DISK_SECTOR_BITS);
+ offset = ((size + offset) & (GRUB_DISK_SECTOR_SIZE - 1));
+ }
+ else
+ sector -= p->length;
+ }
+
+ return ret;
+}
+
+struct grub_fs grub_fs_blocklist =
+ {
+ .name = "blocklist",
+ .dir = 0,
+ .open = grub_fs_blocklist_open,
+ .read = grub_fs_blocklist_read,
+ .close = 0,
+ .next = 0
+ };
diff --git a/grub-core/kern/generic/millisleep.c b/grub-core/kern/generic/millisleep.c
new file mode 100644
index 0000000..9d5971f
--- /dev/null
+++ b/grub-core/kern/generic/millisleep.c
@@ -0,0 +1,39 @@
+/* millisleep.c - generic millisleep function.
+ * The generic implementation of these functions can be used for architectures
+ * or platforms that do not have a more specialized implementation. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,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/>.
+ */
+
+#include <grub/misc.h>
+#include <grub/time.h>
+
+void
+grub_millisleep (grub_uint32_t ms)
+{
+ grub_uint64_t start;
+
+ start = grub_get_time_ms ();
+
+ /* Instead of setting an end time and looping while the current time is
+ less than that, comparing the elapsed sleep time with the desired sleep
+ time handles the (unlikely!) case that the timer would wrap around
+ during the sleep. */
+
+ while (grub_get_time_ms () - start < ms)
+ grub_cpu_idle ();
+}
diff --git a/grub-core/kern/generic/rtc_get_time_ms.c b/grub-core/kern/generic/rtc_get_time_ms.c
new file mode 100644
index 0000000..3592336
--- /dev/null
+++ b/grub-core/kern/generic/rtc_get_time_ms.c
@@ -0,0 +1,37 @@
+/* rtc_get_time_ms.c - get_time_ms implementation using platform RTC.
+ * The generic implementation of these functions can be used for architectures
+ * or platforms that do not have a more specialized implementation. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/>.
+ */
+
+#include <grub/time.h>
+#include <grub/misc.h>
+
+/* Calculate the time in milliseconds since the epoch based on the RTC. */
+grub_uint64_t
+grub_rtc_get_time_ms (void)
+{
+ /* By dimensional analysis:
+
+ 1000 ms N rtc ticks 1 s
+ ------- * ----------- * ----------- = 1000*N/T ms
+ 1 s 1 T rtc ticks
+ */
+ grub_uint64_t ticks_ms_per_sec = ((grub_uint64_t) 1000) * grub_get_rtc ();
+ return grub_divmod64 (ticks_ms_per_sec, GRUB_TICKS_PER_SECOND, 0);
+}
diff --git a/grub-core/kern/i386/coreboot/init.c b/grub-core/kern/i386/coreboot/init.c
new file mode 100644
index 0000000..434b9b5
--- /dev/null
+++ b/grub-core/kern/i386/coreboot/init.c
@@ -0,0 +1,132 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/kernel.h>
+#include <grub/mm.h>
+#include <grub/machine/time.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/console.h>
+#include <grub/offsets.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/loader.h>
+#include <grub/env.h>
+#include <grub/cache.h>
+#include <grub/time.h>
+#include <grub/symbol.h>
+#include <grub/cpu/io.h>
+#include <grub/cpu/floppy.h>
+#include <grub/cpu/tsc.h>
+#ifdef GRUB_MACHINE_QEMU
+#include <grub/machine/kernel.h>
+#endif
+
+extern char _start[];
+extern char _end[];
+
+grub_uint32_t
+grub_get_rtc (void)
+{
+ grub_fatal ("grub_get_rtc() is not implemented.\n");
+}
+
+void
+grub_exit (void)
+{
+ /* We can't use grub_fatal() in this function. This would create an infinite
+ loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */
+ while (1)
+ grub_cpu_idle ();
+}
+
+void
+grub_machine_init (void)
+{
+#ifdef GRUB_MACHINE_QEMU
+ grub_qemu_init_cirrus ();
+#endif
+ /* Initialize the console as early as possible. */
+ grub_vga_text_init ();
+
+ auto int NESTED_FUNC_ATTR heap_init (grub_uint64_t, grub_uint64_t,
+ grub_memory_type_t);
+ int NESTED_FUNC_ATTR heap_init (grub_uint64_t addr, grub_uint64_t size,
+ grub_memory_type_t type)
+ {
+#if GRUB_CPU_SIZEOF_VOID_P == 4
+ /* Restrict ourselves to 32-bit memory space. */
+ if (addr > GRUB_ULONG_MAX)
+ return 0;
+ if (addr + size > GRUB_ULONG_MAX)
+ size = GRUB_ULONG_MAX - addr;
+#endif
+
+ if (type != GRUB_MEMORY_AVAILABLE)
+ return 0;
+
+ /* Avoid the lower memory. */
+ if (addr < GRUB_MEMORY_MACHINE_LOWER_SIZE)
+ {
+ if (addr + size <= GRUB_MEMORY_MACHINE_LOWER_SIZE)
+ return 0;
+ else
+ {
+ size -= GRUB_MEMORY_MACHINE_LOWER_SIZE - addr;
+ addr = GRUB_MEMORY_MACHINE_LOWER_SIZE;
+ }
+ }
+
+ grub_mm_init_region ((void *) (grub_addr_t) addr, (grub_size_t) size);
+
+ return 0;
+ }
+
+#if defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_QEMU)
+ grub_machine_mmap_init ();
+#endif
+ grub_machine_mmap_iterate (heap_init);
+
+ grub_tsc_init ();
+}
+
+void
+grub_machine_set_prefix (void)
+{
+ /* Initialize the prefix. */
+ grub_env_set ("prefix", grub_prefix);
+}
+
+void
+grub_machine_fini (void)
+{
+ grub_vga_text_fini ();
+ grub_stop_floppy ();
+}
+
+/* Return the end of the core image. */
+grub_addr_t
+grub_arch_modules_addr (void)
+{
+#ifdef GRUB_MACHINE_QEMU
+ return grub_core_entry_addr + grub_kernel_image_size;
+#else
+ return ALIGN_UP((grub_addr_t) _end, GRUB_KERNEL_MACHINE_MOD_ALIGN);
+#endif
+}
diff --git a/grub-core/kern/i386/coreboot/mmap.c b/grub-core/kern/i386/coreboot/mmap.c
new file mode 100644
index 0000000..8b0b202
--- /dev/null
+++ b/grub-core/kern/i386/coreboot/mmap.c
@@ -0,0 +1,108 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/>.
+ */
+
+#include <grub/machine/memory.h>
+#include <grub/machine/lbio.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+
+static grub_err_t
+grub_linuxbios_table_iterate (int (*hook) (grub_linuxbios_table_item_t))
+{
+ grub_linuxbios_table_header_t table_header;
+ grub_linuxbios_table_item_t table_item;
+
+ auto int check_signature (grub_linuxbios_table_header_t);
+ int check_signature (grub_linuxbios_table_header_t tbl_header)
+ {
+ if (! grub_memcmp (tbl_header->signature, "LBIO", 4))
+ return 1;
+
+ return 0;
+ }
+
+ /* Assuming table_header is aligned to its size (8 bytes). */
+
+ for (table_header = (grub_linuxbios_table_header_t) 0x500;
+ table_header < (grub_linuxbios_table_header_t) 0x1000; table_header++)
+ if (check_signature (table_header))
+ goto signature_found;
+
+ for (table_header = (grub_linuxbios_table_header_t) 0xf0000;
+ table_header < (grub_linuxbios_table_header_t) 0x100000; table_header++)
+ if (check_signature (table_header))
+ goto signature_found;
+
+ grub_fatal ("Could not find coreboot table\n");
+
+signature_found:
+
+ table_item =
+ (grub_linuxbios_table_item_t) ((long) table_header +
+ (long) table_header->size);
+ for (; table_item->size;
+ table_item = (grub_linuxbios_table_item_t) ((long) table_item + (long) table_item->size))
+ {
+ if (table_item->tag == GRUB_LINUXBIOS_MEMBER_LINK
+ && check_signature ((grub_linuxbios_table_header_t) (grub_addr_t)
+ *(grub_uint64_t *) (table_item + 1)))
+ {
+ table_header = (grub_linuxbios_table_header_t) (grub_addr_t)
+ *(grub_uint64_t *) (table_item + 1);
+ goto signature_found;
+ }
+ if (hook (table_item))
+ return 1;
+ }
+
+ return 0;
+}
+
+grub_err_t
+grub_machine_mmap_iterate (grub_memory_hook_t hook)
+{
+ mem_region_t mem_region;
+
+ auto int iterate_linuxbios_table (grub_linuxbios_table_item_t);
+ int iterate_linuxbios_table (grub_linuxbios_table_item_t table_item)
+ {
+ if (table_item->tag != GRUB_LINUXBIOS_MEMBER_MEMORY)
+ return 0;
+
+ mem_region =
+ (mem_region_t) ((long) table_item +
+ sizeof (struct grub_linuxbios_table_item));
+ while ((long) mem_region < (long) table_item + (long) table_item->size)
+ {
+ if (hook (mem_region->addr, mem_region->size,
+ /* Multiboot mmaps match with the coreboot mmap definition.
+ Therefore, we can just pass type through. */
+ mem_region->type))
+ return 1;
+
+ mem_region++;
+ }
+
+ return 0;
+ }
+
+ grub_linuxbios_table_iterate (iterate_linuxbios_table);
+
+ return 0;
+}
diff --git a/grub-core/kern/i386/coreboot/startup.S b/grub-core/kern/i386/coreboot/startup.S
new file mode 100644
index 0000000..cac023d
--- /dev/null
+++ b/grub-core/kern/i386/coreboot/startup.S
@@ -0,0 +1,89 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2005,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/>.
+ */
+
+#include <grub/symbol.h>
+/* For stack parameters. */
+#include <grub/i386/pc/memory.h>
+#include <grub/machine/memory.h>
+#include <grub/cpu/linux.h>
+#include <grub/offsets.h>
+#include <multiboot.h>
+#include <multiboot2.h>
+
+/*
+ * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
+ * So the first three arguments are passed in %eax, %edx, and %ecx,
+ * respectively, and if a function has a fixed number of arguments
+ * and the number if greater than three, the function must return
+ * with "ret $N" where N is ((the number of arguments) - 3) * 4.
+ */
+
+ .file "startup.S"
+ .text
+ .globl start, _start
+start:
+_start:
+ jmp codestart
+
+ /*
+ * This is a special data area at a fixed offset from the beginning.
+ */
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX
+
+VARIABLE(grub_prefix)
+ /* to be filled by grub-mkimage */
+
+ /*
+ * Leave some breathing room for the prefix.
+ */
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX_END
+
+/*
+ * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB itself).
+ */
+ .p2align 2 /* force 4-byte alignment */
+multiboot_header:
+ /* magic */
+ .long 0x1BADB002
+ /* flags */
+ .long MULTIBOOT_MEMORY_INFO
+ /* checksum */
+ .long -0x1BADB002 - MULTIBOOT_MEMORY_INFO
+
+codestart:
+#ifdef GRUB_MACHINE_MULTIBOOT
+ cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax
+ jne 0f
+ movl %ebx, EXT_C(startup_multiboot_info)
+0:
+#endif
+
+ /* initialize the stack */
+ movl $GRUB_MEMORY_MACHINE_PROT_STACK, %esp
+
+ /* jump to the main body of C code */
+ jmp EXT_C(grub_main)
+
+/*
+ * prot_to_real and associated structures (but NOT real_to_prot, that is
+ * only needed for BIOS gates).
+ */
+#include "../realmode.S"
+
diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c
new file mode 100644
index 0000000..5785496
--- /dev/null
+++ b/grub-core/kern/i386/dl.c
@@ -0,0 +1,109 @@
+/* dl-386.c - arch-dependent part of loadable module support */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,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/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+
+/* Check if EHDR is a valid ELF header. */
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+ Elf_Ehdr *e = ehdr;
+
+ /* Check the magic numbers. */
+ if (e->e_ident[EI_CLASS] != ELFCLASS32
+ || e->e_ident[EI_DATA] != ELFDATA2LSB
+ || e->e_machine != EM_386)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid arch specific ELF magic");
+
+ return GRUB_ERR_NONE;
+}
+
+/* Relocate symbols. */
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+{
+ Elf_Ehdr *e = ehdr;
+ Elf_Shdr *s;
+ Elf_Word entsize;
+ unsigned i;
+
+ /* Find a symbol table. */
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+
+ if (i == e->e_shnum)
+ return grub_error (GRUB_ERR_BAD_MODULE, "no symtab found");
+
+ entsize = s->sh_entsize;
+
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_REL)
+ {
+ grub_dl_segment_t seg;
+
+ /* Find the target segment. */
+ for (seg = mod->segment; seg; seg = seg->next)
+ if (seg->section == s->sh_info)
+ break;
+
+ if (seg)
+ {
+ Elf_Rel *rel, *max;
+
+ for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
+ max = rel + s->sh_size / s->sh_entsize;
+ rel < max;
+ rel++)
+ {
+ Elf_Word *addr;
+ Elf_Sym *sym;
+
+ if (seg->size < rel->r_offset)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "reloc offset is out of the segment");
+
+ addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+ sym = (Elf_Sym *) ((char *) mod->symtab
+ + entsize * ELF_R_SYM (rel->r_info));
+
+ switch (ELF_R_TYPE (rel->r_info))
+ {
+ case R_386_32:
+ *addr += sym->st_value;
+ break;
+
+ case R_386_PC32:
+ *addr += (sym->st_value - (Elf_Word) seg->addr
+ - rel->r_offset);
+ break;
+ }
+ }
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/kern/i386/efi/init.c b/grub-core/kern/i386/efi/init.c
new file mode 100644
index 0000000..f73f828
--- /dev/null
+++ b/grub-core/kern/i386/efi/init.c
@@ -0,0 +1,47 @@
+/* init.c - initialize an x86-based EFI system */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,2007 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/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/cache.h>
+#include <grub/kernel.h>
+#include <grub/efi/efi.h>
+#include <grub/i386/tsc.h>
+
+void
+grub_machine_init (void)
+{
+ grub_efi_init ();
+ grub_tsc_init ();
+}
+
+void
+grub_machine_fini (void)
+{
+ grub_efi_fini ();
+}
+
+void
+grub_machine_set_prefix (void)
+{
+ grub_efi_set_prefix ();
+}
diff --git a/grub-core/kern/i386/efi/startup.S b/grub-core/kern/i386/efi/startup.S
new file mode 100644
index 0000000..5b464ab
--- /dev/null
+++ b/grub-core/kern/i386/efi/startup.S
@@ -0,0 +1,66 @@
+/* startup.S - bootstrap GRUB itself */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,2007,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 <config.h>
+#include <grub/symbol.h>
+#include <grub/boot.h>
+
+ .file "startup.S"
+ .text
+ .globl start, _start
+start:
+_start:
+ jmp codestart
+
+ /*
+ * Compatibility version number
+ *
+ * These MUST be at byte offset 6 and 7 of the executable
+ * DO NOT MOVE !!!
+ */
+ . = _start + 0x6
+ .byte GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
+
+ /*
+ * This is a special data area 8 bytes from the beginning.
+ */
+
+ . = _start + 0x8
+
+VARIABLE(grub_prefix)
+ /* to be filled by grub-mkimage */
+
+ /*
+ * Leave some breathing room for the prefix.
+ */
+
+ . = _start + 0x50
+
+codestart:
+ /*
+ * EFI_SYSTEM_TABLE * and EFI_HANDLE are passed on the stack.
+ */
+ movl 4(%esp), %eax
+ movl %eax, EXT_C(grub_efi_image_handle)
+ movl 8(%esp), %eax
+ movl %eax, EXT_C(grub_efi_system_table)
+ call EXT_C(grub_main)
+ ret
+
+#include "../realmode.S"
diff --git a/grub-core/kern/i386/ieee1275/startup.S b/grub-core/kern/i386/ieee1275/startup.S
new file mode 100644
index 0000000..8208732
--- /dev/null
+++ b/grub-core/kern/i386/ieee1275/startup.S
@@ -0,0 +1,65 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2005,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/>.
+ */
+
+#include <grub/symbol.h>
+#include <grub/offsets.h>
+#include <grub/cpu/linux.h>
+#include <multiboot.h>
+#include <multiboot2.h>
+
+/*
+ * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
+ * So the first three arguments are passed in %eax, %edx, and %ecx,
+ * respectively, and if a function has a fixed number of arguments
+ * and the number if greater than three, the function must return
+ * with "ret $N" where N is ((the number of arguments) - 3) * 4.
+ */
+
+ .file "startup.S"
+ .text
+ .globl start, _start
+
+start:
+_start:
+ jmp codestart
+
+ /*
+ * This is a special data area at a fixed offset from the beginning.
+ */
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX
+
+VARIABLE(grub_prefix)
+ /* to be filled by grub-mkimage */
+
+ /*
+ * Leave some breathing room for the prefix.
+ */
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX_END
+
+codestart:
+ movl %eax, EXT_C(grub_ieee1275_entry_fn)
+ jmp EXT_C(grub_main)
+
+/*
+ * prot_to_real and associated structures (but NOT real_to_prot, that is
+ * only needed for BIOS gates).
+ */
+#include "../realmode.S"
+
diff --git a/grub-core/kern/i386/multiboot_mmap.c b/grub-core/kern/i386/multiboot_mmap.c
new file mode 100644
index 0000000..7f4bc3b
--- /dev/null
+++ b/grub-core/kern/i386/multiboot_mmap.c
@@ -0,0 +1,73 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/machine/memory.h>
+#include <grub/types.h>
+#include <grub/multiboot.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+
+/* A pointer to the MBI in its initial location. */
+struct multiboot_info *startup_multiboot_info;
+
+/* The MBI has to be copied to our BSS so that it won't be
+ overwritten. This is its final location. */
+static struct multiboot_info kern_multiboot_info;
+
+/* Unfortunately we can't use heap at this point. But 32 looks like a sane
+ limit (used by memtest86). */
+static grub_uint8_t mmap_entries[sizeof (struct multiboot_mmap_entry) * 32];
+
+void
+grub_machine_mmap_init ()
+{
+ if (! startup_multiboot_info)
+ grub_fatal ("Unable to find Multiboot Information (is CONFIG_MULTIBOOT disabled in coreboot?)");
+
+ /* Move MBI to a safe place. */
+ grub_memmove (&kern_multiboot_info, startup_multiboot_info, sizeof (struct multiboot_info));
+
+ if ((kern_multiboot_info.flags & MULTIBOOT_INFO_MEM_MAP) == 0)
+ grub_fatal ("Missing Multiboot memory information");
+
+ /* Move the memory map to a safe place. */
+ if (kern_multiboot_info.mmap_length > sizeof (mmap_entries))
+ {
+ grub_printf ("WARNING: Memory map size exceeds limit (0x%x > 0x%x); it will be truncated\n",
+ kern_multiboot_info.mmap_length, sizeof (mmap_entries));
+ kern_multiboot_info.mmap_length = sizeof (mmap_entries);
+ }
+ grub_memmove (mmap_entries, (void *) kern_multiboot_info.mmap_addr, kern_multiboot_info.mmap_length);
+ kern_multiboot_info.mmap_addr = (grub_uint32_t) mmap_entries;
+}
+
+grub_err_t
+grub_machine_mmap_iterate (grub_memory_hook_t hook)
+{
+ struct multiboot_mmap_entry *entry = (void *) kern_multiboot_info.mmap_addr;
+
+ while ((unsigned long) entry < kern_multiboot_info.mmap_addr + kern_multiboot_info.mmap_length)
+ {
+ if (hook (entry->addr, entry->len, entry->type))
+ break;
+
+ entry = (void *) ((grub_addr_t) entry + entry->size + sizeof (entry->size));
+ }
+
+ return 0;
+}
diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c
new file mode 100644
index 0000000..d8c337b
--- /dev/null
+++ b/grub-core/kern/i386/pc/init.c
@@ -0,0 +1,234 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2004,2005,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/kernel.h>
+#include <grub/mm.h>
+#include <grub/machine/boot.h>
+#include <grub/i386/floppy.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/console.h>
+#include <grub/machine/kernel.h>
+#include <grub/machine/int.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/loader.h>
+#include <grub/env.h>
+#include <grub/cache.h>
+#include <grub/time.h>
+#include <grub/cpu/tsc.h>
+
+struct mem_region
+{
+ grub_addr_t addr;
+ grub_size_t size;
+};
+
+#define MAX_REGIONS 32
+
+static struct mem_region mem_regions[MAX_REGIONS];
+static int num_regions;
+
+static char *
+make_install_device (void)
+{
+ /* XXX: This should be enough. */
+ char dev[100], *ptr = dev;
+
+ if (grub_prefix[0] != '(')
+ {
+ /* No hardcoded root partition - make it from the boot drive and the
+ partition number encoded at the install time. */
+ if (grub_boot_drive == GRUB_BOOT_MACHINE_PXE_DL)
+ {
+ grub_strcpy (dev, "(pxe");
+ ptr += sizeof ("(pxe") - 1;
+ }
+ else
+ {
+ grub_snprintf (dev, sizeof (dev),
+ "(%cd%u", (grub_boot_drive & 0x80) ? 'h' : 'f',
+ grub_boot_drive & 0x7f);
+ ptr += grub_strlen (ptr);
+
+ if (grub_install_dos_part >= 0)
+ grub_snprintf (ptr, sizeof (dev) - (ptr - dev),
+ ",%u", grub_install_dos_part + 1);
+ ptr += grub_strlen (ptr);
+
+ if (grub_install_bsd_part >= 0)
+ grub_snprintf (ptr, sizeof (dev) - (ptr - dev), ",%u",
+ grub_install_bsd_part + 1);
+ ptr += grub_strlen (ptr);
+ }
+
+ grub_snprintf (ptr, sizeof (dev) - (ptr - dev), ")%s", grub_prefix);
+ grub_strcpy (grub_prefix, dev);
+ }
+ else if (grub_prefix[1] == ',' || grub_prefix[1] == ')')
+ {
+ /* We have a prefix, but still need to fill in the boot drive. */
+ grub_snprintf (dev, sizeof (dev),
+ "(%cd%u%s", (grub_boot_drive & 0x80) ? 'h' : 'f',
+ grub_boot_drive & 0x7f, grub_prefix + 1);
+ grub_strcpy (grub_prefix, dev);
+ }
+
+ return grub_prefix;
+}
+
+/* Add a memory region. */
+static void
+add_mem_region (grub_addr_t addr, grub_size_t size)
+{
+ if (num_regions == MAX_REGIONS)
+ /* Ignore. */
+ return;
+
+ mem_regions[num_regions].addr = addr;
+ mem_regions[num_regions].size = size;
+ num_regions++;
+}
+
+/* Compact memory regions. */
+static void
+compact_mem_regions (void)
+{
+ int i, j;
+
+ /* Sort them. */
+ for (i = 0; i < num_regions - 1; i++)
+ for (j = i + 1; j < num_regions; j++)
+ if (mem_regions[i].addr > mem_regions[j].addr)
+ {
+ struct mem_region tmp = mem_regions[i];
+ mem_regions[i] = mem_regions[j];
+ mem_regions[j] = tmp;
+ }
+
+ /* Merge overlaps. */
+ for (i = 0; i < num_regions - 1; i++)
+ if (mem_regions[i].addr + mem_regions[i].size >= mem_regions[i + 1].addr)
+ {
+ j = i + 1;
+
+ if (mem_regions[i].addr + mem_regions[i].size
+ < mem_regions[j].addr + mem_regions[j].size)
+ mem_regions[i].size = (mem_regions[j].addr + mem_regions[j].size
+ - mem_regions[i].addr);
+
+ grub_memmove (mem_regions + j, mem_regions + j + 1,
+ (num_regions - j - 1) * sizeof (struct mem_region));
+ i--;
+ num_regions--;
+ }
+}
+
+void
+grub_machine_init (void)
+{
+ int i;
+#if 0
+ int grub_lower_mem;
+#endif
+
+ /* Initialize the console as early as possible. */
+ grub_console_init ();
+
+ /* This sanity check is useless since top of GRUB_MEMORY_MACHINE_RESERVED_END
+ is used for stack and if it's unavailable we wouldn't have gotten so far.
+ */
+#if 0
+ grub_lower_mem = grub_get_conv_memsize () << 10;
+
+ /* Sanity check. */
+ if (grub_lower_mem < GRUB_MEMORY_MACHINE_RESERVED_END)
+ grub_fatal ("too small memory");
+#endif
+
+/* FIXME: This prevents loader/i386/linux.c from using low memory. When our
+ heap implements support for requesting a chunk in low memory, this should
+ no longer be a problem. */
+#if 0
+ /* Add the lower memory into free memory. */
+ if (grub_lower_mem >= GRUB_MEMORY_MACHINE_RESERVED_END)
+ add_mem_region (GRUB_MEMORY_MACHINE_RESERVED_END,
+ grub_lower_mem - GRUB_MEMORY_MACHINE_RESERVED_END);
+#endif
+
+ 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)
+ {
+ /* Avoid the lower memory. */
+ if (addr < 0x100000)
+ {
+ if (size <= 0x100000 - addr)
+ return 0;
+
+ size -= 0x100000 - addr;
+ addr = 0x100000;
+ }
+
+ /* Ignore >4GB. */
+ if (addr <= 0xFFFFFFFF && type == GRUB_MEMORY_AVAILABLE)
+ {
+ grub_size_t len;
+
+ len = (grub_size_t) ((addr + size > 0xFFFFFFFF)
+ ? 0xFFFFFFFF - addr
+ : size);
+ add_mem_region (addr, len);
+ }
+
+ return 0;
+ }
+
+ grub_machine_mmap_iterate (hook);
+
+ compact_mem_regions ();
+
+ for (i = 0; i < num_regions; i++)
+ grub_mm_init_region ((void *) mem_regions[i].addr, mem_regions[i].size);
+
+ grub_tsc_init ();
+}
+
+void
+grub_machine_set_prefix (void)
+{
+ /* Initialize the prefix. */
+ grub_env_set ("prefix", make_install_device ());
+}
+
+void
+grub_machine_fini (void)
+{
+ grub_console_fini ();
+ grub_stop_floppy ();
+}
+
+/* Return the end of the core image. */
+grub_addr_t
+grub_arch_modules_addr (void)
+{
+ return GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR
+ + (grub_kernel_image_size - GRUB_KERNEL_MACHINE_RAW_SIZE);
+}
diff --git a/grub-core/kern/i386/pc/lzma_decode.S b/grub-core/kern/i386/pc/lzma_decode.S
new file mode 100644
index 0000000..88c668d
--- /dev/null
+++ b/grub-core/kern/i386/pc/lzma_decode.S
@@ -0,0 +1,614 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/>.
+ */
+
+#define FIXED_PROPS
+
+#define LZMA_BASE_SIZE 1846
+#define LZMA_LIT_SIZE 768
+
+#define LZMA_PROPERTIES_SIZE 5
+
+#define kNumTopBits 24
+#define kTopValue (1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+
+#define kNumPosBitsMax 4
+#define kNumPosStatesMax (1 << kNumPosBitsMax)
+
+#define kLenNumLowBits 3
+#define kLenNumLowSymbols (1 << kLenNumLowBits)
+#define kLenNumMidBits 3
+#define kLenNumMidSymbols (1 << kLenNumMidBits)
+#define kLenNumHighBits 8
+#define kLenNumHighSymbols (1 << kLenNumHighBits)
+
+#define LenChoice 0
+#define LenChoice2 (LenChoice + 1)
+#define LenLow (LenChoice2 + 1)
+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
+#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
+
+
+#define kNumStates 12
+#define kNumLitStates 7
+
+#define kStartPosModelIndex 4
+#define kEndPosModelIndex 14
+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
+
+#define kNumPosSlotBits 6
+#define kNumLenToPosStates 4
+
+#define kNumAlignBits 4
+#define kAlignTableSize (1 << kNumAlignBits)
+
+#define kMatchMinLen 2
+
+#define IsMatch 0
+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
+#define IsRepG0 (IsRep + kNumStates)
+#define IsRepG1 (IsRepG0 + kNumStates)
+#define IsRepG2 (IsRepG1 + kNumStates)
+#define IsRep0Long (IsRepG2 + kNumStates)
+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
+#define LenCoder (Align + kAlignTableSize)
+#define RepLenCoder (LenCoder + kNumLenProbs)
+#define Literal (RepLenCoder + kNumLenProbs)
+
+#define out_size 8(%ebp)
+
+#define now_pos -4(%ebp)
+#define prev_byte -8(%ebp)
+#define range -12(%ebp)
+#define code -16(%ebp)
+#define state -20(%ebp)
+#define rep0 -24(%ebp)
+#define rep1 -28(%ebp)
+#define rep2 -32(%ebp)
+#define rep3 -36(%ebp)
+
+#ifdef FIXED_PROPS
+
+#define FIXED_LC 3
+#define FIXED_LP 0
+#define FIXED_PB 2
+
+#define POS_STATE_MASK ((1 << (FIXED_PB)) - 1)
+#define LIT_POS_MASK ((1 << (FIXED_LP)) - 1)
+
+#define LOCAL_SIZE 36
+
+#else
+
+#define lc (%ebx)
+#define lp 4(%ebx)
+#define pb 8(%ebx)
+#define probs 12(%ebx)
+
+#define pos_state_mask -40(%ebp)
+#define lit_pos_mask -44(%ebp)
+
+#define LOCAL_SIZE 44
+
+#endif
+
+RangeDecoderBitDecode:
+#ifdef FIXED_PROPS
+ leal (%ebx, %eax, 4), %eax
+#else
+ shll $2, %eax
+ addl probs, %eax
+#endif
+
+ movl %eax, %ecx
+ movl (%ecx), %eax
+
+ movl range, %edx
+ shrl $kNumBitModelTotalBits, %edx
+ mull %edx
+
+ cmpl code, %eax
+ jbe 1f
+
+ movl %eax, range
+ movl $kBitModelTotal, %edx
+ subl (%ecx), %edx
+ shrl $kNumMoveBits, %edx
+ addl %edx, (%ecx)
+ clc
+3:
+ pushf
+ cmpl $kTopValue, range
+ jnc 2f
+ shll $8, code
+ lodsb
+ movb %al, code
+ shll $8, range
+2:
+ popf
+ ret
+1:
+ subl %eax, range
+ subl %eax, code
+ movl (%ecx), %edx
+ shrl $kNumMoveBits, %edx
+ subl %edx, (%ecx)
+ stc
+ jmp 3b
+
+RangeDecoderBitTreeDecode:
+RangeDecoderReverseBitTreeDecode:
+ movzbl %cl, %ecx
+ xorl %edx, %edx
+ pushl %edx
+ incl %edx
+ pushl %edx
+
+1:
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+
+ addl %edx, %eax
+ call RangeDecoderBitDecode
+
+ popl %edx
+ popl %ecx
+
+ jnc 2f
+ movl 4(%esp), %eax
+ orl %eax, 8(%esp)
+ stc
+
+2:
+ adcl %edx, %edx
+ popl %eax
+
+ shll $1, (%esp)
+ loop 1b
+
+ popl %ecx
+ subl %ecx, %edx /* RangeDecoderBitTreeDecode */
+ popl %ecx /* RangeDecoderReverseBitTreeDecode */
+ ret
+
+LzmaLenDecode:
+ pushl %eax
+ addl $LenChoice, %eax
+ call RangeDecoderBitDecode
+ popl %eax
+ jc 1f
+ pushl $0
+ movb $kLenNumLowBits, %cl
+ addl $LenLow, %eax
+2:
+ movl 12(%esp), %edx
+ shll %cl, %edx
+ addl %edx, %eax
+3:
+
+ call RangeDecoderBitTreeDecode
+ popl %eax
+ addl %eax, %edx
+ ret
+
+1:
+ pushl %eax
+ addl $LenChoice2, %eax
+ call RangeDecoderBitDecode
+ popl %eax
+ jc 1f
+ pushl $kLenNumLowSymbols
+ movb $kLenNumMidBits, %cl
+ addl $LenMid, %eax
+ jmp 2b
+
+1:
+ pushl $(kLenNumLowSymbols + kLenNumMidSymbols)
+ addl $LenHigh, %eax
+ movb $kLenNumHighBits, %cl
+ jmp 3b
+
+WriteByte:
+ movb %al, prev_byte
+ stosb
+ incl now_pos
+ ret
+
+/*
+ * int LzmaDecode(CLzmaDecoderState *vs,
+ * const unsigned char *inStream,
+ * unsigned char *outStream,
+ * SizeT outSize);
+ */
+
+_LzmaDecodeA:
+
+ pushl %ebp
+ movl %esp, %ebp
+ subl $LOCAL_SIZE, %esp
+
+#ifndef ASM_FILE
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl %eax, %ebx
+ movl %edx, %esi
+ pushl %ecx
+#else
+ pushl %edi
+#endif
+
+ cld
+
+#ifdef FIXED_PROPS
+ movl %ebx, %edi
+ movl $(Literal + (LZMA_LIT_SIZE << (FIXED_LC + FIXED_LP))), %ecx
+#else
+ movl $LZMA_LIT_SIZE, %eax
+ movb lc, %cl
+ addb lp, %cl
+ shll %cl, %eax
+ addl $Literal, %eax
+ movl %eax, %ecx
+ movl probs, %edi
+#endif
+
+ movl $(kBitModelTotal >> 1), %eax
+
+ rep
+ stosl
+
+ popl %edi
+
+ xorl %eax, %eax
+ movl %eax, now_pos
+ movl %eax, prev_byte
+ movl %eax, state
+
+ incl %eax
+ movl %eax, rep0
+ movl %eax, rep1
+ movl %eax, rep2
+ movl %eax, rep3
+
+#ifndef FIXED_PROPS
+ movl %eax, %edx
+ movb pb, %cl
+ shll %cl, %edx
+ decl %edx
+ movl %edx, pos_state_mask
+
+ movl %eax, %edx
+ movb lp, %cl
+ shll %cl, %edx
+ decl %edx
+ movl %edx, lit_pos_mask;
+#endif
+
+ /* RangeDecoderInit */
+ negl %eax
+ movl %eax, range
+
+ incl %eax
+ movb $5, %cl
+
+1:
+ shll $8, %eax
+ lodsb
+ loop 1b
+
+ movl %eax, code
+
+lzma_decode_loop:
+ movl now_pos, %eax
+ cmpl out_size, %eax
+
+ jb 1f
+
+#ifndef ASM_FILE
+ xorl %eax, %eax
+
+ popl %ebx
+ popl %edi
+ popl %esi
+#endif
+
+ movl %ebp, %esp
+ popl %ebp
+ ret
+
+1:
+#ifdef FIXED_PROPS
+ andl $POS_STATE_MASK, %eax
+#else
+ andl pos_state_mask, %eax
+#endif
+ pushl %eax /* posState */
+ movl state, %edx
+ shll $kNumPosBitsMax, %edx
+ addl %edx, %eax
+ pushl %eax /* (state << kNumPosBitsMax) + posState */
+
+ call RangeDecoderBitDecode
+ jc 1f
+
+ movl now_pos, %eax
+
+#ifdef FIXED_PROPS
+ andl $LIT_POS_MASK, %eax
+ shll $FIXED_LC, %eax
+ movl prev_byte, %edx
+ shrl $(8 - FIXED_LC), %edx
+#else
+ andl lit_pos_mask, %eax
+ movb lc, %cl
+ shll %cl, %eax
+ negb %cl
+ addb $8, %cl
+ movl prev_byte, %edx
+ shrl %cl, %edx
+#endif
+
+ addl %edx, %eax
+ movl $LZMA_LIT_SIZE, %edx
+ mull %edx
+ addl $Literal, %eax
+ pushl %eax
+
+ incl %edx /* edx = 1 */
+
+ movl rep0, %eax
+ negl %eax
+ pushl (%edi, %eax) /* matchByte */
+
+ cmpb $kNumLitStates, state
+ jb 5f
+
+ /* LzmaLiteralDecodeMatch */
+
+3:
+ cmpl $0x100, %edx
+ jae 4f
+
+ xorl %eax, %eax
+ shlb $1, (%esp)
+ adcl %eax, %eax
+
+ pushl %eax
+ pushl %edx
+
+ shll $8, %eax
+ leal 0x100(%edx, %eax), %eax
+ addl 12(%esp), %eax
+ call RangeDecoderBitDecode
+
+ setc %al
+ popl %edx
+ adcl %edx, %edx
+
+ popl %ecx
+ cmpb %cl, %al
+ jz 3b
+
+5:
+
+ /* LzmaLiteralDecode */
+
+ cmpl $0x100, %edx
+ jae 4f
+
+ pushl %edx
+ movl %edx, %eax
+ addl 8(%esp), %eax
+ call RangeDecoderBitDecode
+ popl %edx
+ adcl %edx, %edx
+ jmp 5b
+
+4:
+ addl $16, %esp
+
+ movb %dl, %al
+ call WriteByte
+
+ movb state, %al
+ cmpb $4, %al
+ jae 2f
+ xorb %al, %al
+ jmp 3f
+2:
+ subb $3, %al
+ cmpb $7, %al
+ jb 3f
+ subb $3, %al
+3:
+ movb %al, state
+ jmp lzma_decode_loop
+
+1:
+ movl state, %eax
+ addl $IsRep, %eax
+ call RangeDecoderBitDecode
+ jnc 1f
+
+ movl state, %eax
+ addl $IsRepG0, %eax
+ call RangeDecoderBitDecode
+ jc 10f
+
+ movl (%esp), %eax
+ addl $IsRep0Long, %eax
+ call RangeDecoderBitDecode
+ jc 20f
+
+ cmpb $7, state
+ movb $9, state
+ jb 100f
+ addb $2, state
+100:
+
+ movl $1, %ecx
+
+3:
+ movl rep0, %edx
+ negl %edx
+
+4:
+ movb (%edi, %edx), %al
+ call WriteByte
+ loop 4b
+
+ popl %eax
+ popl %eax
+ jmp lzma_decode_loop
+
+10:
+ movl state, %eax
+ addl $IsRepG1, %eax
+ call RangeDecoderBitDecode
+ movl rep1, %edx
+ jnc 100f
+
+ movl state, %eax
+ addl $IsRepG2, %eax
+ call RangeDecoderBitDecode
+ movl rep2, %edx
+ jnc 1000f
+ movl rep2, %edx
+ xchgl rep3, %edx
+1000:
+ pushl rep1
+ popl rep2
+100:
+ xchg rep0, %edx
+ movl %edx, rep1
+20:
+
+ movl $RepLenCoder, %eax
+ call LzmaLenDecode
+
+ cmpb $7, state
+ movb $8, state
+ jb 100f
+ addb $3, state
+100:
+ jmp 2f
+
+1:
+ movl rep0, %eax
+ xchgl rep1, %eax
+ xchgl rep2, %eax
+ movl %eax, rep3
+
+ cmpb $7, state
+ movb $7, state
+ jb 10f
+ addb $3, state
+10:
+
+ movl $LenCoder, %eax
+ call LzmaLenDecode
+ pushl %edx
+
+ movl $(kNumLenToPosStates - 1), %eax
+ cmpl %eax, %edx
+ jbe 100f
+ movl %eax, %edx
+100:
+ movb $kNumPosSlotBits, %cl
+ shll %cl, %edx
+ leal PosSlot(%edx), %eax
+ call RangeDecoderBitTreeDecode
+
+ movl %edx, rep0
+ cmpl $kStartPosModelIndex, %edx
+ jb 100f
+
+ movl %edx, %ecx
+ shrl $1, %ecx
+ decl %ecx
+
+ movzbl %dl, %eax
+ andb $1, %al
+ orb $2, %al
+ shll %cl, %eax
+ movl %eax, rep0
+
+ cmpl $kEndPosModelIndex, %edx
+ jae 200f
+ movl rep0, %eax
+ addl $(SpecPos - 1), %eax
+ subl %edx, %eax
+ jmp 300f
+200:
+
+ subb $kNumAlignBits, %cl
+
+ /* RangeDecoderDecodeDirectBits */
+ xorl %edx, %edx
+
+1000:
+ shrl $1, range
+ shll $1, %edx
+
+ movl range, %eax
+ cmpl %eax, code
+ jb 2000f
+ subl %eax, code
+ orb $1, %dl
+2000:
+
+ cmpl $kTopValue, %eax
+ jae 3000f
+ shll $8, range
+ shll $8, code
+ lodsb
+ movb %al, code
+
+3000:
+ loop 1000b
+
+ movb $kNumAlignBits, %cl
+ shll %cl, %edx
+ addl %edx, rep0
+
+ movl $Align, %eax
+
+300:
+ call RangeDecoderReverseBitTreeDecode
+ addl %ecx, rep0
+
+100:
+ incl rep0
+ popl %edx
+
+2:
+
+ addl $kMatchMinLen, %edx
+ movl %edx, %ecx
+
+ jmp 3b
diff --git a/grub-core/kern/i386/pc/mmap.c b/grub-core/kern/i386/pc/mmap.c
new file mode 100644
index 0000000..480ffa9
--- /dev/null
+++ b/grub-core/kern/i386/pc/mmap.c
@@ -0,0 +1,190 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2004,2005,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/>.
+ */
+
+#include <grub/machine/memory.h>
+#include <grub/machine/int.h>
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+
+struct grub_machine_mmap_entry
+{
+ grub_uint32_t size;
+ grub_uint64_t addr;
+ grub_uint64_t len;
+#define GRUB_MACHINE_MEMORY_AVAILABLE 1
+#define GRUB_MACHINE_MEMORY_RESERVED 2
+#define GRUB_MACHINE_MEMORY_ACPI 3
+#define GRUB_MACHINE_MEMORY_NVS 4
+#define GRUB_MACHINE_MEMORY_BADRAM 5
+ grub_uint32_t type;
+} __attribute__((packed));
+
+
+/*
+ *
+ * grub_get_conv_memsize(i) : return the conventional memory size in KB.
+ * BIOS call "INT 12H" to get conventional memory size
+ * The return value in AX.
+ */
+static inline grub_uint16_t
+grub_get_conv_memsize (void)
+{
+ struct grub_bios_int_registers regs;
+
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x12, &regs);
+ return regs.eax & 0xffff;
+}
+
+/*
+ * grub_get_ext_memsize() : return the extended memory size in KB.
+ * BIOS call "INT 15H, AH=88H" to get extended memory size
+ * The return value in AX.
+ *
+ */
+static inline grub_uint16_t
+grub_get_ext_memsize (void)
+{
+ struct grub_bios_int_registers regs;
+
+ regs.eax = 0x8800;
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ grub_bios_interrupt (0x15, &regs);
+ return regs.eax & 0xffff;
+}
+
+/* Get a packed EISA memory map. Lower 16 bits are between 1MB and 16MB
+ in 1KB parts, and upper 16 bits are above 16MB in 64KB parts. If error, return zero.
+ BIOS call "INT 15H, AH=E801H" to get EISA memory map,
+ AX = memory between 1M and 16M in 1K parts.
+ BX = memory above 16M in 64K parts.
+*/
+
+static inline grub_uint32_t
+grub_get_eisa_mmap (void)
+{
+ struct grub_bios_int_registers regs;
+
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+ regs.eax = 0xe801;
+ grub_bios_interrupt (0x15, &regs);
+
+ if ((regs.eax & 0xff00) == 0x8600)
+ return 0;
+
+ return (regs.eax & 0xffff) | (regs.ebx << 16);
+}
+
+/*
+ *
+ * grub_get_mmap_entry(addr, cont) : address and old continuation value (zero to
+ * start), for the Query System Address Map BIOS call.
+ *
+ * Sets the first 4-byte int value of "addr" to the size returned by
+ * the call. If the call fails, sets it to zero.
+ *
+ * Returns: new (non-zero) continuation value, 0 if done.
+ */
+/* Get a memory map entry. Return next continuation value. Zero means
+ the end. */
+static grub_uint32_t
+grub_get_mmap_entry (struct grub_machine_mmap_entry *entry,
+ grub_uint32_t cont)
+{
+ struct grub_bios_int_registers regs;
+
+ regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+
+ /* place address (+4) in ES:DI */
+ regs.es = ((grub_addr_t) &entry->addr) >> 4;
+ regs.edi = ((grub_addr_t) &entry->addr) & 0xf;
+
+ /* set continuation value */
+ regs.ebx = cont;
+
+ /* set default maximum buffer size */
+ regs.ecx = sizeof (*entry) - sizeof (entry->size);
+
+ /* set EDX to 'SMAP' */
+ regs.edx = 0x534d4150;
+
+ regs.eax = 0xe820;
+ grub_bios_interrupt (0x15, &regs);
+
+ /* write length of buffer (zero if error) into ADDR */
+ if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) || regs.eax != 0x534d4150
+ || regs.ecx < 0x14 || regs.ecx > 0x400)
+ entry->size = 0;
+ else
+ entry->size = regs.ecx;
+
+ /* return the continuation value */
+ return regs.ebx;
+}
+
+grub_err_t
+grub_machine_mmap_iterate (grub_memory_hook_t hook)
+{
+ grub_uint32_t cont;
+ struct grub_machine_mmap_entry *entry
+ = (struct grub_machine_mmap_entry *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+
+ grub_memset (entry, 0, sizeof (entry));
+
+ /* Check if grub_get_mmap_entry works. */
+ cont = grub_get_mmap_entry (entry, 0);
+
+ if (entry->size)
+ do
+ {
+ if (hook (entry->addr, entry->len,
+ /* GRUB mmaps have been defined to match with the E820 definition.
+ Therefore, we can just pass type through. */
+ ((entry->type <= GRUB_MACHINE_MEMORY_BADRAM) && (entry->type >= GRUB_MACHINE_MEMORY_AVAILABLE)) ? entry->type : GRUB_MEMORY_RESERVED))
+ break;
+
+ if (! cont)
+ break;
+
+ grub_memset (entry, 0, sizeof (entry));
+
+ cont = grub_get_mmap_entry (entry, cont);
+ }
+ while (entry->size);
+ else
+ {
+ grub_uint32_t eisa_mmap = grub_get_eisa_mmap ();
+
+ if (hook (0x0, ((grub_uint32_t) grub_get_conv_memsize ()) << 10,
+ GRUB_MEMORY_AVAILABLE))
+ return 0;
+
+ if (eisa_mmap)
+ {
+ if (hook (0x100000, (eisa_mmap & 0xFFFF) << 10,
+ GRUB_MEMORY_AVAILABLE) == 0)
+ hook (0x1000000, eisa_mmap & ~0xFFFF, GRUB_MEMORY_AVAILABLE);
+ }
+ else
+ hook (0x100000, ((grub_uint32_t) grub_get_ext_memsize ()) << 10,
+ GRUB_MEMORY_AVAILABLE);
+ }
+
+ return 0;
+}
diff --git a/grub-core/kern/i386/pc/startup.S b/grub-core/kern/i386/pc/startup.S
new file mode 100644
index 0000000..e78a0aa
--- /dev/null
+++ b/grub-core/kern/i386/pc/startup.S
@@ -0,0 +1,1011 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * Note: These functions defined in this file may be called from C.
+ * Be careful of that you must not modify some registers. Quote
+ * from gcc-2.95.2/gcc/config/i386/i386.h:
+
+ 1 for registers not available across function calls.
+ These must include the FIXED_REGISTERS and also any
+ registers that can be used without being saved.
+ The latter must include the registers where values are returned
+ and the register where structure-value addresses are passed.
+ Aside from that, you can include as many other registers as you like.
+
+ ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
+{ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
+ */
+
+/*
+ * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
+ * So the first three arguments are passed in %eax, %edx, and %ecx,
+ * respectively, and if a function has a fixed number of arguments
+ * and the number is greater than three, the function must return
+ * with "ret $N" where N is ((the number of arguments) - 3) * 4.
+ */
+
+#include <config.h>
+#include <grub/symbol.h>
+#include <grub/boot.h>
+#include <grub/machine/boot.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/console.h>
+#include <grub/cpu/linux.h>
+#include <grub/machine/kernel.h>
+#include <grub/term.h>
+#include <multiboot.h>
+#include <multiboot2.h>
+
+#define ABS(x) ((x) - LOCAL (base) + GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200)
+
+ .file "startup.S"
+
+ .text
+
+ /* Tell GAS to generate 16-bit instructions so that this code works
+ in real mode. */
+ .code16
+
+ .globl start, _start
+start:
+_start:
+LOCAL (base):
+ /*
+ * Guarantee that "main" is loaded at 0x0:0x8200.
+ */
+#ifdef __APPLE__
+ ljmp $0, $(ABS(LOCAL (codestart)) - 0x10000)
+#else
+ ljmp $0, $ABS(LOCAL (codestart))
+#endif
+ /*
+ * Compatibility version number
+ *
+ * These MUST be at byte offset 6 and 7 of the executable
+ * DO NOT MOVE !!!
+ */
+ . = _start + 0x6
+ .byte GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
+
+ /*
+ * This is a special data area 8 bytes from the beginning.
+ */
+
+ . = _start + 0x8
+
+VARIABLE(grub_total_module_size)
+ .long 0
+VARIABLE(grub_kernel_image_size)
+ .long 0
+VARIABLE(grub_compressed_size)
+ .long 0
+VARIABLE(grub_install_dos_part)
+ .long 0xFFFFFFFF
+VARIABLE(grub_install_bsd_part)
+ .long 0xFFFFFFFF
+reed_solomon_redundancy:
+ .long 0
+
+#ifdef APPLE_CC
+bss_start:
+ .long 0
+bss_end:
+ .long 0
+#endif
+/*
+ * This is the area for all of the special variables.
+ */
+
+VARIABLE(grub_boot_drive)
+ .byte 0
+
+/* the real mode code continues... */
+LOCAL (codestart):
+ cli /* we're not safe here! */
+
+ /* set up %ds, %ss, and %es */
+ xorw %ax, %ax
+ movw %ax, %ds
+ movw %ax, %ss
+ movw %ax, %es
+
+ /* set up the real mode/BIOS stack */
+ movl $GRUB_MEMORY_MACHINE_REAL_STACK, %ebp
+ movl %ebp, %esp
+
+ sti /* we're safe again */
+
+ /* save the boot drive */
+ ADDR32 movb %dl, EXT_C(grub_boot_drive)
+
+ /* reset disk system (%ah = 0) */
+ int $0x13
+
+ /* transition to protected mode */
+ DATA32 call real_to_prot
+
+ /* The ".code32" directive takes GAS out of 16-bit mode. */
+ .code32
+
+ incl %eax
+ call grub_gate_a20
+
+ movl EXT_C(grub_compressed_size), %edx
+ addl $(GRUB_KERNEL_MACHINE_RAW_SIZE - GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_PART), %edx
+ movl reed_solomon_redundancy, %ecx
+ leal _start + GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_PART, %eax
+ call EXT_C (grub_reed_solomon_recover)
+ jmp post_reed_solomon
+
+#include <rs_decoder.S>
+
+ .text
+
+ . = _start + GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_PART
+/*
+ * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB itself).
+ * This uses the a.out kludge to load raw binary to the area starting at 1MB,
+ * and relocates itself after loaded.
+ */
+ .p2align 2 /* force 4-byte alignment */
+multiboot_header:
+ /* magic */
+ .long 0x1BADB002
+ /* flags */
+ .long (1 << 16)
+ /* checksum */
+ .long -0x1BADB002 - (1 << 16)
+ /* header addr */
+ .long multiboot_header - _start + 0x100000 + 0x200
+ /* load addr */
+ .long 0x100000
+ /* load end addr */
+ .long 0
+ /* bss end addr */
+ .long 0
+ /* entry addr */
+ .long multiboot_entry - _start + 0x100000 + 0x200
+
+multiboot_entry:
+ .code32
+ /* obtain the boot device */
+ movl 12(%ebx), %edx
+
+ movl $GRUB_MEMORY_MACHINE_PROT_STACK, %ebp
+ movl %ebp, %esp
+
+ /* relocate the code */
+ movl $(GRUB_KERNEL_MACHINE_RAW_SIZE + 0x200), %ecx
+ addl EXT_C(grub_compressed_size) - _start + 0x100000 + 0x200, %ecx
+ movl $0x100000, %esi
+ movl $GRUB_BOOT_MACHINE_KERNEL_ADDR, %edi
+ cld
+ rep
+ movsb
+ /* jump to the real address */
+ movl $multiboot_trampoline, %eax
+ jmp *%eax
+
+multiboot_trampoline:
+ /* fill the boot information */
+ movl %edx, %eax
+ shrl $8, %eax
+ xorl %ebx, %ebx
+ cmpb $0xFF, %ah
+ je 1f
+ movb %ah, %bl
+ movl %ebx, EXT_C(grub_install_dos_part)
+1:
+ cmpb $0xFF, %al
+ je 2f
+ movb %al, %bl
+ movl %ebx, EXT_C(grub_install_bsd_part)
+2:
+ shrl $24, %edx
+ movb $0xFF, %dh
+ /* enter the usual booting */
+ call prot_to_real
+ jmp LOCAL (codestart)
+
+post_reed_solomon:
+
+#ifdef ENABLE_LZMA
+ movl $GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR, %edi
+ movl $(_start + GRUB_KERNEL_MACHINE_RAW_SIZE), %esi
+ pushl %edi
+ pushl %esi
+ movl EXT_C(grub_kernel_image_size), %ecx
+ addl EXT_C(grub_total_module_size), %ecx
+ subl $GRUB_KERNEL_MACHINE_RAW_SIZE, %ecx
+ pushl %ecx
+ leal (%edi, %ecx), %ebx
+ call _LzmaDecodeA
+ /* _LzmaDecodeA clears DF, so no need to run cld */
+ popl %ecx
+ popl %edi
+ popl %esi
+#endif
+
+ /* copy back the decompressed part (except the modules) */
+ subl EXT_C(grub_total_module_size), %ecx
+ rep
+ movsb
+
+#if 0
+ /* copy modules before cleaning out the bss */
+ movl EXT_C(grub_total_module_size), %ecx
+ movl EXT_C(grub_kernel_image_size), %esi
+ addl %ecx, %esi
+ addl $_start, %esi
+ decl %esi
+ movl $END_SYMBOL, %edi
+ addl %ecx, %edi
+ decl %edi
+ std
+ rep
+ movsb
+#endif
+
+#ifdef APPLE_CC
+ /* clean out the bss */
+ bss_start_abs = ABS (bss_start)
+ bss_end_abs = ABS (bss_end)
+
+ movl bss_start_abs, %edi
+
+ /* compute the bss length */
+ movl bss_end_abs, %ecx
+ subl %edi, %ecx
+#else
+ /* clean out the bss */
+ movl $BSS_START_SYMBOL, %edi
+
+ /* compute the bss length */
+ movl $END_SYMBOL, %ecx
+ subl %edi, %ecx
+#endif
+
+ /* clean out */
+ xorl %eax, %eax
+ cld
+ rep
+ stosb
+
+ /*
+ * Call the start of main body of C code.
+ */
+ call EXT_C(grub_main)
+
+#include "../realmode.S"
+
+/*
+ * grub_gate_a20(int on)
+ *
+ * Gate address-line 20 for high memory.
+ *
+ * This routine is probably overconservative in what it does, but so what?
+ *
+ * It also eats any keystrokes in the keyboard buffer. :-(
+ */
+
+grub_gate_a20:
+ movl %eax, %edx
+
+gate_a20_test_current_state:
+ /* first of all, test if already in a good state */
+ call gate_a20_check_state
+ cmpb %al, %dl
+ jnz gate_a20_try_bios
+ ret
+
+gate_a20_try_bios:
+ /* second, try a BIOS call */
+ pushl %ebp
+ call prot_to_real
+
+ .code16
+ movw $0x2400, %ax
+ testb %dl, %dl
+ jz 1f
+ incw %ax
+1: int $0x15
+
+ DATA32 call real_to_prot
+ .code32
+
+ popl %ebp
+ call gate_a20_check_state
+ cmpb %al, %dl
+ jnz gate_a20_try_system_control_port_a
+ ret
+
+gate_a20_try_system_control_port_a:
+ /*
+ * In macbook, the keyboard test would hang the machine, so we move
+ * this forward.
+ */
+ /* fourth, try the system control port A */
+ inb $0x92
+ andb $(~0x03), %al
+ testb %dl, %dl
+ jz 6f
+ orb $0x02, %al
+6: outb $0x92
+
+ /* When turning off Gate A20, do not check the state strictly,
+ because a failure is not fatal usually, and Gate A20 is always
+ on some modern machines. */
+ testb %dl, %dl
+ jz 7f
+ call gate_a20_check_state
+ cmpb %al, %dl
+ jnz gate_a20_try_keyboard_controller
+7: ret
+
+gate_a20_flush_keyboard_buffer:
+ inb $0x64
+ andb $0x02, %al
+ jnz gate_a20_flush_keyboard_buffer
+2:
+ inb $0x64
+ andb $0x01, %al
+ jz 3f
+ inb $0x60
+ jmp 2b
+3:
+ ret
+
+gate_a20_try_keyboard_controller:
+ /* third, try the keyboard controller */
+ call gate_a20_flush_keyboard_buffer
+
+ movb $0xd1, %al
+ outb $0x64
+4:
+ inb $0x64
+ andb $0x02, %al
+ jnz 4b
+
+ movb $0xdd, %al
+ testb %dl, %dl
+ jz 5f
+ orb $0x02, %al
+5: outb $0x60
+ call gate_a20_flush_keyboard_buffer
+
+ /* output a dummy command (USB keyboard hack) */
+ movb $0xff, %al
+ outb $0x64
+ call gate_a20_flush_keyboard_buffer
+
+ call gate_a20_check_state
+ cmpb %al, %dl
+ /* everything failed, so restart from the beginning */
+ jnz gate_a20_try_bios
+ ret
+
+gate_a20_check_state:
+ /* iterate the checking for a while */
+ movl $100, %ecx
+1:
+ call 3f
+ cmpb %al, %dl
+ jz 2f
+ loop 1b
+2:
+ ret
+3:
+ pushl %ebx
+ pushl %ecx
+ xorl %eax, %eax
+ /* compare the byte at 0x8000 with that at 0x108000 */
+ movl $GRUB_BOOT_MACHINE_KERNEL_ADDR, %ebx
+ pushl %ebx
+ /* save the original byte in CL */
+ movb (%ebx), %cl
+ /* store the value at 0x108000 in AL */
+ addl $0x100000, %ebx
+ movb (%ebx), %al
+ /* try to set one less value at 0x8000 */
+ popl %ebx
+ movb %al, %ch
+ decb %ch
+ movb %ch, (%ebx)
+ /* serialize */
+ outb %al, $0x80
+ outb %al, $0x80
+ /* obtain the value at 0x108000 in CH */
+ pushl %ebx
+ addl $0x100000, %ebx
+ movb (%ebx), %ch
+ /* this result is 1 if A20 is on or 0 if it is off */
+ subb %ch, %al
+ xorb $1, %al
+ /* restore the original */
+ popl %ebx
+ movb %cl, (%ebx)
+ popl %ecx
+ popl %ebx
+ ret
+
+#ifdef ENABLE_LZMA
+#include "lzma_decode.S"
+#endif
+
+/*
+ * The code beyond this point is compressed. Assert that the uncompressed
+ * code fits GRUB_KERNEL_MACHINE_RAW_SIZE.
+ */
+ . = _start + GRUB_KERNEL_MACHINE_RAW_SIZE
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX
+VARIABLE(grub_prefix)
+ /* to be filled by grub-mkimage */
+
+ /*
+ * Leave some breathing room for the prefix.
+ */
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX_END
+
+
+
+/*
+ * grub_exit()
+ *
+ * Exit the system.
+ */
+FUNCTION(grub_exit)
+ call prot_to_real
+ .code16
+ /* Tell the BIOS a boot failure. If this does not work, reboot. */
+ int $0x18
+ jmp cold_reboot
+ .code32
+
+/*
+ * void grub_chainloader_real_boot (int drive, void *part_addr)
+ *
+ * This starts another boot loader.
+ */
+
+FUNCTION(grub_chainloader_real_boot)
+ pushl %edx
+ pushl %eax
+
+ /* Turn off Gate A20 */
+ xorl %eax, %eax
+ call grub_gate_a20
+
+ /* set up to pass boot drive */
+ popl %edx
+
+ /* ESI must point to a partition table entry */
+ popl %esi
+
+ call prot_to_real
+ .code16
+ ljmp $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
+ .code32
+
+/*
+ * void grub_console_putchar (int c)
+ *
+ * Put the character C on the console. Because GRUB wants to write a
+ * character with an attribute, this implementation is a bit tricky.
+ * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh
+ * (TELETYPE OUTPUT). Otherwise, save the original position, put a space,
+ * save the current position, restore the original position, write the
+ * character and the attribute, and restore the current position.
+ *
+ * The reason why this is so complicated is that there is no easy way to
+ * get the height of the screen, and the TELETYPE OUTPUT BIOS call doesn't
+ * support setting a background attribute.
+ */
+FUNCTION(grub_console_putchar)
+ /* Retrieve the base character. */
+ movl 0(%edx), %edx
+ pusha
+ movb EXT_C(grub_console_cur_color), %bl
+
+ call prot_to_real
+ .code16
+ movb %dl, %al
+ xorb %bh, %bh
+
+ /* use teletype output if control character */
+ cmpb $0x7, %al
+ je 1f
+ cmpb $0x8, %al
+ je 1f
+ cmpb $0xa, %al
+ je 1f
+ cmpb $0xd, %al
+ je 1f
+
+ /* save the character and the attribute on the stack */
+ pushw %ax
+ pushw %bx
+
+ /* get the current position */
+ movb $0x3, %ah
+ int $0x10
+
+ /* check the column with the width */
+ cmpb $79, %dl
+ jl 2f
+
+ /* print CR and LF, if next write will exceed the width */
+ movw $0x0e0d, %ax
+ int $0x10
+ movb $0x0a, %al
+ int $0x10
+
+ /* get the current position */
+ movb $0x3, %ah
+ int $0x10
+
+2:
+ /* restore the character and the attribute */
+ popw %bx
+ popw %ax
+
+ /* write the character with the attribute */
+ movb $0x9, %ah
+ movw $1, %cx
+ int $0x10
+
+ /* move the cursor forward */
+ incb %dl
+ movb $0x2, %ah
+ int $0x10
+
+ jmp 3f
+
+1: movw $1, %bx
+ movb $0xe, %ah
+ int $0x10
+
+3: DATA32 call real_to_prot
+ .code32
+
+ popa
+ ret
+
+
+LOCAL(bypass_table):
+ .word 0x011b, 0x0f00 | '\t', 0x0e00 | '\b', 0x1c00 | '\r'
+ .word 0x1c00 | '\n'
+LOCAL(bypass_table_end):
+
+/*
+ * int grub_console_getkey (void)
+ * if there is a character pending, return it; otherwise return -1
+ * BIOS call "INT 16H Function 01H" to check whether a character is pending
+ * Call with %ah = 0x1
+ * Return:
+ * If key waiting to be input:
+ * %ah = keyboard scan code
+ * %al = ASCII character
+ * Zero flag = clear
+ * else
+ * Zero flag = set
+ * BIOS call "INT 16H Function 00H" to read character from keyboard
+ * Call with %ah = 0x0
+ * Return: %ah = keyboard scan code
+ * %al = ASCII character
+ */
+
+FUNCTION(grub_console_getkey)
+ pushl %ebp
+ pushl %edi
+
+ call prot_to_real
+ .code16
+
+ /*
+ * Due to a bug in apple's bootcamp implementation, INT 16/AH = 0 would
+ * cause the machine to hang at the second keystroke. However, we can
+ * work around this problem by ensuring the presence of keystroke with
+ * INT 16/AH = 1 before calling INT 16/AH = 0.
+ */
+
+ movb $1, %ah
+ int $0x16
+ jz notpending
+
+ movb $0, %ah
+ int $0x16
+
+ xorl %edx, %edx
+ movw %ax, %dx /* real_to_prot uses %eax */
+
+ DATA32 call real_to_prot
+ .code32
+
+ movl $0xff, %eax
+ testl %eax, %edx
+ jz 1f
+
+ andl %edx, %eax
+ cmpl $0x20, %eax
+ jae 2f
+ movl %edx, %eax
+ leal LOCAL(bypass_table), %edi
+ movl $((LOCAL(bypass_table_end) - LOCAL(bypass_table)) >> 1), %ecx
+ repne scasw
+ jz 3f
+
+ andl $0xff, %eax
+ addl $(('a' - 1) | GRUB_TERM_CTRL), %eax
+ jmp 2f
+3:
+ andl $0xff, %eax
+ jmp 2f
+
+1: movl %edx, %eax
+ shrl $8, %eax
+ orl $GRUB_TERM_EXTENDED, %eax
+2:
+ popl %edi
+ popl %ebp
+ ret
+
+notpending:
+ .code16
+ DATA32 call real_to_prot
+ .code32
+#if GRUB_TERM_NO_KEY != 0
+#error Fix this asm code
+#endif
+ jmp 2b
+
+
+/*
+ * grub_uint16_t grub_console_getxy (void)
+ * BIOS call "INT 10H Function 03h" to get cursor position
+ * Call with %ah = 0x03
+ * %bh = page
+ * Returns %ch = starting scan line
+ * %cl = ending scan line
+ * %dh = row (0 is top)
+ * %dl = column (0 is left)
+ */
+
+
+FUNCTION(grub_console_getxy)
+ pushl %ebp
+ pushl %ebx /* save EBX */
+
+ call prot_to_real
+ .code16
+
+ xorb %bh, %bh /* set page to 0 */
+ movb $0x3, %ah
+ int $0x10 /* get cursor position */
+
+ DATA32 call real_to_prot
+ .code32
+
+ movb %dl, %ah
+ movb %dh, %al
+
+ popl %ebx
+ popl %ebp
+ ret
+
+
+/*
+ * void grub_console_gotoxy(grub_uint8_t x, grub_uint8_t y)
+ * BIOS call "INT 10H Function 02h" to set cursor position
+ * Call with %ah = 0x02
+ * %bh = page
+ * %dh = row (0 is top)
+ * %dl = column (0 is left)
+ */
+
+
+FUNCTION(grub_console_gotoxy)
+ pushl %ebp
+ pushl %ebx /* save EBX */
+
+ movb %cl, %dh /* %dh = y */
+ /* %dl = x */
+
+ call prot_to_real
+ .code16
+
+ xorb %bh, %bh /* set page to 0 */
+ movb $0x2, %ah
+ int $0x10 /* set cursor position */
+
+ DATA32 call real_to_prot
+ .code32
+
+ popl %ebx
+ popl %ebp
+ ret
+
+
+/*
+ * void grub_console_cls (void)
+ * BIOS call "INT 10H Function 09h" to write character and attribute
+ * Call with %ah = 0x09
+ * %al = (character)
+ * %bh = (page number)
+ * %bl = (attribute)
+ * %cx = (number of times)
+ */
+
+FUNCTION(grub_console_cls)
+ pushl %ebp
+ pushl %ebx /* save EBX */
+
+ call prot_to_real
+ .code16
+
+ /* move the cursor to the beginning */
+ movb $0x02, %ah
+ xorb %bh, %bh
+ xorw %dx, %dx
+ int $0x10
+
+ /* write spaces to the entire screen */
+ movw $0x0920, %ax
+ movw $0x07, %bx
+ movw $(80 * 25), %cx
+ int $0x10
+
+ /* move back the cursor */
+ movb $0x02, %ah
+ int $0x10
+
+ DATA32 call real_to_prot
+ .code32
+
+ popl %ebx
+ popl %ebp
+ ret
+
+
+/*
+ * void grub_console_setcursor (int on)
+ * BIOS call "INT 10H Function 01h" to set cursor type
+ * Call with %ah = 0x01
+ * %ch = cursor starting scanline
+ * %cl = cursor ending scanline
+ */
+
+console_cursor_state:
+ .byte 1
+console_cursor_shape:
+ .word 0
+
+FUNCTION(grub_console_setcursor)
+ pushl %ebp
+ pushl %ebx
+
+ /* push ON */
+ pushl %edx
+
+ /* check if the standard cursor shape has already been saved */
+ movw console_cursor_shape, %ax
+ testw %ax, %ax
+ jne 1f
+
+ call prot_to_real
+ .code16
+
+ movb $0x03, %ah
+ xorb %bh, %bh
+ int $0x10
+
+ DATA32 call real_to_prot
+ .code32
+
+ cmp %cl, %ch
+ jb 3f
+ movw $0x0d0e, %cx
+3:
+ movw %cx, console_cursor_shape
+1:
+ /* set %cx to the designated cursor shape */
+ movw $0x2000, %cx
+ popl %eax
+ testl %eax, %eax
+ jz 2f
+ movw console_cursor_shape, %cx
+2:
+ call prot_to_real
+ .code16
+
+ movb $0x1, %ah
+ int $0x10
+
+ DATA32 call real_to_prot
+ .code32
+
+ popl %ebx
+ popl %ebp
+ ret
+
+/*
+ * grub_get_rtc()
+ * return the real time in ticks, of which there are about
+ * 18-20 per second
+ */
+FUNCTION(grub_get_rtc)
+ pushl %ebp
+
+ call prot_to_real /* enter real mode */
+ .code16
+
+ /* %ax is already zero */
+ int $0x1a
+
+ DATA32 call real_to_prot
+ .code32
+
+ movl %ecx, %eax
+ shll $16, %eax
+ movw %dx, %ax
+
+ popl %ebp
+ ret
+
+/*
+ * int grub_pxe_call (int func, void* data, grub_uint32_t pxe_rm_entry);
+ */
+FUNCTION(grub_pxe_call)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl %ecx, %ebx
+ movl %eax, %ecx
+ movl %edx, %eax
+ andl $0xF, %eax
+ shrl $4, %edx
+ shll $16, %edx
+ addl %eax, %edx
+
+ call prot_to_real
+ .code16
+
+ pushl %ebx
+ pushl %edx
+ pushw %cx
+ movw %sp, %bx
+ lcall *%ss:6(%bx)
+ cld
+ addw $10, %sp
+ movw %ax, %cx
+
+ DATA32 call real_to_prot
+ .code32
+
+ movzwl %cx, %eax
+
+ popl %ebx
+ popl %edi
+ popl %esi
+ popl %ebp
+ ret
+
+FUNCTION(grub_bios_interrupt)
+ pushl %ebp
+ pushl %ecx
+ pushl %eax
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ pushl %edx
+
+ movb %al, intno
+ movl (%edx), %eax
+ movl %eax, LOCAL(bios_register_eax)
+ movw 4(%edx), %ax
+ movw %ax, LOCAL(bios_register_es)
+ movw 6(%edx), %ax
+ movw %ax, LOCAL(bios_register_ds)
+ movw 8(%edx), %ax
+ movw %ax, LOCAL(bios_register_flags)
+
+ movl 12(%edx), %ebx
+ movl 16(%edx), %ecx
+ movl 20(%edx), %edi
+ movl 24(%edx), %esi
+ movl 28(%edx), %edx
+
+ call prot_to_real
+ .code16
+
+ mov %ds, %ax
+ push %ax
+
+ /* movw imm16, %ax*/
+ .byte 0xb8
+LOCAL(bios_register_es):
+ .short 0
+ movw %ax, %es
+ /* movw imm16, %ax*/
+ .byte 0xb8
+LOCAL(bios_register_ds):
+ .short 0
+ movw %ax, %ds
+
+ /* movw imm16, %ax*/
+ .byte 0xb8
+LOCAL(bios_register_flags):
+ .short 0
+ push %ax
+ popf
+
+ /* movl imm32, %eax*/
+ .byte 0x66, 0xb8
+LOCAL(bios_register_eax):
+ .long 0
+
+ /* int imm8. */
+ .byte 0xcd
+intno:
+ .byte 0
+
+ movl %eax, %cs:LOCAL(bios_register_eax)
+ movw %ds, %ax
+ movw %ax, %cs:LOCAL(bios_register_ds)
+ pop %ax
+ mov %ax, %ds
+ pushf
+ pop %ax
+ movw %ax, LOCAL(bios_register_flags)
+ mov %es, %ax
+ movw %ax, LOCAL(bios_register_es)
+
+ DATA32 call real_to_prot
+ .code32
+
+ popl %eax
+
+ movl %ebx, 12(%eax)
+ movl %ecx, 16(%eax)
+ movl %edi, 20(%eax)
+ movl %esi, 24(%eax)
+ movl %edx, 28(%eax)
+
+ movl %eax, %edx
+
+ movl LOCAL(bios_register_eax), %eax
+ movl %eax, (%edx)
+ movw LOCAL(bios_register_es), %ax
+ movw %ax, 4(%edx)
+ movw LOCAL(bios_register_ds), %ax
+ movw %ax, 6(%edx)
+ movw LOCAL(bios_register_flags), %ax
+ movw %ax, 8(%edx)
+
+ popl %edi
+ popl %esi
+ popl %ebx
+ popl %eax
+ popl %ecx
+ popl %ebp
+ ret
diff --git a/grub-core/kern/i386/pit.c b/grub-core/kern/i386/pit.c
new file mode 100644
index 0000000..82a17d3
--- /dev/null
+++ b/grub-core/kern/i386/pit.c
@@ -0,0 +1,56 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/>.
+ */
+
+#include <grub/types.h>
+#include <grub/i386/io.h>
+#include <grub/i386/pit.h>
+
+#define TIMER2_REG_CONTROL 0x42
+#define TIMER_REG_COMMAND 0x43
+#define TIMER2_REG_LATCH 0x61
+
+#define TIMER2_SELECT 0x80
+#define TIMER_ENABLE_LSB 0x20
+#define TIMER_ENABLE_MSB 0x10
+#define TIMER2_LATCH 0x20
+#define TIMER2_SPEAKER 0x02
+#define TIMER2_GATE 0x01
+
+void
+grub_pit_wait (grub_uint16_t tics)
+{
+ /* Disable timer2 gate and speaker. */
+ grub_outb (grub_inb (TIMER2_REG_LATCH) & ~ (TIMER2_SPEAKER | TIMER2_GATE),
+ TIMER2_REG_LATCH);
+
+ /* Set tics. */
+ grub_outb (TIMER2_SELECT | TIMER_ENABLE_LSB | TIMER_ENABLE_MSB, TIMER_REG_COMMAND);
+ grub_outb (tics & 0xff, TIMER2_REG_CONTROL);
+ grub_outb (tics >> 8, TIMER2_REG_CONTROL);
+
+ /* Enable timer2 gate, keep speaker disabled. */
+ grub_outb ((grub_inb (TIMER2_REG_LATCH) & ~ TIMER2_SPEAKER) | TIMER2_GATE,
+ TIMER2_REG_LATCH);
+
+ /* Wait. */
+ while ((grub_inb (TIMER2_REG_LATCH) & TIMER2_LATCH) == 0x00);
+
+ /* Disable timer2 gate and speaker. */
+ grub_outb (grub_inb (TIMER2_REG_LATCH) & ~ (TIMER2_SPEAKER | TIMER2_GATE),
+ TIMER2_REG_LATCH);
+}
diff --git a/grub-core/kern/i386/qemu/init.c b/grub-core/kern/i386/qemu/init.c
new file mode 100644
index 0000000..054dfa8
--- /dev/null
+++ b/grub-core/kern/i386/qemu/init.c
@@ -0,0 +1,147 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2010 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/pci.h>
+#include <grub/machine/kernel.h>
+#include <grub/misc.h>
+#include <grub/vga.h>
+
+static struct {grub_uint8_t r, g, b, a; } colors[] =
+ {
+ // {R, G, B, A}
+ {0x00, 0x00, 0x00, 0xFF}, // 0 = black
+ {0x00, 0x00, 0xA8, 0xFF}, // 1 = blue
+ {0x00, 0xA8, 0x00, 0xFF}, // 2 = green
+ {0x00, 0xA8, 0xA8, 0xFF}, // 3 = cyan
+ {0xA8, 0x00, 0x00, 0xFF}, // 4 = red
+ {0xA8, 0x00, 0xA8, 0xFF}, // 5 = magenta
+ {0xA8, 0x54, 0x00, 0xFF}, // 6 = brown
+ {0xA8, 0xA8, 0xA8, 0xFF}, // 7 = light gray
+
+ {0x54, 0x54, 0x54, 0xFF}, // 8 = dark gray
+ {0x54, 0x54, 0xFE, 0xFF}, // 9 = bright blue
+ {0x54, 0xFE, 0x54, 0xFF}, // 10 = bright green
+ {0x54, 0xFE, 0xFE, 0xFF}, // 11 = bright cyan
+ {0xFE, 0x54, 0x54, 0xFF}, // 12 = bright red
+ {0xFE, 0x54, 0xFE, 0xFF}, // 13 = bright magenta
+ {0xFE, 0xFE, 0x54, 0xFF}, // 14 = yellow
+ {0xFE, 0xFE, 0xFE, 0xFF} // 15 = white
+ };
+
+#include <ascii.h>
+
+static void
+load_font (void)
+{
+ unsigned i;
+
+ grub_vga_gr_write (0 << 2, GRUB_VGA_GR_GR6);
+
+ grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_NORMAL, GRUB_VGA_SR_MEMORY_MODE);
+ grub_vga_sr_write (1 << GRUB_VGA_TEXT_FONT_PLANE,
+ GRUB_VGA_SR_MAP_MASK_REGISTER);
+
+ grub_vga_gr_write (0, GRUB_VGA_GR_DATA_ROTATE);
+ grub_vga_gr_write (0, GRUB_VGA_GR_MODE);
+ grub_vga_gr_write (0xff, GRUB_VGA_GR_BITMASK);
+
+ for (i = 0; i < 128; i++)
+ grub_memcpy ((void *) (0xa0000 + 32 * i), ascii_bitmaps + 16 * i, 16);
+}
+
+static void
+load_palette (void)
+{
+ unsigned i;
+ for (i = 0; i < 16; i++)
+ grub_vga_write_arx (i, i);
+
+ for (i = 0; i < ARRAY_SIZE (colors); i++)
+ grub_vga_palette_write (i, colors[i].r, colors[i].g, colors[i].b);
+}
+
+void
+grub_qemu_init_cirrus (void)
+{
+ auto int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev, grub_pci_id_t pciid);
+ int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev, grub_pci_id_t pciid __attribute__ ((unused)))
+ {
+ grub_pci_address_t addr;
+ grub_uint32_t class;
+
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
+ class = grub_pci_read (addr);
+
+ if (((class >> 16) & 0xffff) != GRUB_PCI_CLASS_SUBCLASS_VGA)
+ return 0;
+
+ /* FIXME: chooose addresses dynamically. */
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
+ grub_pci_write (addr, 0xf0000000 | GRUB_PCI_ADDR_MEM_PREFETCH
+ | GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32);
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG1);
+ grub_pci_write (addr, 0xf2000000
+ | GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32);
+
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
+ grub_pci_write (addr, GRUB_PCI_COMMAND_MEM_ENABLED
+ | GRUB_PCI_COMMAND_IO_ENABLED);
+
+ return 1;
+ }
+
+ grub_pci_iterate (find_card);
+
+ grub_outb (GRUB_VGA_IO_MISC_COLOR, GRUB_VGA_IO_MISC_WRITE);
+
+ load_font ();
+
+ grub_vga_gr_write (GRUB_VGA_GR_GR6_MMAP_CGA, GRUB_VGA_GR_GR6);
+ grub_vga_gr_write (GRUB_VGA_GR_MODE_ODD_EVEN, GRUB_VGA_GR_MODE);
+
+ grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_NORMAL, GRUB_VGA_SR_MEMORY_MODE);
+
+ grub_vga_sr_write ((1 << GRUB_VGA_TEXT_TEXT_PLANE)
+ | (1 << GRUB_VGA_TEXT_ATTR_PLANE),
+ GRUB_VGA_SR_MAP_MASK_REGISTER);
+
+ grub_vga_cr_write (15, GRUB_VGA_CR_CELL_HEIGHT);
+ grub_vga_cr_write (79, GRUB_VGA_CR_HORIZ_END);
+ grub_vga_cr_write (40, GRUB_VGA_CR_PITCH);
+
+ int vert = 25 * 16;
+ grub_vga_cr_write (vert & 0xff, GRUB_VGA_CR_VDISPLAY_END);
+ grub_vga_cr_write (((vert >> GRUB_VGA_CR_OVERFLOW_HEIGHT1_SHIFT)
+ & GRUB_VGA_CR_OVERFLOW_HEIGHT1_MASK)
+ | ((vert >> GRUB_VGA_CR_OVERFLOW_HEIGHT2_SHIFT)
+ & GRUB_VGA_CR_OVERFLOW_HEIGHT2_MASK),
+ GRUB_VGA_CR_OVERFLOW);
+
+ load_palette ();
+
+ grub_vga_write_arx (GRUB_VGA_ARX_MODE_TEXT, GRUB_VGA_ARX_MODE);
+ grub_vga_write_arx (0, GRUB_VGA_ARX_COLOR_SELECT);
+
+ grub_vga_sr_write (GRUB_VGA_SR_CLOCKING_MODE_8_DOT_CLOCK,
+ GRUB_VGA_SR_CLOCKING_MODE);
+
+ grub_vga_cr_write (14, GRUB_VGA_CR_CURSOR_START);
+ grub_vga_cr_write (15, GRUB_VGA_CR_CURSOR_END);
+
+ grub_outb (0x20, 0x3c0);
+}
diff --git a/grub-core/kern/i386/qemu/mmap.c b/grub-core/kern/i386/qemu/mmap.c
new file mode 100644
index 0000000..208f36b
--- /dev/null
+++ b/grub-core/kern/i386/qemu/mmap.c
@@ -0,0 +1,100 @@
+/*
+ * 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/i386/memory.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/boot.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/cmos.h>
+#include <grub/offsets.h>
+
+#define QEMU_CMOS_MEMSIZE_HIGH 0x35
+#define QEMU_CMOS_MEMSIZE_LOW 0x34
+
+#define QEMU_CMOS_MEMSIZE2_HIGH 0x31
+#define QEMU_CMOS_MEMSIZE2_LOW 0x30
+
+#define min(a,b) ((a) > (b) ? (b) : (a))
+
+extern char _start[];
+extern char _end[];
+
+static grub_uint64_t mem_size, above_4g;
+
+void
+grub_machine_mmap_init ()
+{
+ mem_size = ((grub_uint64_t) grub_cmos_read (QEMU_CMOS_MEMSIZE_HIGH)) << 24
+ | ((grub_uint64_t) grub_cmos_read (QEMU_CMOS_MEMSIZE_LOW)) << 16;
+ if (mem_size > 0)
+ {
+ /* Don't ask... */
+ mem_size += (16 * 1024 * 1024);
+ }
+ else
+ {
+ mem_size
+ = ((((grub_uint64_t) grub_cmos_read (QEMU_CMOS_MEMSIZE2_HIGH)) << 18)
+ | ((grub_uint64_t) (grub_cmos_read (QEMU_CMOS_MEMSIZE2_LOW)) << 10))
+ + 1024 * 1024;
+ }
+
+ above_4g = (((grub_uint64_t) grub_cmos_read (0x5b)) << 16)
+ | (((grub_uint64_t) grub_cmos_read (0x5c)) << 24)
+ | (((grub_uint64_t) grub_cmos_read (0x5d)) << 32);
+}
+
+grub_err_t
+grub_machine_mmap_iterate (grub_memory_hook_t hook)
+{
+ if (hook (0x0,
+ (grub_addr_t) _start,
+ GRUB_MEMORY_AVAILABLE))
+ return 1;
+
+ if (hook ((grub_addr_t) _end,
+ 0xa0000 - (grub_addr_t) _end,
+ GRUB_MEMORY_AVAILABLE))
+ return 1;
+
+ if (hook (GRUB_MEMORY_MACHINE_UPPER,
+ 0x100000 - GRUB_MEMORY_MACHINE_UPPER,
+ GRUB_MEMORY_RESERVED))
+ return 1;
+
+ /* Everything else is free. */
+ if (hook (0x100000,
+ min (mem_size, (grub_uint32_t) -GRUB_BOOT_MACHINE_SIZE) - 0x100000,
+ GRUB_MEMORY_AVAILABLE))
+ return 1;
+
+ /* Protect boot.img, which contains the gdt. It is mapped at the top of memory
+ (it is also mapped below 0x100000, but we already reserved that area). */
+ if (hook ((grub_uint32_t) -GRUB_BOOT_MACHINE_SIZE,
+ GRUB_BOOT_MACHINE_SIZE,
+ GRUB_MEMORY_RESERVED))
+ return 1;
+
+ if (above_4g != 0 && hook (0x100000000ULL, above_4g,
+ GRUB_MEMORY_AVAILABLE))
+ return 1;
+
+ return 0;
+}
diff --git a/grub-core/kern/i386/qemu/startup.S b/grub-core/kern/i386/qemu/startup.S
new file mode 100644
index 0000000..7834d1d
--- /dev/null
+++ b/grub-core/kern/i386/qemu/startup.S
@@ -0,0 +1,104 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <grub/symbol.h>
+
+#include <grub/i386/pc/memory.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/kernel.h>
+
+ .text
+ .code32
+ .globl _start
+_start:
+ jmp codestart
+
+ . = _start + GRUB_KERNEL_I386_QEMU_CORE_ENTRY_ADDR
+VARIABLE(grub_core_entry_addr)
+ .long 0
+VARIABLE(grub_kernel_image_size)
+ .long 0
+VARIABLE(grub_prefix)
+ /* to be filled by grub-mkimage */
+
+ /*
+ * Leave some breathing room for the prefix.
+ */
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX_END
+
+codestart:
+ /* Relocate to low memory. First we figure out our location.
+ We will derive the rom start address from it. */
+ call 1f
+1: popl %esi
+
+ /* Rom size is a multiple of 64 kiB. With this we get the
+ value of `grub_core_entry_addr' in %esi. */
+ xorw %si, %si
+
+ /* ... which allows us to access `grub_kernel_image_size'
+ before relocation. */
+ movl (grub_kernel_image_size - _start)(%esi), %ecx
+
+
+ movl $_start, %edi
+ cld
+ rep
+ movsb
+ ljmp $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $1f
+1:
+
+#ifdef APPLE_CC
+ /* clean out the bss */
+ bss_start_abs = ABS (bss_start)
+ bss_end_abs = ABS (bss_end)
+
+ movl bss_start_abs, %edi
+
+ /* compute the bss length */
+ movl bss_end_abs, %ecx
+ subl %edi, %ecx
+#else
+ /* clean out the bss */
+ movl $BSS_START_SYMBOL, %edi
+
+ /* compute the bss length */
+ movl $END_SYMBOL, %ecx
+ subl %edi, %ecx
+#endif
+
+ /* clean out */
+ xorl %eax, %eax
+ cld
+ rep
+ stosb
+
+ /*
+ * Call the start of main body of C code.
+ */
+ call EXT_C(grub_main)
+
+ /* This should never happen. */
+ cli
+1:
+ hlt
+ jmp 1b
+
+#include "../realmode.S"
diff --git a/grub-core/kern/i386/realmode.S b/grub-core/kern/i386/realmode.S
new file mode 100644
index 0000000..578c8d2
--- /dev/null
+++ b/grub-core/kern/i386/realmode.S
@@ -0,0 +1,242 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,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/i386/pc/memory.h>
+
+/*
+ * Note: These functions defined in this file may be called from C.
+ * Be careful of that you must not modify some registers. Quote
+ * from gcc-2.95.2/gcc/config/i386/i386.h:
+
+ 1 for registers not available across function calls.
+ These must include the FIXED_REGISTERS and also any
+ registers that can be used without being saved.
+ The latter must include the registers where values are returned
+ and the register where structure-value addresses are passed.
+ Aside from that, you can include as many other registers as you like.
+
+ ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
+{ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
+ */
+
+/*
+ * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
+ * So the first three arguments are passed in %eax, %edx, and %ecx,
+ * respectively, and if a function has a fixed number of arguments
+ * and the number if greater than three, the function must return
+ * with "ret $N" where N is ((the number of arguments) - 3) * 4.
+ */
+
+/*
+ * This is the area for all of the special variables.
+ */
+
+ .p2align 2 /* force 4-byte alignment */
+
+protstack:
+ .long GRUB_MEMORY_MACHINE_PROT_STACK
+
+/*
+ * This is the Global Descriptor Table
+ *
+ * An entry, a "Segment Descriptor", looks like this:
+ *
+ * 31 24 19 16 7 0
+ * ------------------------------------------------------------
+ * | | |B| |A| | | |1|0|E|W|A| |
+ * | BASE 31..24 |G|/|L|V| LIMIT |P|DPL| TYPE | BASE 23:16 | 4
+ * | | |D| |L| 19..16| | |1|1|C|R|A| |
+ * ------------------------------------------------------------
+ * | | |
+ * | BASE 15..0 | LIMIT 15..0 | 0
+ * | | |
+ * ------------------------------------------------------------
+ *
+ * Note the ordering of the data items is reversed from the above
+ * description.
+ */
+
+ .p2align 2 /* force 4-byte alignment */
+gdt:
+ .word 0, 0
+ .byte 0, 0, 0, 0
+
+ /* -- code segment --
+ * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present
+ * type = 32bit code execute/read, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x9A, 0xCF, 0
+
+ /* -- data segment --
+ * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present
+ * type = 32 bit data read/write, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x92, 0xCF, 0
+
+ /* -- 16 bit real mode CS --
+ * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
+ * type = 16 bit code execute/read only/conforming, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x9E, 0, 0
+
+ /* -- 16 bit real mode DS --
+ * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present
+ * type = 16 bit data read/write, DPL = 0
+ */
+ .word 0xFFFF, 0
+ .byte 0, 0x92, 0, 0
+
+
+/* this is the GDT descriptor */
+gdtdesc:
+ .word 0x27 /* limit */
+ .long gdt /* addr */
+
+/*
+ * These next two routines, "real_to_prot" and "prot_to_real" are structured
+ * in a very specific way. Be very careful when changing them.
+ *
+ * NOTE: Use of either one messes up %eax and %ebp.
+ */
+
+real_to_prot:
+ .code16
+ cli
+
+ /* load the GDT register */
+ xorw %ax, %ax
+ movw %ax, %ds
+ DATA32 ADDR32 lgdt gdtdesc
+
+ /* turn on protected mode */
+ movl %cr0, %eax
+ orl $GRUB_MEMORY_CPU_CR0_PE_ON, %eax
+ movl %eax, %cr0
+
+ /* jump to relocation, flush prefetch queue, and reload %cs */
+ DATA32 ljmp $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg
+
+ .code32
+protcseg:
+ /* reload other segment registers */
+ movw $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /* put the return address in a known safe location */
+ movl (%esp), %eax
+ movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK
+
+ /* get protected mode stack */
+ movl protstack, %eax
+ movl %eax, %esp
+ movl %eax, %ebp
+
+ /* get return address onto the right stack */
+ movl GRUB_MEMORY_MACHINE_REAL_STACK, %eax
+ movl %eax, (%esp)
+
+ /* zero %eax */
+ xorl %eax, %eax
+
+ /* return on the old (or initialized) stack! */
+ ret
+
+prot_to_real:
+ /* just in case, set GDT */
+ lgdt gdtdesc
+
+ /* save the protected mode stack */
+ movl %esp, %eax
+ movl %eax, protstack
+
+ /* get the return address */
+ movl (%esp), %eax
+ movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK
+
+ /* set up new stack */
+ movl $GRUB_MEMORY_MACHINE_REAL_STACK, %eax
+ movl %eax, %esp
+ movl %eax, %ebp
+
+ /* set up segment limits */
+ movw $GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /* this might be an extra step */
+ /* jump to a 16 bit segment */
+ ljmp $GRUB_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcseg
+
+tmpcseg:
+ .code16
+
+ /* clear the PE bit of CR0 */
+ movl %cr0, %eax
+ andl $(~GRUB_MEMORY_CPU_CR0_PE_ON), %eax
+ movl %eax, %cr0
+
+ /* flush prefetch queue, reload %cs */
+ DATA32 ljmp $0, $realcseg
+
+realcseg:
+ /* we are in real mode now
+ * set up the real mode segment registers : DS, SS, ES
+ */
+ /* zero %eax */
+ xorl %eax, %eax
+
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+#ifdef GRUB_MACHINE_PCBIOS
+ /* restore interrupts */
+ sti
+#endif
+
+ /* return on new stack! */
+ DATA32 ret
+
+ .code32
+
+/*
+ * grub_reboot()
+ *
+ * Reboot the system. At the moment, rely on BIOS.
+ */
+FUNCTION(grub_reboot)
+ call prot_to_real
+ .code16
+cold_reboot:
+ /* set 0x472 to 0x0000 for cold boot (0x1234 for warm boot) */
+ movw $0x0472, %di
+ movw %ax, (%di)
+ ljmp $0xf000, $0xfff0
+ .code32
diff --git a/grub-core/kern/i386/tsc.c b/grub-core/kern/i386/tsc.c
new file mode 100644
index 0000000..36b35e2
--- /dev/null
+++ b/grub-core/kern/i386/tsc.c
@@ -0,0 +1,74 @@
+/* kern/i386/tsc.c - x86 TSC time source implementation
+ * Requires Pentium or better x86 CPU that supports the RDTSC instruction.
+ * This module uses the RTC (via grub_get_rtc()) to calibrate the TSC to
+ * real time.
+ *
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/>.
+ */
+
+#include <grub/types.h>
+#include <grub/time.h>
+#include <grub/misc.h>
+#include <grub/i386/tsc.h>
+#include <grub/i386/pit.h>
+
+/* This defines the value TSC had at the epoch (that is, when we calibrated it). */
+static grub_uint64_t tsc_boot_time;
+
+/* Calibrated TSC rate. (In TSC ticks per millisecond.) */
+static grub_uint64_t tsc_ticks_per_ms;
+
+
+grub_uint64_t
+grub_tsc_get_time_ms (void)
+{
+ return tsc_boot_time + grub_divmod64 (grub_get_tsc (), tsc_ticks_per_ms, 0);
+}
+
+
+/* How many RTC ticks to use for calibration loop. (>= 1) */
+#define CALIBRATION_TICKS 2
+
+/* Calibrate the TSC based on the RTC. */
+static void
+calibrate_tsc (void)
+{
+ /* First calibrate the TSC rate (relative, not absolute time). */
+ grub_uint64_t start_tsc;
+ grub_uint64_t end_tsc;
+
+ 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);
+}
+
+void
+grub_tsc_init (void)
+{
+ if (grub_cpu_is_tsc_supported ())
+ {
+ tsc_boot_time = grub_get_tsc ();
+ calibrate_tsc ();
+ grub_install_get_time_ms (grub_tsc_get_time_ms);
+ }
+ else
+ {
+ grub_install_get_time_ms (grub_rtc_get_time_ms);
+ }
+}
diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c
new file mode 100644
index 0000000..2fbe809
--- /dev/null
+++ b/grub-core/kern/ieee1275/cmain.c
@@ -0,0 +1,182 @@
+/* cmain.c - Startup code for the PowerPC. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,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/>.
+ */
+
+#include <grub/kernel.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/ieee1275/ieee1275.h>
+
+int (*grub_ieee1275_entry_fn) (void *);
+
+grub_ieee1275_phandle_t grub_ieee1275_chosen;
+grub_ieee1275_ihandle_t grub_ieee1275_mmu;
+
+static grub_uint32_t grub_ieee1275_flags;
+
+
+
+int
+grub_ieee1275_test_flag (enum grub_ieee1275_flag flag)
+{
+ return (grub_ieee1275_flags & (1 << flag));
+}
+
+void
+grub_ieee1275_set_flag (enum grub_ieee1275_flag flag)
+{
+ grub_ieee1275_flags |= (1 << flag);
+}
+
+#define SF "SmartFirmware(tm)"
+#define OHW "PPC Open Hack'Ware"
+
+static void
+grub_ieee1275_find_options (void)
+{
+ grub_ieee1275_phandle_t root;
+ grub_ieee1275_phandle_t options;
+ grub_ieee1275_phandle_t openprom;
+ grub_ieee1275_phandle_t bootrom;
+ int rc;
+ grub_uint32_t realmode = 0;
+ char tmp[32];
+ int is_smartfirmware = 0;
+ int is_olpc = 0;
+ int is_qemu = 0;
+
+ grub_ieee1275_finddevice ("/", &root);
+ grub_ieee1275_finddevice ("/options", &options);
+ grub_ieee1275_finddevice ("/openprom", &openprom);
+
+ rc = grub_ieee1275_get_integer_property (options, "real-mode?", &realmode,
+ sizeof realmode, 0);
+ if (((rc >= 0) && realmode) || (grub_ieee1275_mmu == 0))
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_REAL_MODE);
+
+ rc = grub_ieee1275_get_property (openprom, "CodeGen-copyright",
+ tmp, sizeof (tmp), 0);
+ if (rc >= 0 && !grub_strncmp (tmp, SF, sizeof (SF) - 1))
+ is_smartfirmware = 1;
+
+ rc = grub_ieee1275_get_property (root, "architecture",
+ tmp, sizeof (tmp), 0);
+ if (rc >= 0 && !grub_strcmp (tmp, "OLPC"))
+ is_olpc = 1;
+
+ rc = grub_ieee1275_get_property (root, "model",
+ tmp, sizeof (tmp), 0);
+ if (rc >= 0 && !grub_strcmp (tmp, "Emulated PC"))
+ is_qemu = 1;
+
+ if (grub_strncmp (tmp, "PowerMac", sizeof ("PowerMac") - 1) == 0)
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS);
+
+ if (is_smartfirmware)
+ {
+ /* Broken in all versions */
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT);
+
+ /* There are two incompatible ways of checking the version number. Try
+ both. */
+ rc = grub_ieee1275_get_property (openprom, "SmartFirmware-version",
+ tmp, sizeof (tmp), 0);
+ if (rc < 0)
+ rc = grub_ieee1275_get_property (openprom, "firmware-version",
+ tmp, sizeof (tmp), 0);
+ if (rc >= 0)
+ {
+ /* It is tempting to implement a version parser to set the flags for
+ e.g. 1.3 and below. However, there's a special situation here.
+ 3rd party updates which fix the partition bugs are common, and for
+ some reason their fixes aren't being merged into trunk. So for
+ example we know that 1.2 and 1.3 are broken, but there's 1.2.99
+ and 1.3.99 which are known good (and applying this workaround
+ would cause breakage). */
+ if (!grub_strcmp (tmp, "1.0")
+ || !grub_strcmp (tmp, "1.1")
+ || !grub_strcmp (tmp, "1.2")
+ || !grub_strcmp (tmp, "1.3"))
+ {
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0);
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS);
+ }
+ }
+ }
+
+ if (is_olpc)
+ {
+ /* OLPC / XO laptops have three kinds of storage devices:
+
+ - NAND flash. These are accessible via OFW callbacks, but:
+ - Follow strange semantics, imposed by hardware constraints.
+ - Its ABI is undocumented, and not stable.
+ They lack "device_type" property, which conveniently makes GRUB
+ skip them.
+
+ - USB drives. Not accessible, because OFW shuts down the controller
+ in order to prevent collisions with applications accessing it
+ directly. Even worse, attempts to access it will NOT return
+ control to the caller, so we have to avoid probing them.
+
+ - SD cards. These work fine.
+
+ To avoid breakage, we only need to skip USB probing. However,
+ since detecting SD cards is more reliable, we do that instead.
+ */
+
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_OFDISK_SDCARD_ONLY);
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF);
+ }
+
+ if (is_qemu)
+ {
+ /* OpenFirmware hangs on qemu if one requests any memory below 1.5 MiB. */
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM);
+
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF);
+ }
+
+ if (! grub_ieee1275_finddevice ("/rom/boot-rom", &bootrom))
+ {
+ rc = grub_ieee1275_get_property (bootrom, "model", tmp, sizeof (tmp), 0);
+ if (rc >= 0 && !grub_strncmp (tmp, OHW, sizeof (OHW) - 1))
+ {
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT);
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_SET_COLORS);
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET);
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM);
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_ANSI);
+ }
+ }
+}
+
+#undef SF
+#undef OHW
+
+void
+grub_ieee1275_init (void)
+{
+ grub_ieee1275_finddevice ("/chosen", &grub_ieee1275_chosen);
+
+ if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, "mmu", &grub_ieee1275_mmu,
+ sizeof grub_ieee1275_mmu, 0) < 0)
+ grub_ieee1275_mmu = 0;
+
+ grub_ieee1275_find_options ();
+}
diff --git a/grub-core/kern/ieee1275/ieee1275.c b/grub-core/kern/ieee1275/ieee1275.c
new file mode 100644
index 0000000..9e29191
--- /dev/null
+++ b/grub-core/kern/ieee1275/ieee1275.c
@@ -0,0 +1,609 @@
+/* of.c - Access the Open Firmware client interface. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/ieee1275/ieee1275.h>
+#include <grub/types.h>
+
+#define IEEE1275_PHANDLE_INVALID ((grub_ieee1275_cell_t) -1)
+#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_cell_t) 0)
+#define IEEE1275_CELL_INVALID ((grub_ieee1275_cell_t) -1)
+
+
+
+int
+grub_ieee1275_finddevice (char *name, grub_ieee1275_phandle_t *phandlep)
+{
+ struct find_device_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t device;
+ grub_ieee1275_cell_t phandle;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "finddevice", 1, 1);
+ args.device = (grub_ieee1275_cell_t) name;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ *phandlep = args.phandle;
+ if (args.phandle == IEEE1275_PHANDLE_INVALID)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_get_property (grub_ieee1275_phandle_t phandle,
+ const char *property, void *buf,
+ grub_size_t size, grub_ssize_t *actual)
+{
+ struct get_property_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t phandle;
+ grub_ieee1275_cell_t prop;
+ grub_ieee1275_cell_t buf;
+ grub_ieee1275_cell_t buflen;
+ grub_ieee1275_cell_t size;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "getprop", 4, 1);
+ args.phandle = phandle;
+ args.prop = (grub_ieee1275_cell_t) property;
+ args.buf = (grub_ieee1275_cell_t) buf;
+ args.buflen = (grub_ieee1275_cell_t) size;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ if (actual)
+ *actual = (grub_ssize_t) args.size;
+ if (args.size == IEEE1275_CELL_INVALID)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_get_integer_property (grub_ieee1275_phandle_t phandle,
+ const char *property, grub_uint32_t *buf,
+ grub_size_t size, grub_ssize_t *actual)
+{
+ int ret;
+ ret = grub_ieee1275_get_property (phandle, property, (void *) buf, size, actual);
+#ifndef GRUB_CPU_WORDS_BIGENDIAN
+ /* Integer properties are always in big endian. */
+ if (ret == 0)
+ {
+ unsigned int i;
+ size /= sizeof (grub_uint32_t);
+ for (i = 0; i < size; i++)
+ buf[i] = grub_be_to_cpu32 (buf[i]);
+ }
+#endif
+ return ret;
+}
+
+int
+grub_ieee1275_next_property (grub_ieee1275_phandle_t phandle, char *prev_prop,
+ char *prop)
+{
+ struct get_property_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t phandle;
+ grub_ieee1275_cell_t prev_prop;
+ grub_ieee1275_cell_t next_prop;
+ grub_ieee1275_cell_t flags;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "nextprop", 3, 1);
+ args.phandle = phandle;
+ args.prev_prop = (grub_ieee1275_cell_t) prev_prop;
+ args.next_prop = (grub_ieee1275_cell_t) prop;
+ args.flags = (grub_ieee1275_cell_t) -1;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ return (int) args.flags;
+}
+
+int
+grub_ieee1275_get_property_length (grub_ieee1275_phandle_t phandle,
+ const char *prop, grub_ssize_t *length)
+{
+ struct get_property_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t phandle;
+ grub_ieee1275_cell_t prop;
+ grub_ieee1275_cell_t length;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "getproplen", 2, 1);
+ args.phandle = phandle;
+ args.prop = (grub_ieee1275_cell_t) prop;
+ args.length = (grub_ieee1275_cell_t) -1;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ *length = args.length;
+ if (args.length == IEEE1275_CELL_INVALID)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_instance_to_package (grub_ieee1275_ihandle_t ihandle,
+ grub_ieee1275_phandle_t *phandlep)
+{
+ struct instance_to_package_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t phandle;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "instance-to-package", 1, 1);
+ args.ihandle = ihandle;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ *phandlep = args.phandle;
+ if (args.phandle == IEEE1275_PHANDLE_INVALID)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_package_to_path (grub_ieee1275_phandle_t phandle,
+ char *path, grub_size_t len,
+ grub_ssize_t *actual)
+{
+ struct instance_to_package_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t phandle;
+ grub_ieee1275_cell_t buf;
+ grub_ieee1275_cell_t buflen;
+ grub_ieee1275_cell_t actual;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "package-to-path", 3, 1);
+ args.phandle = phandle;
+ args.buf = (grub_ieee1275_cell_t) path;
+ args.buflen = (grub_ieee1275_cell_t) len;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ if (actual)
+ *actual = args.actual;
+ if (args.actual == IEEE1275_CELL_INVALID)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_instance_to_path (grub_ieee1275_ihandle_t ihandle,
+ char *path, grub_size_t len,
+ grub_ssize_t *actual)
+{
+ struct instance_to_path_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t buf;
+ grub_ieee1275_cell_t buflen;
+ grub_ieee1275_cell_t actual;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "instance-to-path", 3, 1);
+ args.ihandle = ihandle;
+ args.buf = (grub_ieee1275_cell_t) path;
+ args.buflen = (grub_ieee1275_cell_t) len;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ if (actual)
+ *actual = args.actual;
+ if (args.actual == IEEE1275_CELL_INVALID)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_write (grub_ieee1275_ihandle_t ihandle, void *buffer,
+ grub_size_t len, grub_ssize_t *actualp)
+{
+ struct write_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t buf;
+ grub_ieee1275_cell_t len;
+ grub_ieee1275_cell_t actual;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "write", 3, 1);
+ args.ihandle = ihandle;
+ args.buf = (grub_ieee1275_cell_t) buffer;
+ args.len = (grub_ieee1275_cell_t) len;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ if (actualp)
+ *actualp = args.actual;
+ return 0;
+}
+
+int
+grub_ieee1275_read (grub_ieee1275_ihandle_t ihandle, void *buffer,
+ grub_size_t len, grub_ssize_t *actualp)
+{
+ struct write_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t buf;
+ grub_ieee1275_cell_t len;
+ grub_ieee1275_cell_t actual;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "read", 3, 1);
+ args.ihandle = ihandle;
+ args.buf = (grub_ieee1275_cell_t) buffer;
+ args.len = (grub_ieee1275_cell_t) len;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ if (actualp)
+ *actualp = args.actual;
+ return 0;
+}
+
+int
+grub_ieee1275_seek (grub_ieee1275_ihandle_t ihandle, grub_disk_addr_t pos,
+ grub_ssize_t *result)
+{
+ struct write_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t pos_hi;
+ grub_ieee1275_cell_t pos_lo;
+ grub_ieee1275_cell_t result;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "seek", 3, 1);
+ args.ihandle = ihandle;
+ /* To prevent stupid gcc warning. */
+#if GRUB_IEEE1275_CELL_SIZEOF >= 8
+ args.pos_hi = 0;
+ args.pos_lo = pos;
+#else
+ args.pos_hi = (grub_ieee1275_cell_t) (pos >> (8 * GRUB_IEEE1275_CELL_SIZEOF));
+ args.pos_lo = (grub_ieee1275_cell_t)
+ (pos & ((1ULL << (8 * GRUB_IEEE1275_CELL_SIZEOF)) - 1));
+#endif
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+
+ if (result)
+ *result = args.result;
+ return 0;
+}
+
+int
+grub_ieee1275_peer (grub_ieee1275_phandle_t node,
+ grub_ieee1275_phandle_t *result)
+{
+ struct peer_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t node;
+ grub_ieee1275_cell_t result;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "peer", 1, 1);
+ args.node = node;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ *result = args.result;
+ if (args.result == 0)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_child (grub_ieee1275_phandle_t node,
+ grub_ieee1275_phandle_t *result)
+{
+ struct child_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t node;
+ grub_ieee1275_cell_t result;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "child", 1, 1);
+ args.node = node;
+ args.result = IEEE1275_PHANDLE_INVALID;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ *result = args.result;
+ if (args.result == 0)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_parent (grub_ieee1275_phandle_t node,
+ grub_ieee1275_phandle_t *result)
+{
+ struct parent_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t node;
+ grub_ieee1275_cell_t result;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "parent", 1, 1);
+ args.node = node;
+ args.result = IEEE1275_PHANDLE_INVALID;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ *result = args.result;
+ return 0;
+}
+
+int
+grub_ieee1275_interpret (const char *command, grub_ieee1275_cell_t *catch)
+{
+ struct enter_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t command;
+ grub_ieee1275_cell_t catch;
+ }
+ args;
+
+ if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET))
+ return -1;
+
+ INIT_IEEE1275_COMMON (&args.common, "interpret", 1, 1);
+ args.command = (grub_ieee1275_cell_t) command;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ if (catch)
+ *catch = args.catch;
+ return 0;
+}
+
+int
+grub_ieee1275_enter (void)
+{
+ struct enter_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "enter", 0, 0);
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ return 0;
+}
+
+void
+grub_ieee1275_exit (void)
+{
+ struct exit_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "exit", 0, 0);
+
+ IEEE1275_CALL_ENTRY_FN (&args);
+ for (;;) ;
+}
+
+int
+grub_ieee1275_open (const char *path, grub_ieee1275_ihandle_t *result)
+{
+ struct open_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t path;
+ grub_ieee1275_cell_t result;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "open", 1, 1);
+ args.path = (grub_ieee1275_cell_t) path;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ *result = args.result;
+ if (args.result == IEEE1275_IHANDLE_INVALID)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_close (grub_ieee1275_ihandle_t ihandle)
+{
+ struct close_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t ihandle;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "close", 1, 0);
+ args.ihandle = ihandle;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+
+ return 0;
+}
+
+int
+grub_ieee1275_claim (grub_addr_t addr, grub_size_t size, unsigned int align,
+ grub_addr_t *result)
+{
+ struct claim_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t addr;
+ grub_ieee1275_cell_t size;
+ grub_ieee1275_cell_t align;
+ grub_ieee1275_cell_t base;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "claim", 3, 1);
+ args.addr = (grub_ieee1275_cell_t) addr;
+ args.size = (grub_ieee1275_cell_t) size;
+ args.align = (grub_ieee1275_cell_t) align;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ if (result)
+ *result = args.base;
+ if (args.base == IEEE1275_CELL_INVALID)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_release (grub_addr_t addr, grub_size_t size)
+{
+ struct release_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t addr;
+ grub_ieee1275_cell_t size;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "release", 2, 0);
+ args.addr = addr;
+ args.size = size;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_set_property (grub_ieee1275_phandle_t phandle,
+ const char *propname, void *buf,
+ grub_size_t size, grub_ssize_t *actual)
+{
+ struct set_property_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t phandle;
+ grub_ieee1275_cell_t propname;
+ grub_ieee1275_cell_t buf;
+ grub_ieee1275_cell_t size;
+ grub_ieee1275_cell_t actual;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "setprop", 4, 1);
+ args.size = (grub_ieee1275_cell_t) size;
+ args.buf = (grub_ieee1275_cell_t) buf;
+ args.propname = (grub_ieee1275_cell_t) propname;
+ args.phandle = (grub_ieee1275_cell_t) phandle;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ *actual = args.actual;
+ if ((args.actual == IEEE1275_CELL_INVALID) || (args.actual != args.size))
+ return -1;
+ return 0;
+}
+
+int
+grub_ieee1275_set_color (grub_ieee1275_ihandle_t ihandle,
+ int index, int r, int g, int b)
+{
+ struct set_color_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t method;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t index;
+ grub_ieee1275_cell_t b;
+ grub_ieee1275_cell_t g;
+ grub_ieee1275_cell_t r;
+ grub_ieee1275_cell_t catch_result;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "call-method", 6, 1);
+ args.method = (grub_ieee1275_cell_t) "color!";
+ args.ihandle = ihandle;
+ args.index = index;
+ args.r = r;
+ args.g = g;
+ args.b = b;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ return args.catch_result;
+}
+
+int
+grub_ieee1275_milliseconds (grub_uint32_t *msecs)
+{
+ struct milliseconds_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t msecs;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "milliseconds", 0, 1);
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ *msecs = args.msecs;
+ return 0;
+}
diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
new file mode 100644
index 0000000..682a8b5
--- /dev/null
+++ b/grub-core/kern/ieee1275/init.c
@@ -0,0 +1,274 @@
+/* init.c -- Initialize GRUB on the newworld mac (PPC). */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/kernel.h>
+#include <grub/dl.h>
+#include <grub/disk.h>
+#include <grub/mm.h>
+#include <grub/partition.h>
+#include <grub/normal.h>
+#include <grub/fs.h>
+#include <grub/setjmp.h>
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/time.h>
+#include <grub/ieee1275/console.h>
+#include <grub/ieee1275/ofdisk.h>
+#include <grub/ieee1275/ieee1275.h>
+#include <grub/offsets.h>
+#include <grub/memory.h>
+
+/* The minimal heap size we can live with. */
+#define HEAP_MIN_SIZE (unsigned long) (2 * 1024 * 1024)
+
+/* The maximum heap size we're going to claim */
+#define HEAP_MAX_SIZE (unsigned long) (32 * 1024 * 1024)
+
+/* If possible, we will avoid claiming heap above this address, because it
+ seems to cause relocation problems with OSes that link at 4 MiB */
+#define HEAP_MAX_ADDR (unsigned long) (32 * 1024 * 1024)
+
+extern char _start[];
+extern char _end[];
+
+void
+grub_exit (void)
+{
+ grub_ieee1275_exit ();
+}
+
+/* Translate an OF filesystem path (separated by backslashes), into a GRUB
+ path (separated by forward slashes). */
+static void
+grub_translate_ieee1275_path (char *filepath)
+{
+ char *backslash;
+
+ backslash = grub_strchr (filepath, '\\');
+ while (backslash != 0)
+ {
+ *backslash = '/';
+ backslash = grub_strchr (filepath, '\\');
+ }
+}
+
+void
+grub_machine_set_prefix (void)
+{
+ char bootpath[64]; /* XXX check length */
+ char *filename;
+ char *prefix;
+
+ if (grub_prefix[0])
+ {
+ grub_env_set ("prefix", grub_prefix);
+ /* Prefix is hardcoded in the core image. */
+ return;
+ }
+
+ if (grub_ieee1275_get_property (grub_ieee1275_chosen, "bootpath", &bootpath,
+ sizeof (bootpath), 0))
+ {
+ /* Should never happen. */
+ grub_printf ("/chosen/bootpath property missing!\n");
+ grub_env_set ("prefix", "");
+ return;
+ }
+
+ /* Transform an OF device path to a GRUB path. */
+
+ prefix = grub_ieee1275_encode_devname (bootpath);
+
+ filename = grub_ieee1275_get_filename (bootpath);
+ if (filename)
+ {
+ char *newprefix;
+ char *lastslash = grub_strrchr (filename, '\\');
+
+ /* Truncate at last directory. */
+ if (lastslash)
+ {
+ *lastslash = '\0';
+ grub_translate_ieee1275_path (filename);
+
+ newprefix = grub_xasprintf ("%s%s", prefix, filename);
+ if (newprefix)
+ {
+ grub_free (prefix);
+ prefix = newprefix;
+ }
+ }
+ }
+
+ grub_env_set ("prefix", prefix);
+
+ grub_free (filename);
+ grub_free (prefix);
+}
+
+/* Claim some available memory in the first /memory node. */
+static void grub_claim_heap (void)
+{
+ unsigned long total = 0;
+
+ auto int NESTED_FUNC_ATTR heap_init (grub_uint64_t addr, grub_uint64_t len,
+ grub_memory_type_t type);
+ int NESTED_FUNC_ATTR heap_init (grub_uint64_t addr, grub_uint64_t len,
+ grub_memory_type_t type)
+ {
+ if (type != 1)
+ return 0;
+
+ if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM))
+ {
+ if (addr + len <= 0x180000)
+ return 0;
+
+ if (addr < 0x180000)
+ {
+ len = addr + len - 0x180000;
+ addr = 0x180000;
+ }
+ }
+ len -= 1; /* Required for some firmware. */
+
+ /* Never exceed HEAP_MAX_SIZE */
+ if (total + len > HEAP_MAX_SIZE)
+ len = HEAP_MAX_SIZE - total;
+
+ /* Avoid claiming anything above HEAP_MAX_ADDR, if possible. */
+ if ((addr < HEAP_MAX_ADDR) && /* if it's too late, don't bother */
+ (addr + len > HEAP_MAX_ADDR) && /* if it wasn't available anyway, don't bother */
+ (total + (HEAP_MAX_ADDR - addr) > HEAP_MIN_SIZE)) /* only limit ourselves when we can afford to */
+ len = HEAP_MAX_ADDR - addr;
+
+ /* In theory, firmware should already prevent this from happening by not
+ listing our own image in /memory/available. The check below is intended
+ as a safeguard in case that doesn't happen. However, it doesn't protect
+ us from corrupting our module area, which extends up to a
+ yet-undetermined region above _end. */
+ if ((addr < (grub_addr_t) _end) && ((addr + len) > (grub_addr_t) _start))
+ {
+ grub_printf ("Warning: attempt to claim over our own code!\n");
+ len = 0;
+ }
+
+ if (len)
+ {
+ /* Claim and use it. */
+ if (grub_claimmap (addr, len) < 0)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "failed to claim heap at 0x%llx, len 0x%llx",
+ addr, len);
+ grub_mm_init_region ((void *) (grub_addr_t) addr, len);
+ }
+
+ total += len;
+ if (total >= HEAP_MAX_SIZE)
+ return 1;
+
+ return 0;
+ }
+
+ if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET))
+ heap_init (HEAP_MAX_ADDR - HEAP_MIN_SIZE, HEAP_MIN_SIZE, 1);
+ else
+ grub_machine_mmap_iterate (heap_init);
+}
+
+static grub_uint64_t ieee1275_get_time_ms (void);
+
+void
+grub_machine_init (void)
+{
+ char args[256];
+ grub_ssize_t actual;
+
+ grub_ieee1275_init ();
+
+ grub_console_init_early ();
+ grub_claim_heap ();
+ grub_console_init_lately ();
+ grub_ofdisk_init ();
+
+ /* Process commandline. */
+ if (grub_ieee1275_get_property (grub_ieee1275_chosen, "bootargs", &args,
+ sizeof args, &actual) == 0
+ && actual > 1)
+ {
+ int i = 0;
+
+ while (i < actual)
+ {
+ char *command = &args[i];
+ char *end;
+ char *val;
+
+ end = grub_strchr (command, ';');
+ if (end == 0)
+ i = actual; /* No more commands after this one. */
+ else
+ {
+ *end = '\0';
+ i += end - command + 1;
+ while (grub_isspace(args[i]))
+ i++;
+ }
+
+ /* Process command. */
+ val = grub_strchr (command, '=');
+ if (val)
+ {
+ *val = '\0';
+ grub_env_set (command, val + 1);
+ }
+ }
+ }
+
+ grub_install_get_time_ms (ieee1275_get_time_ms);
+}
+
+void
+grub_machine_fini (void)
+{
+ grub_ofdisk_fini ();
+ grub_console_fini ();
+}
+
+static grub_uint64_t
+ieee1275_get_time_ms (void)
+{
+ grub_uint32_t msecs = 0;
+
+ grub_ieee1275_milliseconds (&msecs);
+
+ return msecs;
+}
+
+grub_uint32_t
+grub_get_rtc (void)
+{
+ return ieee1275_get_time_ms ();
+}
+
+grub_addr_t
+grub_arch_modules_addr (void)
+{
+ return ALIGN_UP((grub_addr_t) _end + GRUB_KERNEL_MACHINE_MOD_GAP, GRUB_KERNEL_MACHINE_MOD_ALIGN);
+}
diff --git a/grub-core/kern/ieee1275/mmap.c b/grub-core/kern/ieee1275/mmap.c
new file mode 100644
index 0000000..2e4e085
--- /dev/null
+++ b/grub-core/kern/ieee1275/mmap.c
@@ -0,0 +1,80 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,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/>.
+ */
+
+#include <grub/memory.h>
+#include <grub/ieee1275/ieee1275.h>
+#include <grub/types.h>
+
+grub_err_t
+grub_machine_mmap_iterate (grub_memory_hook_t hook)
+{
+ grub_ieee1275_phandle_t root;
+ grub_ieee1275_phandle_t memory;
+ grub_uint32_t available[32];
+ grub_ssize_t available_size;
+ grub_uint32_t address_cells = 1;
+ grub_uint32_t size_cells = 1;
+ int i;
+
+ /* Determine the format of each entry in `available'. */
+ grub_ieee1275_finddevice ("/", &root);
+ grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells,
+ sizeof address_cells, 0);
+ grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells,
+ sizeof size_cells, 0);
+
+ if (size_cells > address_cells)
+ address_cells = size_cells;
+
+ /* Load `/memory/available'. */
+ if (grub_ieee1275_finddevice ("/memory", &memory))
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ "couldn't find /memory node");
+ if (grub_ieee1275_get_integer_property (memory, "available", available,
+ sizeof available, &available_size))
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ "couldn't examine /memory/available property");
+
+ if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS))
+ {
+ address_cells = 1;
+ size_cells = 1;
+ }
+
+ /* Decode each entry and call `hook'. */
+ i = 0;
+ available_size /= sizeof (grub_uint32_t);
+ while (i < available_size)
+ {
+ grub_uint64_t address;
+ grub_uint64_t size;
+
+ address = available[i++];
+ if (address_cells == 2)
+ address = (address << 32) | available[i++];
+
+ size = available[i++];
+ if (size_cells == 2)
+ size = (size << 32) | available[i++];
+
+ if (hook (address, size, GRUB_MEMORY_AVAILABLE))
+ break;
+ }
+
+ return grub_errno;
+}
diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c
new file mode 100644
index 0000000..f5dc8ef
--- /dev/null
+++ b/grub-core/kern/ieee1275/openfw.c
@@ -0,0 +1,469 @@
+/* openfw.c -- Open firmware support functions. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/types.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/ieee1275/ieee1275.h>
+
+enum grub_ieee1275_parse_type
+{
+ GRUB_PARSE_FILENAME,
+ GRUB_PARSE_PARTITION,
+};
+
+/* Walk children of 'devpath', calling hook for each. */
+int
+grub_children_iterate (char *devpath,
+ int (*hook) (struct grub_ieee1275_devalias *alias))
+{
+ grub_ieee1275_phandle_t dev;
+ grub_ieee1275_phandle_t child;
+ char *childtype, *childpath;
+ char *childname;
+ int ret = 0;
+
+ if (grub_ieee1275_finddevice (devpath, &dev))
+ return 0;
+
+ if (grub_ieee1275_child (dev, &child))
+ return 0;
+
+ childtype = grub_malloc (IEEE1275_MAX_PROP_LEN);
+ if (!childtype)
+ return 0;
+ childpath = grub_malloc (IEEE1275_MAX_PATH_LEN);
+ if (!childpath)
+ {
+ grub_free (childtype);
+ return 0;
+ }
+ childname = grub_malloc (IEEE1275_MAX_PROP_LEN);
+ if (!childname)
+ {
+ grub_free (childpath);
+ grub_free (childtype);
+ return 0;
+ }
+
+ do
+ {
+ struct grub_ieee1275_devalias alias;
+ grub_ssize_t actual;
+
+ if (grub_ieee1275_get_property (child, "device_type", childtype,
+ IEEE1275_MAX_PROP_LEN, &actual))
+ childtype[0] = 0;
+
+ if (dev == child)
+ continue;
+
+ if (grub_ieee1275_package_to_path (child, childpath,
+ IEEE1275_MAX_PATH_LEN, &actual))
+ continue;
+
+ if (grub_strcmp (devpath, childpath) == 0)
+ continue;
+
+ if (grub_ieee1275_get_property (child, "name", childname,
+ IEEE1275_MAX_PROP_LEN, &actual))
+ continue;
+
+ alias.type = childtype;
+ alias.path = childpath;
+ alias.name = childname;
+ ret = hook (&alias);
+ if (ret)
+ break;
+ }
+ while (grub_ieee1275_peer (child, &child) != -1);
+
+ grub_free (childname);
+ grub_free (childpath);
+ grub_free (childtype);
+
+ return ret;
+}
+
+int
+grub_ieee1275_devices_iterate (int (*hook) (struct grub_ieee1275_devalias *alias))
+{
+ auto int it_through (struct grub_ieee1275_devalias *alias);
+ int it_through (struct grub_ieee1275_devalias *alias)
+ {
+ if (hook (alias))
+ return 1;
+ return grub_children_iterate (alias->path, it_through);
+ }
+
+ return grub_children_iterate ("/", it_through);
+}
+
+/* Iterate through all device aliases. This function can be used to
+ find a device of a specific type. */
+int
+grub_devalias_iterate (int (*hook) (struct grub_ieee1275_devalias *alias))
+{
+ grub_ieee1275_phandle_t aliases;
+ char *aliasname, *devtype;
+ grub_ssize_t actual;
+ struct grub_ieee1275_devalias alias;
+ int ret = 0;
+
+ if (grub_ieee1275_finddevice ("/aliases", &aliases))
+ return 0;
+
+ aliasname = grub_malloc (IEEE1275_MAX_PROP_LEN);
+ if (!aliasname)
+ return 0;
+ devtype = grub_malloc (IEEE1275_MAX_PROP_LEN);
+ if (!devtype)
+ {
+ grub_free (aliasname);
+ return 0;
+ }
+
+ /* Find the first property. */
+ aliasname[0] = '\0';
+
+ while (grub_ieee1275_next_property (aliases, aliasname, aliasname) > 0)
+ {
+ grub_ieee1275_phandle_t dev;
+ grub_ssize_t pathlen;
+ char *devpath;
+
+ grub_dprintf ("devalias", "devalias name = %s\n", aliasname);
+
+ grub_ieee1275_get_property_length (aliases, aliasname, &pathlen);
+
+ /* The property `name' is a special case we should skip. */
+ if (!grub_strcmp (aliasname, "name"))
+ continue;
+
+ /* Sun's OpenBoot often doesn't zero terminate the device alias
+ strings, so we will add a NULL byte at the end explicitly. */
+ pathlen += 1;
+
+ devpath = grub_malloc (pathlen);
+ if (! devpath)
+ {
+ grub_free (devtype);
+ grub_free (aliasname);
+ return 0;
+ }
+
+ if (grub_ieee1275_get_property (aliases, aliasname, devpath, pathlen,
+ &actual))
+ {
+ grub_dprintf ("devalias", "get_property (%s) failed\n", aliasname);
+ goto nextprop;
+ }
+ devpath [actual] = '\0';
+
+ if (grub_ieee1275_finddevice (devpath, &dev))
+ {
+ grub_dprintf ("devalias", "finddevice (%s) failed\n", devpath);
+ goto nextprop;
+ }
+
+ if (grub_ieee1275_get_property (dev, "device_type", devtype,
+ IEEE1275_MAX_PROP_LEN, &actual))
+ {
+ /* NAND device don't have device_type property. */
+ devtype[0] = 0;
+ }
+
+ alias.name = aliasname;
+ alias.path = devpath;
+ alias.type = devtype;
+ ret = hook (&alias);
+
+nextprop:
+ grub_free (devpath);
+ if (ret)
+ break;
+ }
+
+ grub_free (devtype);
+ grub_free (aliasname);
+ return ret;
+}
+
+/* Call the "map" method of /chosen/mmu. */
+int
+grub_ieee1275_map (grub_addr_t phys, grub_addr_t virt, grub_size_t size,
+ grub_uint32_t mode)
+{
+ struct map_args {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t method;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t mode;
+ grub_ieee1275_cell_t size;
+ grub_ieee1275_cell_t virt;
+#ifdef GRUB_MACHINE_SPARC64
+ grub_ieee1275_cell_t phys_high;
+#endif
+ grub_ieee1275_cell_t phys_low;
+ grub_ieee1275_cell_t catch_result;
+ } args;
+
+ INIT_IEEE1275_COMMON (&args.common, "call-method",
+#ifdef GRUB_MACHINE_SPARC64
+ 7,
+#else
+ 6,
+#endif
+ 1);
+ args.method = (grub_ieee1275_cell_t) "map";
+ args.ihandle = grub_ieee1275_mmu;
+#ifdef GRUB_MACHINE_SPARC64
+ args.phys_high = 0;
+#endif
+ args.phys_low = phys;
+ args.virt = virt;
+ args.size = size;
+ args.mode = mode; /* Format is WIMG0PP. */
+ args.catch_result = (grub_ieee1275_cell_t) -1;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+
+ return args.catch_result;
+}
+
+int
+grub_claimmap (grub_addr_t addr, grub_size_t size)
+{
+ if (grub_ieee1275_claim (addr, size, 0, 0))
+ return -1;
+
+ if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_REAL_MODE)
+ && grub_ieee1275_map (addr, addr, size, 0x00))
+ {
+ grub_printf ("map failed: address 0x%llx, size 0x%llx\n",
+ (long long) addr, (long long) size);
+ grub_ieee1275_release (addr, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Get the device arguments of the Open Firmware node name `path'. */
+static char *
+grub_ieee1275_get_devargs (const char *path)
+{
+ char *colon = grub_strchr (path, ':');
+
+ if (! colon)
+ return 0;
+
+ return grub_strdup (colon + 1);
+}
+
+/* Get the device path of the Open Firmware node name `path'. */
+static char *
+grub_ieee1275_get_devname (const char *path)
+{
+ char *colon = grub_strchr (path, ':');
+ char *newpath = 0;
+ int pathlen = grub_strlen (path);
+ auto int match_alias (struct grub_ieee1275_devalias *alias);
+
+ int match_alias (struct grub_ieee1275_devalias *curalias)
+ {
+ /* briQ firmware can change capitalization in /chosen/bootpath. */
+ if (! grub_strncasecmp (curalias->path, path, pathlen))
+ {
+ newpath = grub_strdup (curalias->name);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if (colon)
+ pathlen = (int)(colon - path);
+
+ /* Try to find an alias for this device. */
+ grub_devalias_iterate (match_alias);
+
+ if (! newpath)
+ newpath = grub_strndup (path, pathlen);
+
+ return newpath;
+}
+
+static char *
+grub_ieee1275_parse_args (const char *path, enum grub_ieee1275_parse_type ptype)
+{
+ char type[64]; /* XXX check size. */
+ char *device = grub_ieee1275_get_devname (path);
+ char *args = grub_ieee1275_get_devargs (path);
+ char *ret = 0;
+ grub_ieee1275_phandle_t dev;
+
+ if (!args)
+ /* Shouldn't happen. */
+ return 0;
+
+ /* We need to know what type of device it is in order to parse the full
+ file path properly. */
+ if (grub_ieee1275_finddevice (device, &dev))
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE, "device %s not found", device);
+ goto fail;
+ }
+ if (grub_ieee1275_get_property (dev, "device_type", &type, sizeof type, 0))
+ {
+ grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+ "device %s lacks a device_type property", device);
+ goto fail;
+ }
+
+ if (!grub_strcmp ("block", type))
+ {
+ /* The syntax of the device arguments is defined in the CHRP and PReP
+ IEEE1275 bindings: "[partition][,[filename]]". */
+ char *comma = grub_strchr (args, ',');
+
+ if (ptype == GRUB_PARSE_FILENAME)
+ {
+ if (comma)
+ {
+ char *filepath = comma + 1;
+
+ /* Make sure filepath has leading backslash. */
+ if (filepath[0] != '\\')
+ ret = grub_xasprintf ("\\%s", filepath);
+ else
+ ret = grub_strdup (filepath);
+ }
+ }
+ else if (ptype == GRUB_PARSE_PARTITION)
+ {
+ if (!comma)
+ ret = grub_strdup (args);
+ else
+ ret = grub_strndup (args, (grub_size_t)(comma - args));
+ }
+ }
+ else
+ {
+ /* XXX Handle net devices by configuring & registering a grub_net_dev
+ here, then return its name?
+ Example path: "net:<server ip>,<file name>,<client ip>,<gateway
+ ip>,<bootp retries>,<tftp retries>". */
+ grub_printf ("Unsupported type %s for device %s\n", type, device);
+ }
+
+fail:
+ grub_free (device);
+ grub_free (args);
+ return ret;
+}
+
+char *
+grub_ieee1275_get_filename (const char *path)
+{
+ return grub_ieee1275_parse_args (path, GRUB_PARSE_FILENAME);
+}
+
+/* Convert a device name from IEEE1275 syntax to GRUB syntax. */
+char *
+grub_ieee1275_encode_devname (const char *path)
+{
+ char *device = grub_ieee1275_get_devname (path);
+ char *partition = grub_ieee1275_parse_args (path, GRUB_PARSE_PARTITION);
+ char *encoding;
+
+ if (partition && partition[0])
+ {
+ unsigned int partno = grub_strtoul (partition, 0, 0);
+
+ if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS))
+ /* GRUB partition 1 is OF partition 0. */
+ partno++;
+
+ encoding = grub_xasprintf ("(%s,%d)", device, partno);
+ }
+ else
+ encoding = grub_xasprintf ("(%s)", device);
+
+ grub_free (partition);
+ grub_free (device);
+
+ return encoding;
+}
+
+/* On i386, a firmware-independant grub_reboot() is provided by realmode.S. */
+#ifndef __i386__
+void
+grub_reboot (void)
+{
+ grub_ieee1275_interpret ("reset-all", 0);
+ for (;;) ;
+}
+#endif
+
+/* Resolve aliases. */
+char *
+grub_ieee1275_canonicalise_devname (const char *path)
+{
+ struct canon_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t path;
+ grub_ieee1275_cell_t buf;
+ grub_ieee1275_cell_t inlen;
+ grub_ieee1275_cell_t outlen;
+ }
+ args;
+ char *buf = NULL;
+ grub_size_t bufsize = 64;
+ int i;
+
+ for (i = 0; i < 2; i++)
+ {
+ grub_free (buf);
+
+ buf = grub_malloc (bufsize);
+ if (!buf)
+ return NULL;
+
+ INIT_IEEE1275_COMMON (&args.common, "canon", 3, 1);
+ args.path = (grub_ieee1275_cell_t) path;
+ args.buf = (grub_ieee1275_cell_t) buf;
+ args.inlen = (grub_ieee1275_cell_t) (bufsize - 1);
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return 0;
+ if (args.outlen > bufsize - 1)
+ {
+ bufsize = args.outlen + 2;
+ continue;
+ }
+ return buf;
+ }
+ /* Shouldn't reach here. */
+ grub_free (buf);
+ return NULL;
+}
diff --git a/grub-core/kern/list.c b/grub-core/kern/list.c
new file mode 100644
index 0000000..33c3341
--- /dev/null
+++ b/grub-core/kern/list.c
@@ -0,0 +1,87 @@
+/* list.c - grub list function */
+/*
+ * 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/list.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+void
+grub_list_push (grub_list_t *head, grub_list_t item)
+{
+ item->next = *head;
+ *head = item;
+}
+
+void
+grub_list_remove (grub_list_t *head, grub_list_t item)
+{
+ grub_list_t *p, q;
+
+ for (p = head, q = *p; q; p = &(q->next), q = q->next)
+ if (q == item)
+ {
+ *p = q->next;
+ break;
+ }
+}
+
+void *
+grub_named_list_find (grub_named_list_t head, const char *name)
+{
+ grub_named_list_t item;
+
+ FOR_LIST_ELEMENTS (item, head)
+ if (grub_strcmp (item->name, name) == 0)
+ return item;
+
+ return NULL;
+}
+
+void
+grub_prio_list_insert (grub_prio_list_t *head, grub_prio_list_t nitem)
+{
+ int inactive = 0;
+
+ grub_prio_list_t *p, q;
+
+ for (p = head, q = *p; q; p = &(q->next), q = q->next)
+ {
+ int r;
+
+ r = grub_strcmp (nitem->name, q->name);
+ if (r < 0)
+ break;
+ if (r > 0)
+ continue;
+
+ if (nitem->prio >= (q->prio & GRUB_PRIO_LIST_PRIO_MASK))
+ {
+ q->prio &= ~GRUB_PRIO_LIST_FLAG_ACTIVE;
+ break;
+ }
+
+ inactive = 1;
+ }
+
+ *p = nitem;
+ nitem->next = q;
+
+ if (! inactive)
+ nitem->prio |= GRUB_PRIO_LIST_FLAG_ACTIVE;
+}
diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
new file mode 100644
index 0000000..da71232
--- /dev/null
+++ b/grub-core/kern/main.c
@@ -0,0 +1,200 @@
+/* main.c - the kernel main routine */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005,2006,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/kernel.h>
+#include <grub/misc.h>
+#include <grub/symbol.h>
+#include <grub/dl.h>
+#include <grub/term.h>
+#include <grub/file.h>
+#include <grub/device.h>
+#include <grub/env.h>
+#include <grub/mm.h>
+#include <grub/command.h>
+#include <grub/reader.h>
+#include <grub/parser.h>
+
+void
+grub_module_iterate (int (*hook) (struct grub_module_header *header))
+{
+ struct grub_module_info *modinfo;
+ struct grub_module_header *header;
+ grub_addr_t modbase;
+
+ modbase = grub_arch_modules_addr ();
+ modinfo = (struct grub_module_info *) modbase;
+
+ /* Check if there are any modules. */
+ if ((modinfo == 0) || modinfo->magic != GRUB_MODULE_MAGIC)
+ return;
+
+ for (header = (struct grub_module_header *) (modbase + modinfo->offset);
+ header < (struct grub_module_header *) (modbase + modinfo->size);
+ header = (struct grub_module_header *) ((char *) header + header->size))
+ {
+ if (hook (header))
+ break;
+ }
+}
+
+/* This is actualy platform-independant but used only on yeeloong and sparc. */
+#if defined (GRUB_MACHINE_MIPS_YEELOONG) || defined (GRUB_MACHINE_SPARC64)
+grub_addr_t
+grub_modules_get_end (void)
+{
+ struct grub_module_info *modinfo;
+ grub_addr_t modbase;
+
+ modbase = grub_arch_modules_addr ();
+ modinfo = (struct grub_module_info *) modbase;
+
+ /* Check if there are any modules. */
+ if ((modinfo == 0) || modinfo->magic != GRUB_MODULE_MAGIC)
+ return modbase;
+
+ return modbase + modinfo->size;
+}
+#endif
+
+/* Load all modules in core. */
+static void
+grub_load_modules (void)
+{
+ auto int hook (struct grub_module_header *);
+ int hook (struct grub_module_header *header)
+ {
+ /* Not an ELF module, skip. */
+ if (header->type != OBJ_TYPE_ELF)
+ return 0;
+
+ if (! grub_dl_load_core ((char *) header + sizeof (struct grub_module_header),
+ (header->size - sizeof (struct grub_module_header))))
+ grub_fatal ("%s", grub_errmsg);
+
+ if (grub_errno)
+ grub_print_error ();
+
+ return 0;
+ }
+
+ grub_module_iterate (hook);
+}
+
+static void
+grub_load_config (void)
+{
+ auto int hook (struct grub_module_header *);
+ int hook (struct grub_module_header *header)
+ {
+ /* Not an embedded config, skip. */
+ if (header->type != OBJ_TYPE_CONFIG)
+ return 0;
+
+ grub_parser_execute ((char *) header +
+ sizeof (struct grub_module_header));
+ return 1;
+ }
+
+ grub_module_iterate (hook);
+}
+
+/* Write hook for the environment variables of root. Remove surrounding
+ parentheses, if any. */
+static char *
+grub_env_write_root (struct grub_env_var *var __attribute__ ((unused)),
+ const char *val)
+{
+ /* XXX Is it better to check the existence of the device? */
+ grub_size_t len = grub_strlen (val);
+
+ if (val[0] == '(' && val[len - 1] == ')')
+ return grub_strndup (val + 1, len - 2);
+
+ return grub_strdup (val);
+}
+
+/* Set the root device according to the dl prefix. */
+static void
+grub_set_root_dev (void)
+{
+ const char *prefix;
+
+ grub_register_variable_hook ("root", 0, grub_env_write_root);
+
+ prefix = grub_env_get ("prefix");
+
+ if (prefix)
+ {
+ char *dev;
+
+ dev = grub_file_get_device_name (prefix);
+ if (dev)
+ {
+ grub_env_set ("root", dev);
+ grub_free (dev);
+ }
+ }
+}
+
+/* Load the normal mode module and execute the normal mode if possible. */
+static void
+grub_load_normal_mode (void)
+{
+ /* Load the module. */
+ grub_dl_load ("normal");
+
+ /* Something went wrong. Print errors here to let user know why we're entering rescue mode. */
+ grub_print_error ();
+ grub_errno = 0;
+
+ grub_command_execute ("normal", 0, 0);
+}
+
+/* The main routine. */
+void
+grub_main (void)
+{
+ /* First of all, initialize the machine. */
+ grub_machine_init ();
+
+ /* Hello. */
+ grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
+ grub_printf ("Welcome to GRUB!\n\n");
+ grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
+
+ /* Load pre-loaded modules and free the space. */
+ grub_register_exported_symbols ();
+#ifdef GRUB_LINKER_HAVE_INIT
+ grub_arch_dl_init_linker ();
+#endif
+ grub_load_modules ();
+
+ /* It is better to set the root device as soon as possible,
+ for convenience. */
+ grub_machine_set_prefix ();
+ grub_set_root_dev ();
+ grub_env_export ("root");
+ grub_env_export ("prefix");
+
+ grub_register_core_commands ();
+
+ grub_load_config ();
+ grub_load_normal_mode ();
+ grub_rescue_run ();
+}
diff --git a/grub-core/kern/mips/cache.S b/grub-core/kern/mips/cache.S
new file mode 100644
index 0000000..a4aa06f
--- /dev/null
+++ b/grub-core/kern/mips/cache.S
@@ -0,0 +1,45 @@
+
+#include <grub/symbol.h>
+
+ .set noreorder
+ .set nomacro
+
+FUNCTION (grub_cpu_flush_cache)
+FUNCTION (grub_arch_sync_caches)
+#include "cache_flush.S"
+ j $ra
+
+FUNCTION (grub_arch_sync_dma_caches)
+ move $t2, $a0
+ addu $t3, $a0, $a1
+ srl $t2, $t2, 5
+ sll $t2, $t2, 5
+ addu $t3, $t3, 0x1f
+ srl $t3, $t3, 5
+ sll $t3, $t3, 5
+ move $t0, $t2
+ subu $t1, $t3, $t2
+1:
+ cache 1, 0($t0)
+ addiu $t1, $t1, 0xffff
+ bne $t1, $zero, 1b
+ addiu $t0, $t0, 0x1
+ sync
+ move $t0, $t2
+ subu $t1, $t3, $t2
+2:
+ cache 0, 0($t0)
+ addiu $t1, $t1, 0xffff
+ bne $t1, $zero, 2b
+ addiu $t0, $t0, 0x1
+ sync
+ move $t0, $t2
+ subu $t1, $t3, $t2
+2:
+ cache 23, 0($t0)
+ addiu $t1, $t1, 0xffff
+ bne $t1, $zero, 2b
+ addiu $t0, $t0, 0x1
+ sync
+
+ jr $ra \ No newline at end of file
diff --git a/grub-core/kern/mips/cache_flush.S b/grub-core/kern/mips/cache_flush.S
new file mode 100644
index 0000000..11096c0
--- /dev/null
+++ b/grub-core/kern/mips/cache_flush.S
@@ -0,0 +1,23 @@
+ move $t2, $a0
+ addu $t3, $a0, $a1
+ srl $t2, $t2, 5
+ sll $t2, $t2, 5
+ addu $t3, $t3, 0x1f
+ srl $t3, $t3, 5
+ sll $t3, $t3, 5
+ move $t0, $t2
+ subu $t1, $t3, $t2
+1:
+ cache 1, 0($t0)
+ addiu $t1, $t1, 0xffff
+ bne $t1, $zero, 1b
+ addiu $t0, $t0, 0x1
+ sync
+ move $t0, $t2
+ subu $t1, $t3, $t2
+2:
+ cache 0, 0($t0)
+ addiu $t1, $t1, 0xffff
+ bne $t1, $zero, 2b
+ addiu $t0, $t0, 0x1
+ sync
diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c
new file mode 100644
index 0000000..e17e9f3
--- /dev/null
+++ b/grub-core/kern/mips/dl.c
@@ -0,0 +1,239 @@
+/* dl-386.c - arch-dependent part of loadable module support */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,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/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/cpu/types.h>
+#include <grub/mm.h>
+
+/* Dummy __gnu_local_gp. Resolved by linker. */
+static char __gnu_local_gp_dummy;
+
+/* Check if EHDR is a valid ELF header. */
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+ Elf_Ehdr *e = ehdr;
+
+ /* Check the magic numbers. */
+#ifdef WORDS_BIGENDIAN
+ if (e->e_ident[EI_CLASS] != ELFCLASS32
+ || e->e_ident[EI_DATA] != ELFDATA2MSB
+ || e->e_machine != EM_MIPS)
+#else
+ if (e->e_ident[EI_CLASS] != ELFCLASS32
+ || e->e_ident[EI_DATA] != ELFDATA2LSB
+ || e->e_machine != EM_MIPS)
+#endif
+ return grub_error (GRUB_ERR_BAD_OS, "invalid arch specific ELF magic");
+
+ return GRUB_ERR_NONE;
+}
+
+/* Relocate symbols. */
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+{
+ Elf_Ehdr *e = ehdr;
+ Elf_Shdr *s;
+ Elf_Word entsize;
+ unsigned i;
+ grub_size_t gp_size = 0;
+ /* FIXME: suboptimal. */
+ grub_uint32_t *gp, *gpptr;
+ grub_uint32_t gp0;
+
+ /* Find a symbol table. */
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+
+ if (i == e->e_shnum)
+ return grub_error (GRUB_ERR_BAD_MODULE, "no symtab found");
+
+ entsize = s->sh_entsize;
+
+ /* Find reginfo. */
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_MIPS_REGINFO)
+ break;
+
+ if (i == e->e_shnum)
+ return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found");
+
+ gp0 = ((grub_uint32_t *)((char *) e + s->sh_offset))[5];
+
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_REL)
+ {
+ grub_dl_segment_t seg;
+
+ /* Find the target segment. */
+ for (seg = mod->segment; seg; seg = seg->next)
+ if (seg->section == s->sh_info)
+ break;
+
+ if (seg)
+ {
+ Elf_Rel *rel, *max;
+
+ for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
+ max = rel + s->sh_size / s->sh_entsize;
+ rel < max;
+ rel++)
+ switch (ELF_R_TYPE (rel->r_info))
+ {
+ case R_MIPS_GOT16:
+ case R_MIPS_CALL16:
+ case R_MIPS_GPREL32:
+ gp_size += 4;
+ break;
+ }
+ }
+ }
+
+ if (gp_size > 0x08000)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "__gnu_local_gp is too big\n");
+
+ gpptr = gp = grub_malloc (gp_size);
+ if (!gp)
+ return grub_errno;
+
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_REL)
+ {
+ grub_dl_segment_t seg;
+
+ /* Find the target segment. */
+ for (seg = mod->segment; seg; seg = seg->next)
+ if (seg->section == s->sh_info)
+ break;
+
+ if (seg)
+ {
+ Elf_Rel *rel, *max;
+
+ for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
+ max = rel + s->sh_size / s->sh_entsize;
+ rel < max;
+ rel++)
+ {
+ Elf_Word *addr;
+ Elf_Sym *sym;
+
+ if (seg->size < rel->r_offset)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "reloc offset is out of the segment");
+
+ addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+ sym = (Elf_Sym *) ((char *) mod->symtab
+ + entsize * ELF_R_SYM (rel->r_info));
+ if (sym->st_value == (grub_addr_t) &__gnu_local_gp_dummy)
+ sym->st_value = (grub_addr_t) gp;
+
+ switch (ELF_R_TYPE (rel->r_info))
+ {
+ case R_MIPS_HI16:
+ {
+ grub_uint32_t value;
+ Elf_Rel *rel2;
+
+ /* Handle partner lo16 relocation. Lower part is
+ treated as signed. Hence add 0x8000 to compensate.
+ */
+ value = (*(grub_uint16_t *) addr << 16)
+ + sym->st_value + 0x8000;
+ for (rel2 = rel + 1; rel2 < max; rel2++)
+ if (ELF_R_SYM (rel2->r_info)
+ == ELF_R_SYM (rel->r_info)
+ && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
+ {
+ value += *(grub_int16_t *)
+ ((char *) seg->addr + rel2->r_offset);
+ break;
+ }
+ *(grub_uint16_t *) addr = (value >> 16) & 0xffff;
+ }
+ break;
+ case R_MIPS_LO16:
+ *(grub_uint16_t *) addr += (sym->st_value) & 0xffff;
+ break;
+ case R_MIPS_32:
+ *(grub_uint32_t *) addr += sym->st_value;
+ break;
+ case R_MIPS_GPREL32:
+ *(grub_uint32_t *) addr = sym->st_value
+ + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp;
+ break;
+
+ case R_MIPS_26:
+ {
+ grub_uint32_t value;
+ grub_uint32_t raw;
+ raw = (*(grub_uint32_t *) addr) & 0x3ffffff;
+ value = raw << 2;
+ value += sym->st_value;
+ raw = (value >> 2) & 0x3ffffff;
+
+ *(grub_uint32_t *) addr =
+ raw | ((*(grub_uint32_t *) addr) & 0xfc000000);
+ }
+ break;
+ case R_MIPS_GOT16:
+ case R_MIPS_CALL16:
+ /* FIXME: reuse*/
+ *gpptr = sym->st_value + *(grub_uint16_t *) addr;
+ *(grub_uint16_t *) addr
+ = sizeof (grub_uint32_t) * (gpptr - gp);
+ gpptr++;
+ break;
+ case R_MIPS_JALR:
+ break;
+ default:
+ {
+ grub_free (gp);
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "Unknown relocation type %d\n",
+ ELF_R_TYPE (rel->r_info));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+void
+grub_arch_dl_init_linker (void)
+{
+ grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0);
+}
+
diff --git a/grub-core/kern/mips/init.c b/grub-core/kern/mips/init.c
new file mode 100644
index 0000000..f220108
--- /dev/null
+++ b/grub-core/kern/mips/init.c
@@ -0,0 +1,34 @@
+/*
+ * 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/kernel.h>
+#include <grub/env.h>
+
+void
+grub_machine_set_prefix (void)
+{
+ grub_env_set ("prefix", grub_prefix);
+}
+
+extern char _end[];
+
+grub_addr_t
+grub_arch_modules_addr (void)
+{
+ return (grub_addr_t) _end;
+}
diff --git a/grub-core/kern/mips/qemu-mips/init.c b/grub-core/kern/mips/qemu-mips/init.c
new file mode 100644
index 0000000..f2bb652
--- /dev/null
+++ b/grub-core/kern/mips/qemu-mips/init.c
@@ -0,0 +1,58 @@
+#include <grub/kernel.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/time.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/machine/kernel.h>
+#include <grub/machine/memory.h>
+#include <grub/cpu/kernel.h>
+
+#define RAMSIZE (*(grub_uint32_t *) ((16 << 20) - 264))
+
+grub_uint32_t
+grub_get_rtc (void)
+{
+ static int calln = 0;
+ return calln++;
+}
+
+void
+grub_machine_init (void)
+{
+ grub_mm_init_region ((void *) GRUB_MACHINE_MEMORY_USABLE,
+ RAMSIZE - (GRUB_MACHINE_MEMORY_USABLE & 0x7fffffff));
+ grub_install_get_time_ms (grub_rtc_get_time_ms);
+}
+
+void
+grub_machine_fini (void)
+{
+}
+
+void
+grub_exit (void)
+{
+ while (1);
+}
+
+void
+grub_halt (void)
+{
+ while (1);
+}
+
+void
+grub_reboot (void)
+{
+ while (1);
+}
+
+grub_err_t
+grub_machine_mmap_iterate (grub_memory_hook_t hook)
+{
+ hook (0, RAMSIZE, GRUB_MEMORY_AVAILABLE);
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/kern/mips/startup.S b/grub-core/kern/mips/startup.S
new file mode 100644
index 0000000..ae0e0b1
--- /dev/null
+++ b/grub-core/kern/mips/startup.S
@@ -0,0 +1,126 @@
+/* startup.S - Startup code for the MIPS. */
+/*
+ * 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/symbol.h>
+#include <grub/offsets.h>
+#include <grub/machine/memory.h>
+#include <grub/offsets.h>
+
+#define BASE_ADDR 8
+
+ .globl __start, _start, start
+ .set noreorder
+ .set nomacro
+__start:
+_start:
+start:
+.extern __bss_start
+.extern _end
+ bal cont
+ nop
+
+ . = _start + GRUB_KERNEL_MIPS_YEELOONG_TOTAL_MODULE_SIZE
+total_module_size:
+ .long 0
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX
+
+VARIABLE(grub_prefix)
+
+ /* to be filled by grub-mkimage */
+
+ /*
+ * Leave some breathing room for the prefix.
+ */
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX_END
+#ifdef GRUB_MACHINE_MIPS_YEELOONG
+VARIABLE (grub_arch_busclock)
+ .long 0
+VARIABLE (grub_arch_cpuclock)
+ .long 0
+VARIABLE (grub_arch_memsize)
+ .long 0
+VARIABLE (grub_arch_highmemsize)
+ .long 0
+#endif
+cont:
+ /* Save our base. */
+ move $s0, $ra
+
+#ifdef GRUB_MACHINE_MIPS_YEELOONG
+ lui $t1, %hi(grub_arch_busclock)
+ addiu $t1, %lo(grub_arch_busclock)
+ sw $s2, 0($t1)
+ sw $s3, 4($t1)
+ sw $s4, 8($t1)
+ sw $s5, 12($t1)
+#endif
+
+ /* Move the modules out of BSS. */
+ lui $t2, %hi(__bss_start)
+ addiu $t2, %lo(__bss_start)
+
+ lui $t1, %hi(_end)
+ addiu $t1, %lo(_end)
+ addiu $t1, (GRUB_KERNEL_MACHINE_MOD_ALIGN - 1)
+ li $t3, (GRUB_KERNEL_MACHINE_MOD_ALIGN - 1)
+ nor $t3, $t3, $0
+ and $t1, $t1, $t3
+
+ lw $t3, (GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE - BASE_ADDR)($s0)
+
+ /* Backward copy. */
+ add $t1, $t1, $t3
+ add $t2, $t2, $t3
+ addiu $t1, $t1, -1
+ addiu $t2, $t2, -1
+
+ /* $t2 is source. $t1 is destination. $t3 is size. */
+modulesmovcont:
+ beq $t3, $0, modulesmovdone
+ nop
+ lb $t4, 0($t2)
+ sb $t4, 0($t1)
+ addiu $t2, $t2, -1
+ addiu $t1, $t1, -1
+ b modulesmovcont
+ addiu $t3, $t3, -1
+modulesmovdone:
+
+ /* Clean BSS. */
+
+ lui $t1, %hi(__bss_start)
+ addiu $t1, $t1, %lo(__bss_start)
+ lui $t2, %hi(_end)
+ addiu $t2, $t2, %lo(_end)
+bsscont:
+ sb $0,0($t1)
+ addiu $t1, $t1, 1
+ sltu $t3, $t1, $t2
+ bne $t3, $0, bsscont
+ nop
+
+ lui $t1, %hi(grub_main)
+ addiu $t1, %lo(grub_main)
+
+ lui $sp, %hi(GRUB_MACHINE_MEMORY_STACK_HIGH)
+ jr $t1
+ addiu $sp, $sp, %lo(GRUB_MACHINE_MEMORY_STACK_HIGH)
+
diff --git a/grub-core/kern/mips/yeeloong/init.c b/grub-core/kern/mips/yeeloong/init.c
new file mode 100644
index 0000000..7a48d69
--- /dev/null
+++ b/grub-core/kern/mips/yeeloong/init.c
@@ -0,0 +1,253 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/kernel.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/time.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/machine/kernel.h>
+#include <grub/machine/memory.h>
+#include <grub/memory.h>
+#include <grub/mips/loongson.h>
+#include <grub/cs5536.h>
+#include <grub/term.h>
+#include <grub/machine/ec.h>
+
+extern void grub_video_sm712_init (void);
+extern void grub_video_init (void);
+extern void grub_bitmap_init (void);
+extern void grub_font_init (void);
+extern void grub_gfxterm_init (void);
+extern void grub_at_keyboard_init (void);
+extern void grub_serial_init (void);
+extern void grub_terminfo_init (void);
+extern void grub_keylayouts_init (void);
+extern void grub_boot_init (void);
+
+/* FIXME: use interrupt to count high. */
+grub_uint64_t
+grub_get_rtc (void)
+{
+ static grub_uint32_t high = 0;
+ static grub_uint32_t last = 0;
+ grub_uint32_t low;
+
+ asm volatile ("mfc0 %0, " GRUB_CPU_LOONGSON_COP0_TIMER_COUNT : "=r" (low));
+ if (low < last)
+ high++;
+ last = low;
+
+ return (((grub_uint64_t) high) << 32) | low;
+}
+
+grub_err_t
+grub_machine_mmap_iterate (grub_memory_hook_t hook)
+{
+ hook (GRUB_ARCH_LOWMEMPSTART, grub_arch_memsize << 20,
+ GRUB_MEMORY_AVAILABLE);
+ hook (GRUB_ARCH_HIGHMEMPSTART, grub_arch_highmemsize << 20,
+ GRUB_MEMORY_AVAILABLE);
+ return GRUB_ERR_NONE;
+}
+
+static void
+init_pci (void)
+{
+ auto int NESTED_FUNC_ATTR set_card (grub_pci_device_t dev, grub_pci_id_t pciid);
+ int NESTED_FUNC_ATTR set_card (grub_pci_device_t dev, grub_pci_id_t pciid)
+ {
+ grub_pci_address_t addr;
+ /* FIXME: autoscan for BARs and devices. */
+ switch (pciid)
+ {
+ case GRUB_YEELOONG_OHCI_PCIID:
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
+ grub_pci_write (addr, 0x5025000);
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
+ grub_pci_write_word (addr, GRUB_PCI_COMMAND_SERR_ENABLE
+ | GRUB_PCI_COMMAND_PARITY_ERROR
+ | GRUB_PCI_COMMAND_BUS_MASTER
+ | GRUB_PCI_COMMAND_MEM_ENABLED);
+
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_STATUS);
+ grub_pci_write_word (addr, 0x0200 | GRUB_PCI_STATUS_CAPABILITIES);
+ break;
+ case GRUB_YEELOONG_EHCI_PCIID:
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
+ grub_pci_write (addr, 0x5026000);
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
+ grub_pci_write_word (addr, GRUB_PCI_COMMAND_SERR_ENABLE
+ | GRUB_PCI_COMMAND_PARITY_ERROR
+ | GRUB_PCI_COMMAND_BUS_MASTER
+ | GRUB_PCI_COMMAND_MEM_ENABLED);
+
+ addr = grub_pci_make_address (dev, GRUB_PCI_REG_STATUS);
+ grub_pci_write_word (addr, (1 << GRUB_PCI_STATUS_DEVSEL_TIMING_SHIFT)
+ | GRUB_PCI_STATUS_CAPABILITIES);
+ break;
+ }
+ return 0;
+ }
+
+ *((volatile grub_uint32_t *) GRUB_CPU_LOONGSON_PCI_HIT1_SEL_LO) = 0x8000000c;
+ *((volatile grub_uint32_t *) GRUB_CPU_LOONGSON_PCI_HIT1_SEL_HI) = 0xffffffff;
+
+ /* Setup PCI controller. */
+ *((volatile grub_uint16_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER
+ + GRUB_PCI_REG_COMMAND))
+ = GRUB_PCI_COMMAND_PARITY_ERROR | GRUB_PCI_COMMAND_BUS_MASTER
+ | GRUB_PCI_COMMAND_MEM_ENABLED;
+ *((volatile grub_uint16_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER
+ + GRUB_PCI_REG_STATUS))
+ = (1 << GRUB_PCI_STATUS_DEVSEL_TIMING_SHIFT)
+ | GRUB_PCI_STATUS_FAST_B2B_CAPABLE | GRUB_PCI_STATUS_66MHZ_CAPABLE
+ | GRUB_PCI_STATUS_CAPABILITIES;
+
+ *((volatile grub_uint32_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER
+ + GRUB_PCI_REG_CACHELINE)) = 0xff;
+ *((volatile grub_uint32_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER
+ + GRUB_PCI_REG_ADDRESS_REG0))
+ = 0x80000000 | GRUB_PCI_ADDR_MEM_TYPE_64 | GRUB_PCI_ADDR_MEM_PREFETCH;
+ *((volatile grub_uint32_t *) (GRUB_MACHINE_PCI_CONTROLLER_HEADER
+ + GRUB_PCI_REG_ADDRESS_REG1)) = 0;
+
+ grub_pci_iterate (set_card);
+}
+
+void
+grub_machine_init (void)
+{
+ grub_addr_t modend;
+
+ /* FIXME: measure this. */
+ if (grub_arch_busclock == 0)
+ {
+ grub_arch_busclock = 66000000;
+ grub_arch_cpuclock = 797000000;
+ }
+
+ grub_install_get_time_ms (grub_rtc_get_time_ms);
+
+ if (grub_arch_memsize == 0)
+ {
+ grub_port_t smbbase;
+ grub_err_t err;
+ grub_pci_device_t dev;
+ struct grub_smbus_spd spd;
+ unsigned totalmem;
+ int i;
+
+ if (!grub_cs5536_find (&dev))
+ grub_fatal ("No CS5536 found\n");
+
+ err = grub_cs5536_init_smbus (dev, 0x7ff, &smbbase);
+ if (err)
+ grub_fatal ("Couldn't init SMBus: %s\n", grub_errmsg);
+
+ /* Yeeloong has only one memory slot. */
+ err = grub_cs5536_read_spd (smbbase, GRUB_SMB_RAM_START_ADDR, &spd);
+ if (err)
+ grub_fatal ("Couldn't read SPD: %s\n", grub_errmsg);
+ for (i = 5; i < 13; i++)
+ if (spd.ddr2.rank_capacity & (1 << (i & 7)))
+ break;
+ /* Something is wrong. */
+ if (i == 13)
+ totalmem = 256;
+ else
+ totalmem = ((spd.ddr2.num_of_ranks
+ & GRUB_SMBUS_SPD_MEMORY_NUM_OF_RANKS_MASK) + 1) << (i + 2);
+
+ if (totalmem >= 256)
+ {
+ grub_arch_memsize = 256;
+ grub_arch_highmemsize = totalmem - 256;
+ }
+ else
+ {
+ grub_arch_memsize = (totalmem >> 20);
+ grub_arch_highmemsize = 0;
+ }
+
+ grub_cs5536_init_geode (dev);
+
+ init_pci ();
+ }
+
+ modend = grub_modules_get_end ();
+ grub_mm_init_region ((void *) modend, (grub_arch_memsize << 20)
+ - (modend - GRUB_ARCH_LOWMEMVSTART));
+ /* FIXME: use upper memory as well. */
+
+ /* Initialize output terminal (can't be done earlier, as gfxterm
+ relies on a working heap. */
+ grub_video_init ();
+ grub_video_sm712_init ();
+ grub_bitmap_init ();
+ grub_font_init ();
+ grub_gfxterm_init ();
+
+ grub_keylayouts_init ();
+ grub_at_keyboard_init ();
+
+ grub_terminfo_init ();
+ grub_serial_init ();
+
+ grub_boot_init ();
+}
+
+void
+grub_machine_fini (void)
+{
+}
+
+void
+grub_halt (void)
+{
+ grub_outb (grub_inb (GRUB_CPU_LOONGSON_GPIOCFG)
+ & ~GRUB_CPU_LOONGSON_SHUTDOWN_GPIO, GRUB_CPU_LOONGSON_GPIOCFG);
+
+ grub_millisleep (1500);
+
+ grub_printf ("Shutdown failed\n");
+ grub_refresh ();
+ while (1);
+}
+
+void
+grub_exit (void)
+{
+ grub_halt ();
+}
+
+void
+grub_reboot (void)
+{
+ grub_write_ec (GRUB_MACHINE_EC_COMMAND_REBOOT);
+
+ grub_millisleep (1500);
+
+ grub_printf ("Reboot failed\n");
+ grub_refresh ();
+ while (1);
+}
+
diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
new file mode 100644
index 0000000..1b4cdec
--- /dev/null
+++ b/grub-core/kern/misc.c
@@ -0,0 +1,1019 @@
+/* misc.c - definitions of misc functions */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,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/misc.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <stdarg.h>
+#include <grub/term.h>
+#include <grub/env.h>
+#include <grub/i18n.h>
+
+static int
+grub_vsnprintf_real (char *str, grub_size_t n, const char *fmt, va_list args);
+
+static int
+grub_iswordseparator (int c)
+{
+ return (grub_isspace (c) || c == ',' || c == ';' || c == '|' || c == '&');
+}
+
+/* grub_gettext_dummy is not translating anything. */
+static const char *
+grub_gettext_dummy (const char *s)
+{
+ return s;
+}
+
+const char* (*grub_gettext) (const char *s) = grub_gettext_dummy;
+
+void *
+grub_memmove (void *dest, const void *src, grub_size_t n)
+{
+ char *d = (char *) dest;
+ const char *s = (const char *) src;
+
+ if (d < s)
+ while (n--)
+ *d++ = *s++;
+ else
+ {
+ d += n;
+ s += n;
+
+ while (n--)
+ *--d = *--s;
+ }
+
+ return dest;
+}
+
+#ifndef APPLE_CC
+void *memmove (void *dest, const void *src, grub_size_t n)
+ __attribute__ ((alias ("grub_memmove")));
+/* GCC emits references to memcpy() for struct copies etc. */
+void *memcpy (void *dest, const void *src, grub_size_t n)
+ __attribute__ ((alias ("grub_memmove")));
+#else
+void *memcpy (void *dest, const void *src, grub_size_t n)
+{
+ return grub_memmove (dest, src, n);
+}
+void *memmove (void *dest, const void *src, grub_size_t n)
+{
+ return grub_memmove (dest, src, n);
+}
+#endif
+
+char *
+grub_strcpy (char *dest, const char *src)
+{
+ char *p = dest;
+
+ while ((*p++ = *src++) != '\0')
+ ;
+
+ return dest;
+}
+
+char *
+grub_strncpy (char *dest, const char *src, int c)
+{
+ char *p = dest;
+
+ while ((*p++ = *src++) != '\0' && --c)
+ ;
+
+ return dest;
+}
+
+char *
+grub_stpcpy (char *dest, const char *src)
+{
+ char *d = dest;
+ const char *s = src;
+
+ do
+ *d++ = *s;
+ while (*s++ != '\0');
+
+ return d - 1;
+}
+
+int
+grub_printf (const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start (ap, fmt);
+ ret = grub_vprintf (fmt, ap);
+ va_end (ap);
+
+ return ret;
+}
+
+int
+grub_printf_ (const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start (ap, fmt);
+ ret = grub_vprintf (_(fmt), ap);
+ va_end (ap);
+
+ return ret;
+}
+
+int
+grub_puts_ (const char *s)
+{
+ return grub_puts (_(s));
+}
+
+#if defined (APPLE_CC) && ! defined (GRUB_UTIL)
+int
+grub_err_printf (const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start (ap, fmt);
+ ret = grub_vprintf (fmt, ap);
+ va_end (ap);
+
+ return ret;
+}
+#endif
+
+#if ! defined (APPLE_CC) && ! defined (GRUB_UTIL)
+int grub_err_printf (const char *fmt, ...)
+__attribute__ ((alias("grub_printf")));
+#endif
+
+void
+grub_real_dprintf (const char *file, const int line, const char *condition,
+ const char *fmt, ...)
+{
+ va_list args;
+ const char *debug = grub_env_get ("debug");
+
+ if (! debug)
+ return;
+
+ if (grub_strword (debug, "all") || grub_strword (debug, condition))
+ {
+ grub_printf ("%s:%d: ", file, line);
+ va_start (args, fmt);
+ grub_vprintf (fmt, args);
+ va_end (args);
+ grub_refresh ();
+ }
+}
+
+#define PREALLOC_SIZE 255
+
+int
+grub_vprintf (const char *fmt, va_list args)
+{
+ grub_size_t s;
+ static char buf[PREALLOC_SIZE + 1];
+ char *curbuf = buf;
+ va_list ap2;
+ va_copy (ap2, args);
+
+ s = grub_vsnprintf_real (buf, PREALLOC_SIZE, fmt, args);
+ if (s > PREALLOC_SIZE)
+ {
+ curbuf = grub_malloc (s + 1);
+ if (!curbuf)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ buf[PREALLOC_SIZE - 3] = '.';
+ buf[PREALLOC_SIZE - 2] = '.';
+ buf[PREALLOC_SIZE - 1] = '.';
+ buf[PREALLOC_SIZE] = 0;
+ }
+ else
+ s = grub_vsnprintf_real (curbuf, s, fmt, ap2);
+ }
+
+ grub_xputs (curbuf);
+
+ if (curbuf != buf)
+ grub_free (curbuf);
+
+ return s;
+}
+
+int
+grub_memcmp (const void *s1, const void *s2, grub_size_t n)
+{
+ const char *t1 = s1;
+ const char *t2 = s2;
+
+ while (n--)
+ {
+ if (*t1 != *t2)
+ return (int) *t1 - (int) *t2;
+
+ t1++;
+ t2++;
+ }
+
+ return 0;
+}
+#ifndef APPLE_CC
+int memcmp (const void *s1, const void *s2, grub_size_t n)
+ __attribute__ ((alias ("grub_memcmp")));
+#else
+int memcmp (const void *s1, const void *s2, grub_size_t n)
+{
+ return grub_memcmp (s1, s2, n);
+}
+#endif
+
+int
+grub_strcmp (const char *s1, const char *s2)
+{
+ while (*s1 && *s2)
+ {
+ if (*s1 != *s2)
+ break;
+
+ s1++;
+ s2++;
+ }
+
+ return (int) *s1 - (int) *s2;
+}
+
+int
+grub_strncmp (const char *s1, const char *s2, grub_size_t n)
+{
+ if (n == 0)
+ return 0;
+
+ while (*s1 && *s2 && --n)
+ {
+ if (*s1 != *s2)
+ break;
+
+ s1++;
+ s2++;
+ }
+
+ return (int) *s1 - (int) *s2;
+}
+
+char *
+grub_strchr (const char *s, int c)
+{
+ do
+ {
+ if (*s == c)
+ return (char *) s;
+ }
+ while (*s++);
+
+ return 0;
+}
+
+char *
+grub_strrchr (const char *s, int c)
+{
+ char *p = NULL;
+
+ do
+ {
+ if (*s == c)
+ p = (char *) s;
+ }
+ while (*s++);
+
+ return p;
+}
+
+/* Copied from gnulib.
+ Written by Bruno Haible <bruno@clisp.org>, 2005. */
+char *
+grub_strstr (const char *haystack, const char *needle)
+{
+ /* Be careful not to look at the entire extent of haystack or needle
+ until needed. This is useful because of these two cases:
+ - haystack may be very long, and a match of needle found early,
+ - needle may be very long, and not even a short initial segment of
+ needle may be found in haystack. */
+ if (*needle != '\0')
+ {
+ /* Speed up the following searches of needle by caching its first
+ character. */
+ char b = *needle++;
+
+ for (;; haystack++)
+ {
+ if (*haystack == '\0')
+ /* No match. */
+ return NULL;
+ if (*haystack == b)
+ /* The first character matches. */
+ {
+ const char *rhaystack = haystack + 1;
+ const char *rneedle = needle;
+
+ for (;; rhaystack++, rneedle++)
+ {
+ if (*rneedle == '\0')
+ /* Found a match. */
+ return (char *) haystack;
+ if (*rhaystack == '\0')
+ /* No match. */
+ return NULL;
+ if (*rhaystack != *rneedle)
+ /* Nothing in this round. */
+ break;
+ }
+ }
+ }
+ }
+ else
+ return (char *) haystack;
+}
+
+int
+grub_strword (const char *haystack, const char *needle)
+{
+ const char *n_pos = needle;
+
+ while (grub_iswordseparator (*haystack))
+ haystack++;
+
+ while (*haystack)
+ {
+ /* Crawl both the needle and the haystack word we're on. */
+ while(*haystack && !grub_iswordseparator (*haystack)
+ && *haystack == *n_pos)
+ {
+ haystack++;
+ n_pos++;
+ }
+
+ /* If we reached the end of both words at the same time, the word
+ is found. If not, eat everything in the haystack that isn't the
+ next word (or the end of string) and "reset" the needle. */
+ if ( (!*haystack || grub_iswordseparator (*haystack))
+ && (!*n_pos || grub_iswordseparator (*n_pos)))
+ return 1;
+ else
+ {
+ n_pos = needle;
+ while (*haystack && !grub_iswordseparator (*haystack))
+ haystack++;
+ while (grub_iswordseparator (*haystack))
+ haystack++;
+ }
+ }
+
+ return 0;
+}
+
+int
+grub_isspace (int c)
+{
+ return (c == '\n' || c == '\r' || c == ' ' || c == '\t');
+}
+
+int
+grub_isprint (int c)
+{
+ return (c >= ' ' && c <= '~');
+}
+
+
+unsigned long
+grub_strtoul (const char *str, char **end, int base)
+{
+ unsigned long long num;
+
+ num = grub_strtoull (str, end, base);
+ if (num > ~0UL)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
+ return ~0UL;
+ }
+
+ return (unsigned long) num;
+}
+
+unsigned long long
+grub_strtoull (const char *str, char **end, int base)
+{
+ unsigned long long num = 0;
+ int found = 0;
+
+ /* Skip white spaces. */
+ while (*str && grub_isspace (*str))
+ str++;
+
+ /* Guess the base, if not specified. The prefix `0x' means 16, and
+ the prefix `0' means 8. */
+ if (str[0] == '0')
+ {
+ if (str[1] == 'x')
+ {
+ if (base == 0 || base == 16)
+ {
+ base = 16;
+ str += 2;
+ }
+ }
+ else if (base == 0 && str[1] >= '0' && str[1] <= '7')
+ base = 8;
+ }
+
+ if (base == 0)
+ base = 10;
+
+ while (*str)
+ {
+ unsigned long digit;
+
+ digit = grub_tolower (*str) - '0';
+ if (digit > 9)
+ {
+ digit += '0' - 'a' + 10;
+ if (digit >= (unsigned long) base)
+ break;
+ }
+
+ found = 1;
+
+ /* NUM * BASE + DIGIT > ~0ULL */
+ if (num > grub_divmod64 (~0ULL - digit, base, 0))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
+ return ~0ULL;
+ }
+
+ num = num * base + digit;
+ str++;
+ }
+
+ if (! found)
+ {
+ grub_error (GRUB_ERR_BAD_NUMBER, "unrecognized number");
+ return 0;
+ }
+
+ if (end)
+ *end = (char *) str;
+
+ return num;
+}
+
+char *
+grub_strdup (const char *s)
+{
+ grub_size_t len;
+ char *p;
+
+ len = grub_strlen (s) + 1;
+ p = (char *) grub_malloc (len);
+ if (! p)
+ return 0;
+
+ return grub_memcpy (p, s, len);
+}
+
+char *
+grub_strndup (const char *s, grub_size_t n)
+{
+ grub_size_t len;
+ char *p;
+
+ len = grub_strlen (s);
+ if (len > n)
+ len = n;
+ p = (char *) grub_malloc (len + 1);
+ if (! p)
+ return 0;
+
+ grub_memcpy (p, s, len);
+ p[len] = '\0';
+ return p;
+}
+
+void *
+grub_memset (void *s, int c, grub_size_t len)
+{
+ void *p = s;
+ grub_uint8_t pattern8 = c;
+
+ if (len >= 3 * sizeof (unsigned long))
+ {
+ unsigned long patternl = 0;
+ grub_size_t i;
+
+ for (i = 0; i < sizeof (unsigned long); i++)
+ patternl |= ((unsigned long) pattern8) << (8 * i);
+
+ while (len > 0 && (((grub_addr_t) p) & (sizeof (unsigned long) - 1)))
+ {
+ *(grub_uint8_t *) p = pattern8;
+ p = (grub_uint8_t *) p + 1;
+ len--;
+ }
+ while (len >= sizeof (unsigned long))
+ {
+ *(unsigned long *) p = patternl;
+ p = (unsigned long *) p + 1;
+ len -= sizeof (unsigned long);
+ }
+ }
+
+ while (len > 0)
+ {
+ *(grub_uint8_t *) p = pattern8;
+ p = (grub_uint8_t *) p + 1;
+ len--;
+ }
+
+ return s;
+}
+#ifndef APPLE_CC
+void *memset (void *s, int c, grub_size_t n)
+ __attribute__ ((alias ("grub_memset")));
+#else
+void *memset (void *s, int c, grub_size_t n)
+{
+ return grub_memset (s, c, n);
+}
+#endif
+
+grub_size_t
+grub_strlen (const char *s)
+{
+ const char *p = s;
+
+ while (*p)
+ p++;
+
+ return p - s;
+}
+
+static inline void
+grub_reverse (char *str)
+{
+ char *p = str + grub_strlen (str) - 1;
+
+ while (str < p)
+ {
+ char tmp;
+
+ tmp = *str;
+ *str = *p;
+ *p = tmp;
+ str++;
+ p--;
+ }
+}
+
+/* Divide N by D, return the quotient, and store the remainder in *R. */
+grub_uint64_t
+grub_divmod64_full (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r)
+{
+ /* This algorithm is typically implemented by hardware. The idea
+ is to get the highest bit in N, 64 times, by keeping
+ upper(N * 2^i) = (Q * D + M), where upper
+ represents the high 64 bits in 128-bits space. */
+ unsigned bits = 64;
+ grub_uint64_t q = 0;
+ grub_uint64_t m = 0;
+
+ /* Skip the slow computation if 32-bit arithmetic is possible. */
+ if (n < 0xffffffff && d < 0xffffffff)
+ {
+ if (r)
+ *r = ((grub_uint32_t) n) % (grub_uint32_t) d;
+
+ return ((grub_uint32_t) n) / (grub_uint32_t) d;
+ }
+
+ while (bits--)
+ {
+ m <<= 1;
+
+ if (n & (1ULL << 63))
+ m |= 1;
+
+ q <<= 1;
+ n <<= 1;
+
+ if (m >= d)
+ {
+ q |= 1;
+ m -= d;
+ }
+ }
+
+ if (r)
+ *r = m;
+
+ return q;
+}
+
+/* Convert a long long value to a string. This function avoids 64-bit
+ modular arithmetic or divisions. */
+static char *
+grub_lltoa (char *str, int c, unsigned long long n)
+{
+ unsigned base = (c == 'x') ? 16 : 10;
+ char *p;
+
+ if ((long long) n < 0 && c == 'd')
+ {
+ n = (unsigned long long) (-((long long) n));
+ *str++ = '-';
+ }
+
+ p = str;
+
+ if (base == 16)
+ do
+ {
+ unsigned d = (unsigned) (n & 0xf);
+ *p++ = (d > 9) ? d + 'a' - 10 : d + '0';
+ }
+ while (n >>= 4);
+ else
+ /* BASE == 10 */
+ do
+ {
+ unsigned m;
+
+ n = grub_divmod64 (n, 10, &m);
+ *p++ = m + '0';
+ }
+ while (n);
+
+ *p = 0;
+
+ grub_reverse (str);
+ return p;
+}
+
+static int
+grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt, va_list args)
+{
+ char c;
+ grub_size_t count = 0;
+ auto void write_char (unsigned char ch);
+ auto void write_str (const char *s);
+ auto void write_fill (const char ch, int n);
+
+ void write_char (unsigned char ch)
+ {
+ if (count < max_len)
+ *str++ = ch;
+
+ count++;
+ }
+
+ void write_str (const char *s)
+ {
+ while (*s)
+ write_char (*s++);
+ }
+
+ void write_fill (const char ch, int n)
+ {
+ int i;
+ for (i = 0; i < n; i++)
+ write_char (ch);
+ }
+
+ while ((c = *fmt++) != 0)
+ {
+ if (c != '%')
+ write_char (c);
+ else
+ {
+ char tmp[32];
+ char *p;
+ unsigned int format1 = 0;
+ unsigned int format2 = ~ 0U;
+ char zerofill = ' ';
+ int rightfill = 0;
+ int n;
+ int longfmt = 0;
+ int longlongfmt = 0;
+ int unsig = 0;
+
+ if (*fmt && *fmt =='-')
+ {
+ rightfill = 1;
+ fmt++;
+ }
+
+ p = (char *) fmt;
+ /* Read formatting parameters. */
+ while (*p && grub_isdigit (*p))
+ p++;
+
+ if (p > fmt)
+ {
+ char s[p - fmt + 1];
+ grub_strncpy (s, fmt, p - fmt);
+ s[p - fmt] = 0;
+ if (s[0] == '0')
+ zerofill = '0';
+ format1 = grub_strtoul (s, 0, 10);
+ fmt = p;
+ }
+
+ if (*p && *p == '.')
+ {
+ p++;
+ fmt++;
+ while (*p && grub_isdigit (*p))
+ p++;
+
+ if (p > fmt)
+ {
+ char fstr[p - fmt + 1];
+ grub_strncpy (fstr, fmt, p - fmt);
+ fstr[p - fmt] = 0;
+ format2 = grub_strtoul (fstr, 0, 10);
+ fmt = p;
+ }
+ }
+
+ c = *fmt++;
+ if (c == 'l')
+ {
+ longfmt = 1;
+ c = *fmt++;
+ if (c == 'l')
+ {
+ longlongfmt = 1;
+ c = *fmt++;
+ }
+ }
+
+ switch (c)
+ {
+ case 'p':
+ write_str ("0x");
+ c = 'x';
+ longlongfmt |= (sizeof (void *) == sizeof (long long));
+ /* Fall through. */
+ case 'x':
+ case 'u':
+ unsig = 1;
+ /* Fall through. */
+ case 'd':
+ if (longlongfmt)
+ {
+ long long ll;
+
+ ll = va_arg (args, long long);
+ grub_lltoa (tmp, c, ll);
+ }
+ else if (longfmt && unsig)
+ {
+ unsigned long l = va_arg (args, unsigned long);
+ grub_lltoa (tmp, c, l);
+ }
+ else if (longfmt)
+ {
+ long l = va_arg (args, long);
+ grub_lltoa (tmp, c, l);
+ }
+ else if (unsig)
+ {
+ unsigned u = va_arg (args, unsigned);
+ grub_lltoa (tmp, c, u);
+ }
+ else
+ {
+ n = va_arg (args, int);
+ grub_lltoa (tmp, c, n);
+ }
+ if (! rightfill && grub_strlen (tmp) < format1)
+ write_fill (zerofill, format1 - grub_strlen (tmp));
+ write_str (tmp);
+ if (rightfill && grub_strlen (tmp) < format1)
+ write_fill (zerofill, format1 - grub_strlen (tmp));
+ break;
+
+ case 'c':
+ n = va_arg (args, int);
+ write_char (n & 0xff);
+ break;
+
+ case 'C':
+ {
+ grub_uint32_t code = va_arg (args, grub_uint32_t);
+ int shift;
+ unsigned mask;
+
+ if (code <= 0x7f)
+ {
+ shift = 0;
+ mask = 0;
+ }
+ else if (code <= 0x7ff)
+ {
+ shift = 6;
+ mask = 0xc0;
+ }
+ else if (code <= 0xffff)
+ {
+ shift = 12;
+ mask = 0xe0;
+ }
+ else if (code <= 0x1fffff)
+ {
+ shift = 18;
+ mask = 0xf0;
+ }
+ else if (code <= 0x3ffffff)
+ {
+ shift = 24;
+ mask = 0xf8;
+ }
+ else if (code <= 0x7fffffff)
+ {
+ shift = 30;
+ mask = 0xfc;
+ }
+ else
+ {
+ code = '?';
+ shift = 0;
+ mask = 0;
+ }
+
+ write_char (mask | (code >> shift));
+
+ for (shift -= 6; shift >= 0; shift -= 6)
+ write_char (0x80 | (0x3f & (code >> shift)));
+ }
+ break;
+
+ case 's':
+ p = va_arg (args, char *);
+ if (p)
+ {
+ grub_size_t len = 0;
+ while (len < format2 && p[len])
+ len++;
+
+ if (!rightfill && len < format1)
+ write_fill (zerofill, format1 - len);
+
+ grub_size_t i;
+ for (i = 0; i < len; i++)
+ write_char (*p++);
+
+ if (rightfill && len < format1)
+ write_fill (zerofill, format1 - len);
+ }
+ else
+ write_str ("(null)");
+
+ break;
+
+ default:
+ write_char (c);
+ break;
+ }
+ }
+ }
+
+ *str = '\0';
+
+ return count;
+}
+
+int
+grub_vsnprintf (char *str, grub_size_t n, const char *fmt, va_list ap)
+{
+ grub_size_t ret;
+
+ if (!n)
+ return 0;
+
+ n--;
+
+ ret = grub_vsnprintf_real (str, n, fmt, ap);
+
+ return ret < n ? ret : n;
+}
+
+int
+grub_snprintf (char *str, grub_size_t n, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start (ap, fmt);
+ ret = grub_vsnprintf (str, n, fmt, ap);
+ va_end (ap);
+
+ return ret;
+}
+
+char *
+grub_xvasprintf (const char *fmt, va_list ap)
+{
+ grub_size_t s, as = PREALLOC_SIZE;
+ char *ret;
+
+ while (1)
+ {
+ va_list ap2;
+ va_copy (ap2, ap);
+ ret = grub_malloc (as + 1);
+ if (!ret)
+ return NULL;
+
+ s = grub_vsnprintf_real (ret, as, fmt, ap2);
+ if (s <= as)
+ return ret;
+
+ grub_free (ret);
+ as = s;
+ }
+}
+
+char *
+grub_xasprintf (const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start (ap, fmt);
+ ret = grub_xvasprintf (fmt, ap);
+ va_end (ap);
+
+ return ret;
+}
+
+/* Abort GRUB. This function does not return. */
+void
+grub_abort (void)
+{
+ grub_printf ("\nAborted.");
+
+#ifndef GRUB_UTIL
+ if (grub_term_inputs)
+#endif
+ {
+ grub_printf (" Press any key to exit.");
+ grub_getkey ();
+ }
+
+ grub_exit ();
+}
+
+#if ! defined (APPLE_CC) && !defined (GRUB_UTIL)
+/* GCC emits references to abort(). */
+void abort (void) __attribute__ ((alias ("grub_abort")));
+#endif
+
+#if NEED_ENABLE_EXECUTE_STACK && !defined(GRUB_UTIL) && !defined(GRUB_MACHINE_EMU)
+/* Some gcc versions generate a call to this function
+ in trampolines for nested functions. */
+void __enable_execute_stack (void *addr __attribute__ ((unused)))
+{
+}
+#endif
+
+#if NEED_REGISTER_FRAME_INFO && !defined(GRUB_UTIL)
+void __register_frame_info (void)
+{
+}
+
+void __deregister_frame_info (void)
+{
+}
+#endif
+
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
new file mode 100644
index 0000000..d54f3f2
--- /dev/null
+++ b/grub-core/kern/mm.c
@@ -0,0 +1,572 @@
+/* mm.c - functions for memory manager */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,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/>.
+ */
+
+/*
+ The design of this memory manager.
+
+ This is a simple implementation of malloc with a few extensions. These are
+ the extensions:
+
+ - memalign is implemented efficiently.
+
+ - multiple regions may be used as free space. They may not be
+ contiguous.
+
+ Regions are managed by a singly linked list, and the meta information is
+ stored in the beginning of each region. Space after the meta information
+ is used to allocate memory.
+
+ The memory space is used as cells instead of bytes for simplicity. This
+ is important for some CPUs which may not access multiple bytes at a time
+ when the first byte is not aligned at a certain boundary (typically,
+ 4-byte or 8-byte). The size of each cell is equal to the size of struct
+ grub_mm_header, so the header of each allocated/free block fits into one
+ cell precisely. One cell is 16 bytes on 32-bit platforms and 32 bytes
+ on 64-bit platforms.
+
+ There are two types of blocks: allocated blocks and free blocks.
+
+ In allocated blocks, the header of each block has only its size. Note that
+ this size is based on cells but not on bytes. The header is located right
+ before the returned pointer, that is, the header resides at the previous
+ cell.
+
+ Free blocks constitutes a ring, using a singly linked list. The first free
+ block is pointed to by the meta information of a region. The allocator
+ attempts to pick up the second block instead of the first one. This is
+ a typical optimization against defragmentation, and makes the
+ implementation a bit easier.
+
+ For safety, both allocated blocks and free ones are marked by magic
+ numbers. Whenever anything unexpected is detected, GRUB aborts the
+ operation.
+ */
+
+#include <config.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/mm_private.h>
+
+#ifdef MM_DEBUG
+# undef grub_malloc
+# undef grub_zalloc
+# undef grub_realloc
+# undef grub_free
+# undef grub_memalign
+#endif
+
+
+
+grub_mm_region_t grub_mm_base;
+
+/* Get a header from the pointer PTR, and set *P and *R to a pointer
+ to the header and a pointer to its region, respectively. PTR must
+ be allocated. */
+static void
+get_header_from_pointer (void *ptr, grub_mm_header_t *p, grub_mm_region_t *r)
+{
+ if ((grub_addr_t) ptr & (GRUB_MM_ALIGN - 1))
+ grub_fatal ("unaligned pointer %p", ptr);
+
+ for (*r = grub_mm_base; *r; *r = (*r)->next)
+ if ((grub_addr_t) ptr > (grub_addr_t) ((*r) + 1)
+ && (grub_addr_t) ptr <= (grub_addr_t) ((*r) + 1) + (*r)->size)
+ break;
+
+ if (! *r)
+ grub_fatal ("out of range pointer %p", ptr);
+
+ *p = (grub_mm_header_t) ptr - 1;
+ if ((*p)->magic != GRUB_MM_ALLOC_MAGIC)
+ grub_fatal ("alloc magic is broken at %p", *p);
+}
+
+/* Initialize a region starting from ADDR and whose size is SIZE,
+ to use it as free space. */
+void
+grub_mm_init_region (void *addr, grub_size_t size)
+{
+ grub_mm_header_t h;
+ grub_mm_region_t r, *p, q;
+
+#if 0
+ grub_printf ("Using memory for heap: start=%p, end=%p\n", addr, addr + (unsigned int) size);
+#endif
+
+ /* Allocate a region from the head. */
+ r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
+ size -= (char *) r - (char *) addr + sizeof (*r);
+
+ /* If this region is too small, ignore it. */
+ if (size < GRUB_MM_ALIGN)
+ return;
+
+ h = (grub_mm_header_t) (r + 1);
+ h->next = h;
+ h->magic = GRUB_MM_FREE_MAGIC;
+ h->size = (size >> GRUB_MM_ALIGN_LOG2);
+
+ r->first = h;
+ r->pre_size = (grub_addr_t) r - (grub_addr_t) addr;
+ r->size = (h->size << GRUB_MM_ALIGN_LOG2);
+
+ /* Find where to insert this region. Put a smaller one before bigger ones,
+ to prevent fragmentation. */
+ for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p)
+ if (q->size > r->size)
+ break;
+
+ *p = r;
+ r->next = q;
+}
+
+/* Allocate the number of units N with the alignment ALIGN from the ring
+ buffer starting from *FIRST. ALIGN must be a power of two. Both N and
+ ALIGN are in units of GRUB_MM_ALIGN. Return a non-NULL if successful,
+ otherwise return NULL. */
+static void *
+grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
+{
+ grub_mm_header_t p, q;
+
+ /* When everything is allocated side effect is that *first will have alloc
+ magic marked, meaning that there is no room in this region. */
+ if ((*first)->magic == GRUB_MM_ALLOC_MAGIC)
+ return 0;
+
+ /* Try to search free slot for allocation in this memory region. */
+ for (q = *first, p = q->next; ; q = p, p = p->next)
+ {
+ grub_off_t extra;
+
+ extra = ((grub_addr_t) (p + 1) >> GRUB_MM_ALIGN_LOG2) % align;
+ if (extra)
+ extra = align - extra;
+
+ if (! p)
+ grub_fatal ("null in the ring");
+
+ if (p->magic != GRUB_MM_FREE_MAGIC)
+ grub_fatal ("free magic is broken at %p: 0x%x", p, p->magic);
+
+ if (p->size >= n + extra)
+ {
+ extra += (p->size - extra - n) & (~(align - 1));
+ if (extra == 0 && p->size == n)
+ {
+ /* There is no special alignment requirement and memory block
+ is complete match.
+
+ 1. Just mark memory block as allocated and remove it from
+ free list.
+
+ Result:
+ +---------------+ previous block's next
+ | alloc, size=n | |
+ +---------------+ v
+ */
+ q->next = p->next;
+ }
+ else if (align == 1 || p->size == n + extra)
+ {
+ /* There might be alignment requirement, when taking it into
+ account memory block fits in.
+
+ 1. Allocate new area at end of memory block.
+ 2. Reduce size of available blocks from original node.
+ 3. Mark new area as allocated and "remove" it from free
+ list.
+
+ Result:
+ +---------------+
+ | free, size-=n | next --+
+ +---------------+ |
+ | alloc, size=n | |
+ +---------------+ v
+ */
+
+ p->size -= n;
+ p += p->size;
+ }
+ else if (extra == 0)
+ {
+ grub_mm_header_t r;
+
+ r = p + extra + n;
+ r->magic = GRUB_MM_FREE_MAGIC;
+ r->size = p->size - extra - n;
+ r->next = p->next;
+ q->next = r;
+
+ if (q == p)
+ {
+ q = r;
+ r->next = r;
+ }
+ }
+ else
+ {
+ /* There is alignment requirement and there is room in memory
+ block. Split memory block to three pieces.
+
+ 1. Create new memory block right after section being
+ allocated. Mark it as free.
+ 2. Add new memory block to free chain.
+ 3. Mark current memory block having only extra blocks.
+ 4. Advance to aligned block and mark that as allocated and
+ "remove" it from free list.
+
+ Result:
+ +------------------------------+
+ | free, size=extra | next --+
+ +------------------------------+ |
+ | alloc, size=n | |
+ +------------------------------+ |
+ | free, size=orig.size-extra-n | <------+, next --+
+ +------------------------------+ v
+ */
+ grub_mm_header_t r;
+
+ r = p + extra + n;
+ r->magic = GRUB_MM_FREE_MAGIC;
+ r->size = p->size - extra - n;
+ r->next = p;
+
+ p->size = extra;
+ q->next = r;
+ p += extra;
+ }
+
+ p->magic = GRUB_MM_ALLOC_MAGIC;
+ p->size = n;
+
+ /* Mark find as a start marker for next allocation to fasten it.
+ This will have side effect of fragmenting memory as small
+ pieces before this will be un-used. */
+ *first = q;
+
+ return p + 1;
+ }
+
+ /* Search was completed without result. */
+ if (p == *first)
+ break;
+ }
+
+ return 0;
+}
+
+/* Allocate SIZE bytes with the alignment ALIGN and return the pointer. */
+void *
+grub_memalign (grub_size_t align, grub_size_t size)
+{
+ grub_mm_region_t r;
+ grub_size_t n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1;
+ int count = 0;
+
+ if (!grub_mm_base)
+ goto fail;
+
+ align = (align >> GRUB_MM_ALIGN_LOG2);
+ if (align == 0)
+ align = 1;
+
+ again:
+
+ for (r = grub_mm_base; r; r = r->next)
+ {
+ void *p;
+
+ p = grub_real_malloc (&(r->first), n, align);
+ if (p)
+ return p;
+ }
+
+ /* If failed, increase free memory somehow. */
+ switch (count)
+ {
+ case 0:
+ /* Invalidate disk caches. */
+ grub_disk_cache_invalidate_all ();
+ count++;
+ goto again;
+
+#if 0
+ case 1:
+ /* Unload unneeded modules. */
+ grub_dl_unload_unneeded ();
+ count++;
+ goto again;
+#endif
+
+ default:
+ break;
+ }
+
+ fail:
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
+ return 0;
+}
+
+/* Allocate SIZE bytes and return the pointer. */
+void *
+grub_malloc (grub_size_t size)
+{
+ return grub_memalign (0, size);
+}
+
+/* Allocate SIZE bytes, clear them and return the pointer. */
+void *
+grub_zalloc (grub_size_t size)
+{
+ void *ret;
+
+ ret = grub_memalign (0, size);
+ if (ret)
+ grub_memset (ret, 0, size);
+
+ return ret;
+}
+
+/* Deallocate the pointer PTR. */
+void
+grub_free (void *ptr)
+{
+ grub_mm_header_t p;
+ grub_mm_region_t r;
+
+ if (! ptr)
+ return;
+
+ get_header_from_pointer (ptr, &p, &r);
+
+ if (r->first->magic == GRUB_MM_ALLOC_MAGIC)
+ {
+ p->magic = GRUB_MM_FREE_MAGIC;
+ r->first = p->next = p;
+ }
+ else
+ {
+ grub_mm_header_t q;
+
+#if 0
+ q = r->first;
+ do
+ {
+ grub_printf ("%s:%d: q=%p, q->size=0x%x, q->magic=0x%x\n",
+ GRUB_FILE, __LINE__, q, q->size, q->magic);
+ q = q->next;
+ }
+ while (q != r->first);
+#endif
+
+ for (q = r->first; q >= p || q->next <= p; q = q->next)
+ {
+ if (q->magic != GRUB_MM_FREE_MAGIC)
+ grub_fatal ("free magic is broken at %p: 0x%x", q, q->magic);
+
+ if (q >= q->next && (q < p || q->next > p))
+ break;
+ }
+
+ p->magic = GRUB_MM_FREE_MAGIC;
+ p->next = q->next;
+ q->next = p;
+
+ if (p + p->size == p->next)
+ {
+ if (p->next == q)
+ q = p;
+
+ p->next->magic = 0;
+ p->size += p->next->size;
+ p->next = p->next->next;
+ }
+
+ if (q + q->size == p)
+ {
+ p->magic = 0;
+ q->size += p->size;
+ q->next = p->next;
+ }
+
+ r->first = q;
+ }
+}
+
+/* Reallocate SIZE bytes and return the pointer. The contents will be
+ the same as that of PTR. */
+void *
+grub_realloc (void *ptr, grub_size_t size)
+{
+ grub_mm_header_t p;
+ grub_mm_region_t r;
+ void *q;
+ grub_size_t n;
+
+ if (! ptr)
+ return grub_malloc (size);
+
+ if (! size)
+ {
+ grub_free (ptr);
+ return 0;
+ }
+
+ /* FIXME: Not optimal. */
+ n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1;
+ get_header_from_pointer (ptr, &p, &r);
+
+ if (p->size >= n)
+ return ptr;
+
+ q = grub_malloc (size);
+ if (! q)
+ return q;
+
+ grub_memcpy (q, ptr, size);
+ grub_free (ptr);
+ return q;
+}
+
+#ifdef MM_DEBUG
+int grub_mm_debug = 0;
+
+void
+grub_mm_dump_free (void)
+{
+ grub_mm_region_t r;
+
+ for (r = grub_mm_base; r; r = r->next)
+ {
+ grub_mm_header_t p;
+
+ /* Follow the free list. */
+ p = r->first;
+ do
+ {
+ if (p->magic != GRUB_MM_FREE_MAGIC)
+ grub_fatal ("free magic is broken at %p: 0x%x", p, p->magic);
+
+ grub_printf ("F:%p:%u:%p\n",
+ p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2, p->next);
+ p = p->next;
+ }
+ while (p != r->first);
+ }
+
+ grub_printf ("\n");
+}
+
+void
+grub_mm_dump (unsigned lineno)
+{
+ grub_mm_region_t r;
+
+ grub_printf ("called at line %u\n", lineno);
+ for (r = grub_mm_base; r; r = r->next)
+ {
+ grub_mm_header_t p;
+
+ for (p = (grub_mm_header_t) ALIGN_UP ((grub_addr_t) (r + 1),
+ GRUB_MM_ALIGN);
+ (grub_addr_t) p < (grub_addr_t) (r+1) + r->size;
+ p++)
+ {
+ switch (p->magic)
+ {
+ case GRUB_MM_FREE_MAGIC:
+ grub_printf ("F:%p:%u:%p\n",
+ p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2, p->next);
+ break;
+ case GRUB_MM_ALLOC_MAGIC:
+ grub_printf ("A:%p:%u\n", p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2);
+ break;
+ }
+ }
+ }
+
+ grub_printf ("\n");
+}
+
+void *
+grub_debug_malloc (const char *file, int line, grub_size_t size)
+{
+ void *ptr;
+
+ if (grub_mm_debug)
+ grub_printf ("%s:%d: malloc (0x%zx) = ", file, line, size);
+ ptr = grub_malloc (size);
+ if (grub_mm_debug)
+ grub_printf ("%p\n", ptr);
+ return ptr;
+}
+
+void *
+grub_debug_zalloc (const char *file, int line, grub_size_t size)
+{
+ void *ptr;
+
+ if (grub_mm_debug)
+ grub_printf ("%s:%d: zalloc (0x%zx) = ", file, line, size);
+ ptr = grub_zalloc (size);
+ if (grub_mm_debug)
+ grub_printf ("%p\n", ptr);
+ return ptr;
+}
+
+void
+grub_debug_free (const char *file, int line, void *ptr)
+{
+ if (grub_mm_debug)
+ grub_printf ("%s:%d: free (%p)\n", file, line, ptr);
+ grub_free (ptr);
+}
+
+void *
+grub_debug_realloc (const char *file, int line, void *ptr, grub_size_t size)
+{
+ if (grub_mm_debug)
+ grub_printf ("%s:%d: realloc (%p, 0x%zx) = ", file, line, ptr, size);
+ ptr = grub_realloc (ptr, size);
+ if (grub_mm_debug)
+ grub_printf ("%p\n", ptr);
+ return ptr;
+}
+
+void *
+grub_debug_memalign (const char *file, int line, grub_size_t align,
+ grub_size_t size)
+{
+ void *ptr;
+
+ if (grub_mm_debug)
+ grub_printf ("%s:%d: memalign (0x%zx, 0x%zx) = ",
+ file, line, align, size);
+ ptr = grub_memalign (align, size);
+ if (grub_mm_debug)
+ grub_printf ("%p\n", ptr);
+ return ptr;
+}
+
+#endif /* MM_DEBUG */
diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
new file mode 100644
index 0000000..6370a7b
--- /dev/null
+++ b/grub-core/kern/parser.c
@@ -0,0 +1,267 @@
+/* parser.c - the part of the parser that can return partial tokens */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/parser.h>
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+
+/* All the possible state transitions on the command line. If a
+ transition can not be found, it is assumed that there is no
+ transition and keep_value is assumed to be 1. */
+static struct grub_parser_state_transition state_transitions[] = {
+ {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_QUOTE, '\'', 0},
+ {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_DQUOTE, '\"', 0},
+ {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_VAR, '$', 0},
+ {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_ESC, '\\', 0},
+
+ {GRUB_PARSER_STATE_ESC, GRUB_PARSER_STATE_TEXT, 0, 1},
+
+ {GRUB_PARSER_STATE_QUOTE, GRUB_PARSER_STATE_TEXT, '\'', 0},
+
+ {GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_TEXT, '\"', 0},
+ {GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_QVAR, '$', 0},
+
+ {GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME2, '{', 0},
+ {GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME, 0, 1},
+ {GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, ' ', 1},
+ {GRUB_PARSER_STATE_VARNAME2, GRUB_PARSER_STATE_TEXT, '}', 0},
+
+ {GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0},
+ {GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME, 0, 1},
+ {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_TEXT, '\"', 0},
+ {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, ' ', 1},
+ {GRUB_PARSER_STATE_QVARNAME2, GRUB_PARSER_STATE_DQUOTE, '}', 0},
+
+ {0, 0, 0, 0}
+};
+
+
+/* Determines the state following STATE, determined by C. */
+grub_parser_state_t
+grub_parser_cmdline_state (grub_parser_state_t state, char c, char *result)
+{
+ struct grub_parser_state_transition *transition;
+ struct grub_parser_state_transition default_transition;
+
+ default_transition.to_state = state;
+ default_transition.keep_value = 1;
+
+ /* Look for a good translation. */
+ for (transition = state_transitions; transition->from_state; transition++)
+ {
+ if (transition->from_state != state)
+ continue;
+ /* An exact match was found, use it. */
+ if (transition->input == c)
+ break;
+
+ if (transition->input == ' ' && !grub_isalpha (c)
+ && !grub_isdigit (c) && c != '_')
+ break;
+
+ /* A less perfect match was found, use this one if no exact
+ match can be found. */
+ if (transition->input == 0)
+ break;
+ }
+
+ if (!transition->from_state)
+ transition = &default_transition;
+
+ if (transition->keep_value)
+ *result = c;
+ else
+ *result = 0;
+ return transition->to_state;
+}
+
+
+grub_err_t
+grub_parser_split_cmdline (const char *cmdline, grub_reader_getline_t getline,
+ int *argc, char ***argv)
+{
+ grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
+ /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
+ allocated. */
+ char buffer[1024];
+ char *bp = buffer;
+ char *rd = (char *) cmdline;
+ char varname[200];
+ char *vp = varname;
+ char *args;
+ int i;
+
+ auto int check_varstate (grub_parser_state_t s);
+
+ int check_varstate (grub_parser_state_t s)
+ {
+ return (s == GRUB_PARSER_STATE_VARNAME
+ || s == GRUB_PARSER_STATE_VARNAME2
+ || s == GRUB_PARSER_STATE_QVARNAME
+ || s == GRUB_PARSER_STATE_QVARNAME2);
+ }
+
+ auto void add_var (grub_parser_state_t newstate);
+
+ void add_var (grub_parser_state_t newstate)
+ {
+ char *val;
+
+ /* Check if a variable was being read in and the end of the name
+ was reached. */
+ if (!(check_varstate (state) && !check_varstate (newstate)))
+ return;
+
+ *(vp++) = '\0';
+ val = grub_env_get (varname);
+ vp = varname;
+ if (!val)
+ return;
+
+ /* Insert the contents of the variable in the buffer. */
+ for (; *val; val++)
+ *(bp++) = *val;
+ }
+
+ *argc = 0;
+ do
+ {
+ if (!rd || !*rd)
+ {
+ if (getline)
+ getline (&rd, 1);
+ else
+ break;
+ }
+
+ if (!rd)
+ break;
+
+ for (; *rd; rd++)
+ {
+ grub_parser_state_t newstate;
+ char use;
+
+ newstate = grub_parser_cmdline_state (state, *rd, &use);
+
+ /* If a variable was being processed and this character does
+ not describe the variable anymore, write the variable to
+ the buffer. */
+ add_var (newstate);
+
+ if (check_varstate (newstate))
+ {
+ if (use)
+ *(vp++) = use;
+ }
+ else
+ {
+ if (newstate == GRUB_PARSER_STATE_TEXT
+ && state != GRUB_PARSER_STATE_ESC && use == ' ')
+ {
+ /* Don't add more than one argument if multiple
+ spaces are used. */
+ if (bp != buffer && *(bp - 1))
+ {
+ *(bp++) = '\0';
+ (*argc)++;
+ }
+ }
+ else if (use)
+ *(bp++) = use;
+ }
+ state = newstate;
+ }
+ }
+ while (state != GRUB_PARSER_STATE_TEXT && !check_varstate (state));
+
+ /* A special case for when the last character was part of a
+ variable. */
+ add_var (GRUB_PARSER_STATE_TEXT);
+
+ if (bp != buffer && *(bp - 1))
+ {
+ *(bp++) = '\0';
+ (*argc)++;
+ }
+
+ /* Reserve memory for the return values. */
+ args = grub_malloc (bp - buffer);
+ if (!args)
+ return grub_errno;
+ grub_memcpy (args, buffer, bp - buffer);
+
+ *argv = grub_malloc (sizeof (char *) * (*argc + 1));
+ if (!*argv)
+ {
+ grub_free (args);
+ return grub_errno;
+ }
+
+ /* The arguments are separated with 0's, setup argv so it points to
+ the right values. */
+ bp = args;
+ for (i = 0; i < *argc; i++)
+ {
+ (*argv)[i] = bp;
+ while (*bp)
+ bp++;
+ bp++;
+ }
+
+ return 0;
+}
+
+grub_err_t
+grub_parser_execute (char *source)
+{
+ auto grub_err_t getline (char **line, int cont);
+ grub_err_t getline (char **line, int cont __attribute__ ((unused)))
+ {
+ char *p;
+
+ if (!source)
+ {
+ *line = 0;
+ return 0;
+ }
+
+ p = grub_strchr (source, '\n');
+
+ if (p)
+ *line = grub_strndup (source, p - source);
+ else
+ *line = grub_strdup (source);
+ source = p ? p + 1 : 0;
+ return 0;
+ }
+
+ while (source)
+ {
+ char *line;
+
+ getline (&line, 0);
+ grub_rescue_parse_line (line, getline);
+ grub_free (line);
+ }
+
+ return grub_errno;
+}
diff --git a/grub-core/kern/partition.c b/grub-core/kern/partition.c
new file mode 100644
index 0000000..a2f5dd7
--- /dev/null
+++ b/grub-core/kern/partition.c
@@ -0,0 +1,253 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2004,2007 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/misc.h>
+#include <grub/mm.h>
+#include <grub/partition.h>
+#include <grub/disk.h>
+
+#ifdef GRUB_UTIL
+#include <grub/util/misc.h>
+#endif
+
+grub_partition_map_t grub_partition_map_list;
+
+/*
+ * Checks that disk->partition contains part. This function assumes that the
+ * start of part is relative to the start of disk->partition. Returns 1 if
+ * disk->partition is null.
+ */
+static int
+grub_partition_check_containment (const grub_disk_t disk,
+ const grub_partition_t part)
+{
+ if (disk->partition == NULL)
+ return 1;
+
+ if (part->start + part->len > disk->partition->len)
+ {
+ char *partname;
+
+ partname = grub_partition_get_name (disk->partition);
+ grub_dprintf ("partition", "sub-partition %s%d of (%s,%s) ends after parent.\n",
+ part->partmap->name, part->number + 1, disk->name, partname);
+#ifdef GRUB_UTIL
+ grub_util_warn ("Discarding improperly nested partition (%s,%s,%s%d)",
+ disk->name, partname, part->partmap->name, part->number + 1);
+#endif
+ grub_free (partname);
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static grub_partition_t
+grub_partition_map_probe (const grub_partition_map_t partmap,
+ grub_disk_t disk, int partnum)
+{
+ grub_partition_t p = 0;
+
+ auto int find_func (grub_disk_t d, const grub_partition_t partition);
+
+ int find_func (grub_disk_t dsk,
+ const grub_partition_t partition)
+ {
+ if (partnum != partition->number)
+ return 0;
+
+ if (!(grub_partition_check_containment (dsk, partition)))
+ return 0;
+
+ p = (grub_partition_t) grub_malloc (sizeof (*p));
+ if (! p)
+ return 1;
+
+ grub_memcpy (p, partition, sizeof (*p));
+ return 1;
+ }
+
+ partmap->iterate (disk, find_func);
+ if (grub_errno)
+ goto fail;
+
+ return p;
+
+ fail:
+ grub_free (p);
+ return 0;
+}
+
+grub_partition_t
+grub_partition_probe (struct grub_disk *disk, const char *str)
+{
+ grub_partition_t part = 0;
+ grub_partition_t curpart = 0;
+ grub_partition_t tail;
+ const char *ptr;
+
+ part = tail = disk->partition;
+
+ for (ptr = str; *ptr;)
+ {
+ grub_partition_map_t partmap;
+ int num;
+ const char *partname, *partname_end;
+
+ partname = ptr;
+ while (*ptr && grub_isalpha (*ptr))
+ ptr++;
+ partname_end = ptr;
+ num = grub_strtoul (ptr, (char **) &ptr, 0) - 1;
+
+ curpart = 0;
+ /* Use the first partition map type found. */
+ FOR_PARTITION_MAPS(partmap)
+ {
+ if (partname_end != partname &&
+ (grub_strncmp (partmap->name, partname, partname_end - partname)
+ != 0 || partmap->name[partname_end - partname] != 0))
+ continue;
+
+ disk->partition = part;
+ curpart = grub_partition_map_probe (partmap, disk, num);
+ disk->partition = tail;
+ if (curpart)
+ break;
+
+ if (grub_errno == GRUB_ERR_BAD_PART_TABLE)
+ {
+ /* Continue to next partition map type. */
+ grub_errno = GRUB_ERR_NONE;
+ continue;
+ }
+
+ break;
+ }
+
+ if (! curpart)
+ {
+ while (part)
+ {
+ curpart = part->parent;
+ grub_free (part);
+ part = curpart;
+ }
+ return 0;
+ }
+ curpart->parent = part;
+ part = curpart;
+ if (! ptr || *ptr != ',')
+ break;
+ ptr++;
+ }
+
+ return part;
+}
+
+int
+grub_partition_iterate (struct grub_disk *disk,
+ int (*hook) (grub_disk_t disk,
+ const grub_partition_t partition))
+{
+ int ret = 0;
+
+ auto int part_iterate (grub_disk_t dsk, const grub_partition_t p);
+
+ int part_iterate (grub_disk_t dsk,
+ const grub_partition_t partition)
+ {
+ struct grub_partition p = *partition;
+
+ if (!(grub_partition_check_containment (dsk, partition)))
+ return 0;
+
+ p.parent = dsk->partition;
+ dsk->partition = 0;
+ if (hook (dsk, &p))
+ {
+ ret = 1;
+ return 1;
+ }
+ if (p.start != 0)
+ {
+ const struct grub_partition_map *partmap;
+ dsk->partition = &p;
+ FOR_PARTITION_MAPS(partmap)
+ {
+ grub_err_t err;
+ err = partmap->iterate (dsk, part_iterate);
+ if (err)
+ grub_errno = GRUB_ERR_NONE;
+ if (ret)
+ break;
+ }
+ }
+ dsk->partition = p.parent;
+ return ret;
+ }
+
+ {
+ const struct grub_partition_map *partmap;
+ FOR_PARTITION_MAPS(partmap)
+ {
+ grub_err_t err;
+ err = partmap->iterate (disk, part_iterate);
+ if (err)
+ grub_errno = GRUB_ERR_NONE;
+ if (ret)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+char *
+grub_partition_get_name (const grub_partition_t partition)
+{
+ char *out = 0;
+ int curlen = 0;
+ grub_partition_t part;
+ for (part = partition; part; part = part->parent)
+ {
+ /* Even on 64-bit machines this buffer is enough to hold
+ longest number. */
+ char buf[grub_strlen (part->partmap->name) + 25];
+ int strl;
+ grub_snprintf (buf, sizeof (buf), "%s%d", part->partmap->name,
+ part->number + 1);
+ strl = grub_strlen (buf);
+ if (curlen)
+ {
+ out = grub_realloc (out, curlen + strl + 2);
+ grub_memcpy (out + strl + 1, out, curlen);
+ out[curlen + 1 + strl] = 0;
+ grub_memcpy (out, buf, strl);
+ out[strl] = ',';
+ curlen = curlen + 1 + strl;
+ }
+ else
+ {
+ curlen = strl;
+ out = grub_strdup (buf);
+ }
+ }
+ return out;
+}
diff --git a/grub-core/kern/powerpc/cache.S b/grub-core/kern/powerpc/cache.S
new file mode 100644
index 0000000..d85e68d
--- /dev/null
+++ b/grub-core/kern/powerpc/cache.S
@@ -0,0 +1,26 @@
+/* cache.S - Flush the processor cache for a specific region. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2004,2007,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/>.
+ */
+
+ .text
+
+ .align 2
+ .globl grub_arch_sync_caches
+grub_arch_sync_caches:
+#include "cache_flush.S"
+ blr
diff --git a/grub-core/kern/powerpc/cache_flush.S b/grub-core/kern/powerpc/cache_flush.S
new file mode 100644
index 0000000..1410f78
--- /dev/null
+++ b/grub-core/kern/powerpc/cache_flush.S
@@ -0,0 +1,43 @@
+/* cache.S - Flush the processor cache for a specific region. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2004,2007,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/>.
+ */
+
+#undef CACHE_LINE_BYTES
+#define CACHE_LINE_BYTES 32
+
+ /* `address' may not be CACHE_LINE_BYTES-aligned. */
+ andi. 6, 3, CACHE_LINE_BYTES - 1 /* Find the misalignment. */
+ add 4, 4, 6 /* Adjust `size' to compensate. */
+
+ /* Force the dcache lines to memory. */
+ li 5, 0
+1: dcbst 5, 3
+ addi 5, 5, CACHE_LINE_BYTES
+ cmpw 5, 4
+ blt 1b
+ sync /* Force all dcbsts to complete. */
+
+ /* Invalidate the icache lines. */
+ li 5, 0
+1: icbi 5, 3
+ addi 5, 5, CACHE_LINE_BYTES
+ cmpw 5, 4
+ blt 1b
+ sync /* Force all icbis to complete. */
+ isync /* Discard partially executed instructions that were
+ loaded from the invalid icache. */
diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c
new file mode 100644
index 0000000..ad19e56
--- /dev/null
+++ b/grub-core/kern/powerpc/dl.c
@@ -0,0 +1,136 @@
+/* dl.c - arch-dependent part of loadable module support */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,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/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+
+/* Check if EHDR is a valid ELF header. */
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+ Elf_Ehdr *e = ehdr;
+
+ /* Check the magic numbers. */
+ if (e->e_ident[EI_CLASS] != ELFCLASS32
+ || e->e_ident[EI_DATA] != ELFDATA2MSB
+ || e->e_machine != EM_PPC)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid arch specific ELF magic");
+
+ return GRUB_ERR_NONE;
+}
+
+
+/* Relocate symbols. */
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+{
+ Elf_Ehdr *e = ehdr;
+ Elf_Shdr *s;
+ Elf_Word entsize;
+ unsigned i;
+
+ /* Find a symbol table. */
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+
+ if (i == e->e_shnum)
+ return grub_error (GRUB_ERR_BAD_MODULE, "no symtab found");
+
+ entsize = s->sh_entsize;
+
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_RELA)
+ {
+ grub_dl_segment_t seg;
+
+ /* Find the target segment. */
+ for (seg = mod->segment; seg; seg = seg->next)
+ if (seg->section == s->sh_info)
+ break;
+
+ if (seg)
+ {
+ Elf_Rela *rel, *max;
+
+ for (rel = (Elf_Rela *) ((char *) e + s->sh_offset),
+ max = rel + s->sh_size / s->sh_entsize;
+ rel < max;
+ rel++)
+ {
+ Elf_Word *addr;
+ Elf_Sym *sym;
+ grub_uint32_t value;
+
+ if (seg->size < rel->r_offset)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "reloc offset is out of the segment");
+
+ addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+ sym = (Elf_Sym *) ((char *) mod->symtab
+ + entsize * ELF_R_SYM (rel->r_info));
+
+ /* On the PPC the value does not have an explicit
+ addend, add it. */
+ value = sym->st_value + rel->r_addend;
+ switch (ELF_R_TYPE (rel->r_info))
+ {
+ case R_PPC_ADDR16_LO:
+ *(Elf_Half *) addr = value;
+ break;
+
+ case R_PPC_REL24:
+ {
+ Elf_Sword delta = value - (Elf_Word) addr;
+
+ if (delta << 6 >> 6 != delta)
+ return grub_error (GRUB_ERR_BAD_MODULE, "relocation overflow");
+ *addr = (*addr & 0xfc000003) | (delta & 0x3fffffc);
+ break;
+ }
+
+ case R_PPC_ADDR16_HA:
+ *(Elf_Half *) addr = (value + 0x8000) >> 16;
+ break;
+
+ case R_PPC_ADDR32:
+ *addr = value;
+ break;
+
+ case R_PPC_REL32:
+ *addr = value - (Elf_Word) addr;
+ break;
+
+ default:
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "this relocation (%d) is not implemented yet",
+ ELF_R_TYPE (rel->r_info));
+ }
+ }
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/kern/powerpc/ieee1275/startup.S b/grub-core/kern/powerpc/ieee1275/startup.S
new file mode 100644
index 0000000..0b1c233
--- /dev/null
+++ b/grub-core/kern/powerpc/ieee1275/startup.S
@@ -0,0 +1,64 @@
+/* startup.S - Startup code for the PowerPC. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,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/>.
+ */
+
+#include <grub/symbol.h>
+#include <grub/offsets.h>
+
+.extern __bss_start
+.extern _end
+
+ .text
+ .align 2
+ .globl start, _start
+start:
+_start:
+ b codestart
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX
+
+VARIABLE(grub_prefix)
+ /* to be filled by grub-mkimage */
+
+ /*
+ * Leave some breathing room for the prefix.
+ */
+
+ . = _start + GRUB_KERNEL_MACHINE_PREFIX_END
+
+codestart:
+ li 2, 0
+ li 13, 0
+
+ /* Stage1 won't zero BSS for us. In other cases, why not do it again? */
+ lis 6, (__bss_start - 4)@h
+ ori 6, 6, (__bss_start - 4)@l
+ lis 7, (_end - 4)@h
+ ori 7, 7, (_end - 4)@l
+ subf 7, 6, 7
+ srwi 7, 7, 2 /* We store 4 bytes at a time. */
+ mtctr 7
+2: stwu 2, 4(6) /* We know r2 is already 0 from above. */
+ bdnz 2b
+
+ /* Store r5 in grub_ieee1275_entry_fn. */
+ lis 9, grub_ieee1275_entry_fn@ha
+ stw 5, grub_ieee1275_entry_fn@l(9)
+
+ bl grub_main
+1: b 1b
diff --git a/grub-core/kern/rescue_parser.c b/grub-core/kern/rescue_parser.c
new file mode 100644
index 0000000..9c85ba6
--- /dev/null
+++ b/grub-core/kern/rescue_parser.c
@@ -0,0 +1,76 @@
+/* rescue_parser.c - rescue mode parser */
+/*
+ * 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/types.h>
+#include <grub/mm.h>
+#include <grub/env.h>
+#include <grub/parser.h>
+#include <grub/misc.h>
+#include <grub/command.h>
+
+grub_err_t
+grub_rescue_parse_line (char *line, grub_reader_getline_t getline)
+{
+ char *name;
+ int n;
+ grub_command_t cmd;
+ char **args;
+
+ if (grub_parser_split_cmdline (line, getline, &n, &args) || n < 0)
+ return grub_errno;
+
+ if (n == 0)
+ return GRUB_ERR_NONE;
+
+ /* In case of an assignment set the environment accordingly
+ instead of calling a function. */
+ if (n == 1 && grub_strchr (line, '='))
+ {
+ char *val = grub_strchr (args[0], '=');
+ val[0] = 0;
+ grub_env_set (args[0], val + 1);
+ val[0] = '=';
+ goto quit;
+ }
+
+ /* Get the command name. */
+ name = args[0];
+
+ /* If nothing is specified, restart. */
+ if (*name == '\0')
+ goto quit;
+
+ cmd = grub_command_find (name);
+ if (cmd)
+ {
+ (cmd->func) (cmd, n - 1, &args[1]);
+ }
+ else
+ {
+ grub_printf ("Unknown command `%s'\n", name);
+ if (grub_command_find ("help"))
+ grub_printf ("Try `help' for usage\n");
+ }
+
+ quit:
+ grub_free (args[0]);
+ grub_free (args);
+
+ return grub_errno;
+}
diff --git a/grub-core/kern/rescue_reader.c b/grub-core/kern/rescue_reader.c
new file mode 100644
index 0000000..a810085
--- /dev/null
+++ b/grub-core/kern/rescue_reader.c
@@ -0,0 +1,97 @@
+/* rescue_reader.c - rescue mode reader */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/types.h>
+#include <grub/reader.h>
+#include <grub/parser.h>
+#include <grub/misc.h>
+#include <grub/term.h>
+#include <grub/mm.h>
+
+#define GRUB_RESCUE_BUF_SIZE 256
+
+static char linebuf[GRUB_RESCUE_BUF_SIZE];
+
+/* Prompt to input a command and read the line. */
+static grub_err_t
+grub_rescue_read_line (char **line, int cont)
+{
+ int c;
+ int pos = 0;
+ char str[4];
+
+ grub_printf ((cont) ? "> " : "grub rescue> ");
+ grub_memset (linebuf, 0, GRUB_RESCUE_BUF_SIZE);
+
+ while ((c = grub_getkey ()) != '\n' && c != '\r')
+ {
+ if (grub_isprint (c))
+ {
+ if (pos < GRUB_RESCUE_BUF_SIZE - 1)
+ {
+ str[0] = c;
+ str[1] = 0;
+ linebuf[pos++] = c;
+ grub_xputs (str);
+ }
+ }
+ else if (c == '\b')
+ {
+ if (pos > 0)
+ {
+ str[0] = c;
+ str[1] = ' ';
+ str[2] = c;
+ str[3] = 0;
+ linebuf[--pos] = 0;
+ grub_xputs (str);
+ }
+ }
+ grub_refresh ();
+ }
+
+ grub_xputs ("\n");
+ grub_refresh ();
+
+ *line = grub_strdup (linebuf);
+
+ return 0;
+}
+
+void
+grub_rescue_run (void)
+{
+ grub_printf ("Entering rescue mode...\n");
+
+ while (1)
+ {
+ char *line;
+
+ /* Print an error, if any. */
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+
+ grub_rescue_read_line (&line, 0);
+ if (! line || line[0] == '\0')
+ continue;
+
+ grub_rescue_parse_line (line, grub_rescue_read_line);
+ grub_free (line);
+ }
+}
diff --git a/grub-core/kern/sparc64/cache.S b/grub-core/kern/sparc64/cache.S
new file mode 100644
index 0000000..1a16add
--- /dev/null
+++ b/grub-core/kern/sparc64/cache.S
@@ -0,0 +1,41 @@
+/* cache.S - Flush the processor cache for a specific region. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/symbol.h>
+
+ .file "cache.S"
+
+ .text
+
+/*
+ * void grub_arch_sync_caches (void *address, grub_size_t len)
+ */
+FUNCTION(grub_arch_sync_caches)
+ brz,pn %o1, 2f
+ add %o0, %o1, %o1
+ add %o1, 7, %o1
+ andn %o1, 7, %o1
+ andn %o0, 7, %o0
+ sub %o1, %o0, %o1
+1: subcc %o1, 8, %o1
+ bne,pt %icc, 1b
+ flush %o0 + %o1
+2: retl
+ nop
+
diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c
new file mode 100644
index 0000000..7b6266c
--- /dev/null
+++ b/grub-core/kern/sparc64/dl.c
@@ -0,0 +1,142 @@
+/* dl.c - arch-dependent part of loadable module support */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,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/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+
+/* Check if EHDR is a valid ELF header. */
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+ Elf_Ehdr *e = ehdr;
+
+ /* Check the magic numbers. */
+ if (e->e_ident[EI_CLASS] != ELFCLASS64
+ || e->e_ident[EI_DATA] != ELFDATA2MSB
+ || e->e_machine != EM_SPARCV9)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid arch specific ELF magic");
+
+ return GRUB_ERR_NONE;
+}
+
+
+/* Relocate symbols. */
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+{
+ Elf_Ehdr *e = ehdr;
+ Elf_Shdr *s;
+ Elf_Word entsize;
+ unsigned i;
+
+ /* Find a symbol table. */
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+
+ if (i == e->e_shnum)
+ return grub_error (GRUB_ERR_BAD_MODULE, "no symtab found");
+
+ entsize = s->sh_entsize;
+
+ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_RELA)
+ {
+ grub_dl_segment_t seg;
+
+ /* Find the target segment. */
+ for (seg = mod->segment; seg; seg = seg->next)
+ if (seg->section == s->sh_info)
+ break;
+
+ if (seg)
+ {
+ Elf_Rela *rel, *max;
+
+ for (rel = (Elf_Rela *) ((char *) e + s->sh_offset),
+ max = rel + s->sh_size / s->sh_entsize;
+ rel < max;
+ rel++)
+ {
+ Elf_Word *addr;
+ Elf_Sym *sym;
+ Elf_Addr value;
+
+ if (seg->size < rel->r_offset)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "reloc offset is out of the segment");
+
+ addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+ sym = (Elf_Sym *) ((char *) mod->symtab
+ + entsize * ELF_R_SYM (rel->r_info));
+
+ value = sym->st_value + rel->r_addend;
+ switch (ELF_R_TYPE (rel->r_info) & 0xff)
+ {
+ case R_SPARC_32: /* 3 V-word32 */
+ if (value & 0xFFFFFFFF00000000)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "address out of 32 bits range");
+ *addr = value;
+ break;
+ case R_SPARC_WDISP30: /* 7 V-disp30 */
+ if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) &&
+ (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000)
+ != 0xFFFFFFFF00000000))
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "displacement out of 30 bits range");
+ *addr = (*addr & 0xC0000000) |
+ (((grub_int32_t) ((value - (Elf_Addr) addr) >> 2)) &
+ 0x3FFFFFFF);
+ break;
+ case R_SPARC_HI22: /* 9 V-imm22 */
+ if (((grub_int32_t) value) & 0xFF00000000)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "high address out of 22 bits range");
+ *addr = (*addr & 0xFFC00000) | ((value >> 10) & 0x3FFFFF);
+ break;
+ case R_SPARC_LO10: /* 12 T-simm13 */
+ *addr = (*addr & 0xFFFFFC00) | (value & 0x3FF);
+ break;
+ case R_SPARC_64: /* 32 V-xwords64 */
+ *(Elf_Xword *) addr = value;
+ break;
+ case R_SPARC_OLO10:
+ *addr = (*addr & ~0x1fff)
+ | (((value & 0x3ff) +
+ (ELF_R_TYPE (rel->r_info) >> 8))
+ & 0x1fff);
+ break;
+ default:
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "this relocation (%d) is not implemented yet",
+ ELF_R_TYPE (rel->r_info));
+ }
+ }
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/kern/sparc64/ieee1275/crt0.S b/grub-core/kern/sparc64/ieee1275/crt0.S
new file mode 100644
index 0000000..cebdca2
--- /dev/null
+++ b/grub-core/kern/sparc64/ieee1275/crt0.S
@@ -0,0 +1,94 @@
+/* crt0.S - Startup code for the Sparc64. */
+/*
+ * 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/symbol.h>
+#include <grub/machine/kernel.h>
+#include <grub/offsets.h>
+
+ .text
+ .align 4
+ .globl _start
+_start:
+ ba codestart
+ mov %o4, %o0
+
+ . = EXT_C(_start) + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE
+
+VARIABLE(grub_total_module_size)
+ .word 0
+VARIABLE(grub_kernel_image_size)
+ .word 0
+VARIABLE(grub_compressed_size)
+ .word 0
+VARIABLE(grub_prefix)
+ /* to be filled by grub-mkimage */
+
+ /*
+ * Leave some breathing room for the prefix.
+ */
+
+ . = EXT_C(_start) + GRUB_KERNEL_MACHINE_PREFIX_END
+
+codestart:
+ /* Copy the modules past the end of the kernel image.
+ * They are currently sitting in the BSS.
+ */
+ sethi %hi(__bss_start), %o2
+ or %o2, %lo(__bss_start), %o2
+ sethi %hi(_end), %o3
+ or %o3, %lo(_end), %o3
+ sethi %hi(grub_total_module_size), %o4
+ lduw [%o4 + %lo(grub_total_module_size)], %o4
+
+ add %o2, %o4, %o2
+ add %o3, %o4, %o3
+
+ /* Save ieee1275 stack for future use by booter. */
+ mov %o6, %o1
+ /* Our future stack. */
+ sethi %hi(GRUB_KERNEL_MACHINE_STACK_SIZE), %o5
+ or %o5, %lo(GRUB_KERNEL_MACHINE_STACK_SIZE), %o5
+ add %o3, %o5, %o6
+ and %o6, ~0xff, %o6
+ sub %o6, 2047, %o6
+
+ sub %o2, 4, %o2
+ sub %o3, 4, %o3
+1: lduw [%o2], %o5
+ stw %o5, [%o3]
+ subcc %o4, 4, %o4
+ sub %o2, 4, %o2
+ bne,pt %icc, 1b
+ sub %o3, 4, %o3
+
+ /* Now it's safe to clear out the BSS. */
+ sethi %hi(__bss_start), %o2
+ or %o2, %lo(__bss_start), %o2
+ sethi %hi(_end), %o3
+ or %o3, %lo(_end), %o3
+1: stx %g0, [%o2]
+ add %o2, 8, %o2
+ cmp %o2, %o3
+ blt,pt %xcc, 1b
+ nop
+ sethi %hi(grub_ieee1275_original_stack), %o2
+ stx %o1, [%o2 + %lo(grub_ieee1275_original_stack)]
+ sethi %hi(grub_ieee1275_entry_fn), %o2
+ call grub_main
+ stx %o0, [%o2 + %lo(grub_ieee1275_entry_fn)]
+1: ba,a 1b
diff --git a/grub-core/kern/sparc64/ieee1275/ieee1275.c b/grub-core/kern/sparc64/ieee1275/ieee1275.c
new file mode 100644
index 0000000..53be692
--- /dev/null
+++ b/grub-core/kern/sparc64/ieee1275/ieee1275.c
@@ -0,0 +1,91 @@
+/*
+ * 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/ieee1275/ieee1275.h>
+#include <grub/types.h>
+
+/* Sun specific ieee1275 interfaces used by GRUB. */
+
+int
+grub_ieee1275_claim_vaddr (grub_addr_t vaddr, grub_size_t size)
+{
+ struct claim_vaddr_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t method;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t align;
+ grub_ieee1275_cell_t size;
+ grub_ieee1275_cell_t virt;
+ grub_ieee1275_cell_t catch_result;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "call-method", 5, 2);
+ args.method = (grub_ieee1275_cell_t) "claim";
+ args.ihandle = grub_ieee1275_mmu;
+ args.align = 0;
+ args.size = size;
+ args.virt = vaddr;
+ args.catch_result = (grub_ieee1275_cell_t) -1;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+ return args.catch_result;
+}
+
+int
+grub_ieee1275_alloc_physmem (grub_addr_t *paddr, grub_size_t size,
+ grub_uint32_t align)
+{
+ grub_uint32_t memory_ihandle;
+ struct alloc_physmem_args
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t method;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t align;
+ grub_ieee1275_cell_t size;
+ grub_ieee1275_cell_t catch_result;
+ grub_ieee1275_cell_t phys_high;
+ grub_ieee1275_cell_t phys_low;
+ }
+ args;
+ grub_ssize_t actual = 0;
+
+ grub_ieee1275_get_property (grub_ieee1275_chosen, "memory",
+ &memory_ihandle, sizeof (memory_ihandle),
+ &actual);
+ if (actual != sizeof (memory_ihandle))
+ return -1;
+
+ if (!align)
+ align = 1;
+
+ INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 3);
+ args.method = (grub_ieee1275_cell_t) "claim";
+ args.ihandle = memory_ihandle;
+ args.align = (align ? align : 1);
+ args.size = size;
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+
+ *paddr = args.phys_low;
+
+ return args.catch_result;
+}
diff --git a/grub-core/kern/sparc64/ieee1275/init.c b/grub-core/kern/sparc64/ieee1275/init.c
new file mode 100644
index 0000000..72ee1f1
--- /dev/null
+++ b/grub-core/kern/sparc64/ieee1275/init.c
@@ -0,0 +1,174 @@
+/* init.c -- Initialize GRUB on SPARC64. */
+/*
+ * 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/kernel.h>
+#include <grub/mm.h>
+#include <grub/env.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/time.h>
+#include <grub/machine/boot.h>
+#include <grub/ieee1275/console.h>
+#include <grub/machine/kernel.h>
+#include <grub/machine/time.h>
+#include <grub/ieee1275/ofdisk.h>
+#include <grub/ieee1275/ieee1275.h>
+
+grub_addr_t grub_ieee1275_original_stack;
+
+void
+grub_exit (void)
+{
+ grub_ieee1275_exit ();
+}
+
+static grub_uint64_t
+ieee1275_get_time_ms (void)
+{
+ grub_uint32_t msecs = 0;
+
+ grub_ieee1275_milliseconds (&msecs);
+
+ return msecs;
+}
+
+grub_uint32_t
+grub_get_rtc (void)
+{
+ return ieee1275_get_time_ms ();
+}
+
+grub_addr_t
+grub_arch_modules_addr (void)
+{
+ extern char _end[];
+ return (grub_addr_t) _end;
+}
+
+void
+grub_machine_set_prefix (void)
+{
+ if (grub_prefix[0] != '(')
+ {
+ char bootpath[IEEE1275_MAX_PATH_LEN];
+ char *prefix, *path, *colon;
+ grub_ssize_t actual;
+
+ if (grub_ieee1275_get_property (grub_ieee1275_chosen, "bootpath",
+ &bootpath, sizeof (bootpath), &actual))
+ {
+ /* Should never happen. */
+ grub_printf ("/chosen/bootpath property missing!\n");
+ grub_env_set ("prefix", "");
+ return;
+ }
+
+ /* Transform an OF device path to a GRUB path. */
+ colon = grub_strchr (bootpath, ':');
+ if (colon)
+ {
+ char *part = colon + 1;
+
+ /* Consistently provide numbered partitions to GRUB.
+ OpenBOOT traditionally uses alphabetical partition
+ specifiers. */
+ if (part[0] >= 'a' && part[0] <= 'z')
+ part[0] = '1' + (part[0] - 'a');
+ }
+ prefix = grub_ieee1275_encode_devname (bootpath);
+
+ path = grub_xasprintf("%s%s", prefix, grub_prefix);
+
+ grub_strcpy (grub_prefix, path);
+
+ grub_free (path);
+ grub_free (prefix);
+ }
+
+ grub_env_set ("prefix", grub_prefix);
+}
+
+static void
+grub_heap_init (void)
+{
+ grub_mm_init_region ((void *) (grub_modules_get_end ()
+ + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000);
+}
+
+static void
+grub_parse_cmdline (void)
+{
+ grub_ssize_t actual;
+ char args[256];
+
+ if (grub_ieee1275_get_property (grub_ieee1275_chosen, "bootargs", &args,
+ sizeof args, &actual) == 0
+ && actual > 1)
+ {
+ int i = 0;
+
+ while (i < actual)
+ {
+ char *command = &args[i];
+ char *end;
+ char *val;
+
+ end = grub_strchr (command, ';');
+ if (end == 0)
+ i = actual; /* No more commands after this one. */
+ else
+ {
+ *end = '\0';
+ i += end - command + 1;
+ while (grub_isspace(args[i]))
+ i++;
+ }
+
+ /* Process command. */
+ val = grub_strchr (command, '=');
+ if (val)
+ {
+ *val = '\0';
+ grub_env_set (command, val + 1);
+ }
+ }
+ }
+}
+
+void
+grub_machine_init (void)
+{
+ grub_ieee1275_init ();
+ grub_console_init_early ();
+ grub_heap_init ();
+ grub_console_init_lately ();
+
+ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0);
+ grub_ofdisk_init ();
+
+ grub_parse_cmdline ();
+ grub_install_get_time_ms (ieee1275_get_time_ms);
+}
+
+void
+grub_machine_fini (void)
+{
+ grub_ofdisk_fini ();
+ grub_console_fini ();
+}
diff --git a/grub-core/kern/term.c b/grub-core/kern/term.c
new file mode 100644
index 0000000..7b35931
--- /dev/null
+++ b/grub-core/kern/term.c
@@ -0,0 +1,130 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,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/term.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/env.h>
+#include <grub/time.h>
+
+struct grub_term_output *grub_term_outputs_disabled;
+struct grub_term_input *grub_term_inputs_disabled;
+struct grub_term_output *grub_term_outputs;
+struct grub_term_input *grub_term_inputs;
+
+void (*grub_term_poll_usb) (void) = NULL;
+
+/* Put a Unicode character. */
+static void
+grub_putcode_dumb (grub_uint32_t code,
+ struct grub_term_output *term)
+{
+ struct grub_unicode_glyph c =
+ {
+ .base = code,
+ .variant = 0,
+ .attributes = 0,
+ .ncomb = 0,
+ .combining = 0,
+ .estimated_width = 1
+ };
+
+ if (code == '\t' && term->getxy)
+ {
+ int n;
+
+ n = 8 - ((term->getxy (term) >> 8) & 7);
+ while (n--)
+ grub_putcode_dumb (' ', term);
+
+ return;
+ }
+
+ (term->putchar) (term, &c);
+ if (code == '\n')
+ grub_putcode_dumb ('\r', term);
+}
+
+static void
+grub_xputs_dumb (const char *str)
+{
+ for (; *str; str++)
+ {
+ grub_term_output_t term;
+ grub_uint32_t code = *str;
+ if (code > 0x7f)
+ code = '?';
+
+ FOR_ACTIVE_TERM_OUTPUTS(term)
+ grub_putcode_dumb (code, term);
+ }
+}
+
+void (*grub_xputs) (const char *str) = grub_xputs_dumb;
+
+static int pending_key = GRUB_TERM_NO_KEY;
+
+int
+grub_checkkey (void)
+{
+ grub_term_input_t term;
+
+ if (pending_key != GRUB_TERM_NO_KEY)
+ return pending_key;
+
+ if (grub_term_poll_usb)
+ grub_term_poll_usb ();
+
+ FOR_ACTIVE_TERM_INPUTS(term)
+ {
+ pending_key = term->getkey (term);
+ if (pending_key != GRUB_TERM_NO_KEY)
+ return pending_key;
+ }
+
+ return -1;
+}
+
+int
+grub_getkey (void)
+{
+ int ret;
+
+ grub_refresh ();
+
+ grub_checkkey ();
+ while (pending_key == GRUB_TERM_NO_KEY)
+ {
+ grub_cpu_idle ();
+ grub_checkkey ();
+ }
+ ret = pending_key;
+ pending_key = GRUB_TERM_NO_KEY;
+ return ret;
+}
+
+
+void
+grub_refresh (void)
+{
+ struct grub_term_output *term;
+
+ FOR_ACTIVE_TERM_OUTPUTS(term)
+ grub_term_refresh (term);
+}
diff --git a/grub-core/kern/time.c b/grub-core/kern/time.c
new file mode 100644
index 0000000..6521ec6
--- /dev/null
+++ b/grub-core/kern/time.c
@@ -0,0 +1,37 @@
+/* time.c - kernel time functions */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 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/>.
+ */
+
+#include <grub/time.h>
+
+typedef grub_uint64_t (*get_time_ms_func_t) (void);
+
+/* Function pointer to the implementation in use. */
+static get_time_ms_func_t get_time_ms_func;
+
+grub_uint64_t
+grub_get_time_ms (void)
+{
+ return get_time_ms_func ();
+}
+
+void
+grub_install_get_time_ms (get_time_ms_func_t func)
+{
+ get_time_ms_func = func;
+}
diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c
new file mode 100644
index 0000000..090ad78
--- /dev/null
+++ b/grub-core/kern/x86_64/dl.c
@@ -0,0 +1,119 @@
+/* dl-x86_64.c - arch-dependent part of loadable module support */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,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/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+
+/* Check if EHDR is a valid ELF header. */
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+ Elf64_Ehdr *e = ehdr;
+
+ /* Check the magic numbers. */
+ if (e->e_ident[EI_CLASS] != ELFCLASS64
+ || e->e_ident[EI_DATA] != ELFDATA2LSB
+ || e->e_machine != EM_X86_64)
+ return grub_error (GRUB_ERR_BAD_OS, "invalid arch specific ELF magic");
+
+ return GRUB_ERR_NONE;
+}
+
+/* Relocate symbols. */
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
+{
+ Elf64_Ehdr *e = ehdr;
+ Elf64_Shdr *s;
+ Elf64_Word entsize;
+ unsigned i;
+
+ /* Find a symbol table. */
+ for (i = 0, s = (Elf64_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf64_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_SYMTAB)
+ break;
+
+ if (i == e->e_shnum)
+ return grub_error (GRUB_ERR_BAD_MODULE, "no symtab found");
+
+ entsize = s->sh_entsize;
+
+ for (i = 0, s = (Elf64_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf64_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_RELA)
+ {
+ grub_dl_segment_t seg;
+
+ /* Find the target segment. */
+ for (seg = mod->segment; seg; seg = seg->next)
+ if (seg->section == s->sh_info)
+ break;
+
+ if (seg)
+ {
+ Elf64_Rela *rel, *max;
+
+ for (rel = (Elf64_Rela *) ((char *) e + s->sh_offset),
+ max = rel + s->sh_size / s->sh_entsize;
+ rel < max;
+ rel++)
+ {
+ Elf64_Word *addr32;
+ Elf64_Xword *addr64;
+ Elf64_Sym *sym;
+
+ if (seg->size < rel->r_offset)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "reloc offset is out of the segment");
+
+ addr32 = (Elf64_Word *) ((char *) seg->addr + rel->r_offset);
+ addr64 = (Elf64_Xword *) addr32;
+ sym = (Elf64_Sym *) ((char *) mod->symtab
+ + entsize * ELF_R_SYM (rel->r_info));
+
+ switch (ELF_R_TYPE (rel->r_info))
+ {
+ case R_X86_64_64:
+ *addr64 += rel->r_addend + sym->st_value;
+ break;
+
+ case R_X86_64_PC32:
+ *addr32 += rel->r_addend + sym->st_value -
+ (Elf64_Xword) seg->addr - rel->r_offset;
+ break;
+
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ *addr32 += rel->r_addend + sym->st_value;
+ break;
+
+ default:
+ grub_fatal ("Unrecognized relocation: %d\n", ELF_R_TYPE (rel->r_info));
+ }
+ }
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/kern/x86_64/efi/callwrap.S b/grub-core/kern/x86_64/efi/callwrap.S
new file mode 100644
index 0000000..aae2678
--- /dev/null
+++ b/grub-core/kern/x86_64/efi/callwrap.S
@@ -0,0 +1,116 @@
+/* callwrap.S - wrapper for x86_64 efi calls */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,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 <config.h>
+#include <grub/symbol.h>
+#include <grub/boot.h>
+
+/*
+ * x86_64 uses registry to pass parameters. Unfortunately, gcc and efi use
+ * different call conversion, so we need to do some conversion.
+ *
+ * gcc:
+ * %rdi, %esi, %rdx, %rcx, %r8, %r9, 8(%rsp), 16(%rsp), ...
+ *
+ * efi:
+ * %rcx, %rdx, %r8, %r9, 32(%rsp), 40(%rsp), 48(%rsp), ...
+ *
+ */
+
+ .file "callwrap.S"
+ .text
+
+FUNCTION(efi_wrap_0)
+ subq $48, %rsp
+ call *%rdi
+ addq $48, %rsp
+ ret
+
+FUNCTION(efi_wrap_1)
+ subq $48, %rsp
+ mov %rsi, %rcx
+ call *%rdi
+ addq $48, %rsp
+ ret
+
+FUNCTION(efi_wrap_2)
+ subq $48, %rsp
+ mov %rsi, %rcx
+ call *%rdi
+ addq $48, %rsp
+ ret
+
+FUNCTION(efi_wrap_3)
+ subq $48, %rsp
+ mov %rcx, %r8
+ mov %rsi, %rcx
+ call *%rdi
+ addq $48, %rsp
+ ret
+
+FUNCTION(efi_wrap_4)
+ subq $48, %rsp
+ mov %r8, %r9
+ mov %rcx, %r8
+ mov %rsi, %rcx
+ call *%rdi
+ addq $48, %rsp
+ ret
+
+FUNCTION(efi_wrap_5)
+ subq $48, %rsp
+ mov %r9, 32(%rsp)
+ mov %r8, %r9
+ mov %rcx, %r8
+ mov %rsi, %rcx
+ call *%rdi
+ addq $48, %rsp
+ ret
+
+FUNCTION(efi_wrap_6)
+ subq $64, %rsp
+ mov 64+8(%rsp), %rax
+ mov %rax, 40(%rsp)
+ mov %r9, 32(%rsp)
+ mov %r8, %r9
+ mov %rcx, %r8
+ mov %rsi, %rcx
+ call *%rdi
+ addq $64, %rsp
+ ret
+
+FUNCTION(efi_wrap_10)
+ subq $96, %rsp
+ mov 96+40(%rsp), %rax
+ mov %rax, 72(%rsp)
+ mov 96+32(%rsp), %rax
+ mov %rax, 64(%rsp)
+ mov 96+24(%rsp), %rax
+ mov %rax, 56(%rsp)
+ mov 96+16(%rsp), %rax
+ mov %rax, 48(%rsp)
+ mov 96+8(%rsp), %rax
+ mov %rax, 40(%rsp)
+ mov %r9, 32(%rsp)
+ mov %r8, %r9
+ mov %rcx, %r8
+ mov %rsi, %rcx
+ call *%rdi
+ addq $96, %rsp
+ ret
diff --git a/grub-core/kern/x86_64/efi/startup.S b/grub-core/kern/x86_64/efi/startup.S
new file mode 100644
index 0000000..fb4fc7b
--- /dev/null
+++ b/grub-core/kern/x86_64/efi/startup.S
@@ -0,0 +1,63 @@
+/* startup.S - bootstrap GRUB itself */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2006,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 <config.h>
+#include <grub/symbol.h>
+#include <grub/boot.h>
+
+ .file "startup.S"
+ .text
+ .globl start, _start
+ .code64
+
+start:
+_start:
+ jmp codestart
+
+ /*
+ * Compatibility version number
+ *
+ * These MUST be at byte offset 6 and 7 of the executable
+ * DO NOT MOVE !!!
+ */
+ . = _start + 0x6
+ .byte GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
+
+ /*
+ * This is a special data area 8 bytes from the beginning.
+ */
+
+ . = _start + 0x8
+
+VARIABLE(grub_prefix)
+ /* to be filled by grub-mkimage */
+
+ /*
+ * Leave some breathing room for the prefix.
+ */
+
+ . = _start + 0x50
+
+codestart:
+ movq %rcx, EXT_C(grub_efi_image_handle)(%rip)
+ movq %rdx, EXT_C(grub_efi_system_table)(%rip)
+
+ call EXT_C(grub_main)
+ ret
+