summaryrefslogtreecommitdiffstats
path: root/grub-core/efiemu
diff options
context:
space:
mode:
Diffstat (limited to 'grub-core/efiemu')
-rw-r--r--grub-core/efiemu/i386/coredetect.c27
-rw-r--r--grub-core/efiemu/i386/loadcore32.c121
-rw-r--r--grub-core/efiemu/i386/loadcore64.c138
-rw-r--r--grub-core/efiemu/i386/nocfgtables.c30
-rw-r--r--grub-core/efiemu/i386/pc/cfgtables.c69
-rw-r--r--grub-core/efiemu/loadcore.c387
-rw-r--r--grub-core/efiemu/loadcore32.c22
-rw-r--r--grub-core/efiemu/loadcore64.c22
-rw-r--r--grub-core/efiemu/loadcore_common.c196
-rw-r--r--grub-core/efiemu/main.c328
-rw-r--r--grub-core/efiemu/mm.c677
-rw-r--r--grub-core/efiemu/pnvram.c269
-rw-r--r--grub-core/efiemu/prepare.c169
-rw-r--r--grub-core/efiemu/prepare32.c22
-rw-r--r--grub-core/efiemu/prepare64.c22
-rw-r--r--grub-core/efiemu/runtime/config.h36
-rw-r--r--grub-core/efiemu/runtime/efiemu.S159
-rw-r--r--grub-core/efiemu/runtime/efiemu.c636
-rw-r--r--grub-core/efiemu/symbols.c272
19 files changed, 3602 insertions, 0 deletions
diff --git a/grub-core/efiemu/i386/coredetect.c b/grub-core/efiemu/i386/coredetect.c
new file mode 100644
index 0000000..a262b53
--- /dev/null
+++ b/grub-core/efiemu/i386/coredetect.c
@@ -0,0 +1,27 @@
+/*
+ * 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/efiemu/efiemu.h>
+#include <grub/command.h>
+#include <grub/i386/cpuid.h>
+
+const char *
+grub_efiemu_get_default_core_name (void)
+{
+ return grub_cpuid_has_longmode ? "efiemu64.o" : "efiemu32.o";
+}
diff --git a/grub-core/efiemu/i386/loadcore32.c b/grub-core/efiemu/i386/loadcore32.c
new file mode 100644
index 0000000..e746df8
--- /dev/null
+++ b/grub-core/efiemu/i386/loadcore32.c
@@ -0,0 +1,121 @@
+/* i386 CPU-specific part of loadcore.c for 32-bit mode */
+/*
+ * 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/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/cpu/efiemu.h>
+#include <grub/elf.h>
+#include <grub/i18n.h>
+
+/* Check if EHDR is a valid ELF header. */
+int
+grub_arch_efiemu_check_header32 (void *ehdr)
+{
+ Elf32_Ehdr *e = ehdr;
+
+ /* Check the magic numbers. */
+ return (e->e_ident[EI_CLASS] == ELFCLASS32
+ && e->e_ident[EI_DATA] == ELFDATA2LSB
+ && e->e_machine == EM_386);
+}
+
+/* Relocate symbols. */
+grub_err_t
+grub_arch_efiemu_relocate_symbols32 (grub_efiemu_segment_t segs,
+ struct grub_efiemu_elf_sym *elfsyms,
+ void *ehdr)
+{
+ unsigned i;
+ Elf32_Ehdr *e = ehdr;
+ Elf32_Shdr *s;
+ grub_err_t err;
+
+ grub_dprintf ("efiemu", "relocating symbols %d %d\n",
+ e->e_shoff, e->e_shnum);
+
+ for (i = 0, s = (Elf32_Shdr *) ((char *) e + e->e_shoff);
+ i < e->e_shnum;
+ i++, s = (Elf32_Shdr *) ((char *) s + e->e_shentsize))
+ if (s->sh_type == SHT_REL)
+ {
+ grub_efiemu_segment_t seg;
+ grub_dprintf ("efiemu", "shtrel\n");
+
+ /* Find the target segment. */
+ for (seg = segs; seg; seg = seg->next)
+ if (seg->section == s->sh_info)
+ break;
+
+ if (seg)
+ {
+ Elf32_Rel *rel, *max;
+
+ for (rel = (Elf32_Rel *) ((char *) e + s->sh_offset),
+ max = rel + s->sh_size / s->sh_entsize;
+ rel < max;
+ rel++)
+ {
+ Elf32_Word *addr;
+ struct grub_efiemu_elf_sym sym;
+ if (seg->size < rel->r_offset)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "reloc offset is out of the segment");
+
+ addr = (Elf32_Word *)
+ ((char *) grub_efiemu_mm_obtain_request (seg->handle)
+ + seg->off + rel->r_offset);
+ sym = elfsyms[ELF32_R_SYM (rel->r_info)];
+
+ switch (ELF32_R_TYPE (rel->r_info))
+ {
+ case R_386_32:
+ err = grub_efiemu_write_value (addr, sym.off + *addr,
+ sym.handle, 0,
+ seg->ptv_rel_needed,
+ sizeof (grub_uint32_t));
+ if (err)
+ return err;
+
+ break;
+
+ case R_386_PC32:
+ err = grub_efiemu_write_value (addr, sym.off + *addr
+ - rel->r_offset
+ - seg->off, sym.handle,
+ seg->handle,
+ seg->ptv_rel_needed,
+ sizeof (grub_uint32_t));
+ if (err)
+ return err;
+ break;
+ default:
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("relocation 0x%x is not implemented yet"),
+ ELF_R_TYPE (rel->r_info));
+ }
+ }
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+
diff --git a/grub-core/efiemu/i386/loadcore64.c b/grub-core/efiemu/i386/loadcore64.c
new file mode 100644
index 0000000..ae476ef
--- /dev/null
+++ b/grub-core/efiemu/i386/loadcore64.c
@@ -0,0 +1,138 @@
+/* i386 CPU-specific part of loadcore.c for 32-bit mode */
+/*
+ * 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/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/cpu/efiemu.h>
+#include <grub/elf.h>
+#include <grub/i18n.h>
+
+/* Check if EHDR is a valid ELF header. */
+int
+grub_arch_efiemu_check_header64 (void *ehdr)
+{
+ Elf64_Ehdr *e = ehdr;
+
+ return (e->e_ident[EI_CLASS] == ELFCLASS64
+ && e->e_ident[EI_DATA] == ELFDATA2LSB
+ && e->e_machine == EM_X86_64);
+}
+
+/* Relocate symbols. */
+grub_err_t
+grub_arch_efiemu_relocate_symbols64 (grub_efiemu_segment_t segs,
+ struct grub_efiemu_elf_sym *elfsyms,
+ void *ehdr)
+{
+ unsigned i;
+ Elf64_Ehdr *e = ehdr;
+ Elf64_Shdr *s;
+ grub_err_t err;
+
+ 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_efiemu_segment_t seg;
+ grub_dprintf ("efiemu", "shtrel\n");
+
+ /* Find the target segment. */
+ for (seg = segs; 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 + (unsigned long) s->sh_size
+ / (unsigned long)s->sh_entsize;
+ rel < max;
+ rel++)
+ {
+ void *addr;
+ grub_uint32_t *addr32;
+ grub_uint64_t *addr64;
+ struct grub_efiemu_elf_sym sym;
+ if (seg->size < rel->r_offset)
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "reloc offset is out of the segment");
+
+ addr =
+ ((char *) grub_efiemu_mm_obtain_request (seg->handle)
+ + seg->off + rel->r_offset);
+ addr32 = (grub_uint32_t *) addr;
+ addr64 = (grub_uint64_t *) addr;
+ sym = elfsyms[ELF64_R_SYM (rel->r_info)];
+
+ switch (ELF64_R_TYPE (rel->r_info))
+ {
+ case R_X86_64_64:
+ err = grub_efiemu_write_value (addr,
+ *addr64 + rel->r_addend
+ + sym.off, sym.handle,
+ 0, seg->ptv_rel_needed,
+ sizeof (grub_uint64_t));
+ if (err)
+ return err;
+ break;
+
+ case R_X86_64_PC32:
+ case R_X86_64_PLT32:
+ err = grub_efiemu_write_value (addr,
+ *addr32 + rel->r_addend
+ + sym.off
+ - rel->r_offset - seg->off,
+ sym.handle, seg->handle,
+ seg->ptv_rel_needed,
+ sizeof (grub_uint32_t));
+ if (err)
+ return err;
+ break;
+
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ err = grub_efiemu_write_value (addr,
+ *addr32 + rel->r_addend
+ + sym.off, sym.handle,
+ 0, seg->ptv_rel_needed,
+ sizeof (grub_uint32_t));
+ if (err)
+ return err;
+ break;
+ default:
+ {
+ char rel_info[17]; /* log16(2^64) = 16, plus NUL. */
+
+ grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T,
+ ELF_R_TYPE (rel->r_info));
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("relocation 0x%s is not implemented yet"), rel_info);
+ }
+ }
+ }
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/efiemu/i386/nocfgtables.c b/grub-core/efiemu/i386/nocfgtables.c
new file mode 100644
index 0000000..775f1d0
--- /dev/null
+++ b/grub-core/efiemu/i386/nocfgtables.c
@@ -0,0 +1,30 @@
+/* Register SMBIOS and ACPI tables. */
+/*
+ * 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/err.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/acpi.h>
+
+grub_err_t
+grub_machine_efiemu_init_tables (void)
+{
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/efiemu/i386/pc/cfgtables.c b/grub-core/efiemu/i386/pc/cfgtables.c
new file mode 100644
index 0000000..e5fffb7
--- /dev/null
+++ b/grub-core/efiemu/i386/pc/cfgtables.c
@@ -0,0 +1,69 @@
+/* Register SMBIOS and ACPI tables. */
+/*
+ * 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/err.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/acpi.h>
+#include <grub/smbios.h>
+
+grub_err_t
+grub_machine_efiemu_init_tables (void)
+{
+ void *table;
+ grub_err_t err;
+ grub_efi_guid_t smbios = GRUB_EFI_SMBIOS_TABLE_GUID;
+ grub_efi_guid_t acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID;
+ grub_efi_guid_t acpi = GRUB_EFI_ACPI_TABLE_GUID;
+
+ err = grub_efiemu_unregister_configuration_table (smbios);
+ if (err)
+ return err;
+ err = grub_efiemu_unregister_configuration_table (acpi);
+ if (err)
+ return err;
+ err = grub_efiemu_unregister_configuration_table (acpi20);
+ if (err)
+ return err;
+
+ table = grub_acpi_get_rsdpv1 ();
+ if (table)
+ {
+ err = grub_efiemu_register_configuration_table (acpi, 0, 0, table);
+ if (err)
+ return err;
+ }
+ table = grub_acpi_get_rsdpv2 ();
+ if (table)
+ {
+ err = grub_efiemu_register_configuration_table (acpi20, 0, 0, table);
+ if (err)
+ return err;
+ }
+ table = grub_smbios_get_eps ();
+ if (table)
+ {
+ err = grub_efiemu_register_configuration_table (smbios, 0, 0, table);
+ if (err)
+ return err;
+ }
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/efiemu/loadcore.c b/grub-core/efiemu/loadcore.c
new file mode 100644
index 0000000..2b92462
--- /dev/null
+++ b/grub-core/efiemu/loadcore.c
@@ -0,0 +1,387 @@
+/* Load runtime image of EFIemu. Functions specific to 32/64-bit mode */
+/*
+ * 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/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/cpu/efiemu.h>
+#include <grub/elf.h>
+#include <grub/i18n.h>
+
+/* ELF symbols and their values */
+static struct grub_efiemu_elf_sym *grub_efiemu_elfsyms = 0;
+static int grub_efiemu_nelfsyms = 0;
+
+/* Return the address of a section whose index is N. */
+static grub_err_t
+grub_efiemu_get_section_addr (grub_efiemu_segment_t segs, unsigned n,
+ int *handle, grub_off_t *off)
+{
+ grub_efiemu_segment_t seg;
+
+ for (seg = segs; seg; seg = seg->next)
+ if (seg->section == n)
+ {
+ *handle = seg->handle;
+ *off = seg->off;
+ return GRUB_ERR_NONE;
+ }
+
+ return grub_error (GRUB_ERR_BAD_OS, "section %d not found", n);
+}
+
+grub_err_t
+SUFFIX (grub_efiemu_loadcore_unload) (void)
+{
+ grub_free (grub_efiemu_elfsyms);
+ grub_efiemu_elfsyms = 0;
+ return GRUB_ERR_NONE;
+}
+
+/* Check if EHDR is a valid ELF header. */
+int
+SUFFIX (grub_efiemu_check_header) (void *ehdr, grub_size_t size)
+{
+ Elf_Ehdr *e = ehdr;
+
+ /* Check the header size. */
+ if (size < sizeof (Elf_Ehdr))
+ return 0;
+
+ /* Check the magic numbers. */
+ if (!SUFFIX (grub_arch_efiemu_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 0;
+
+ return 1;
+}
+
+/* Load all segments from memory specified by E. */
+static grub_err_t
+grub_efiemu_load_segments (grub_efiemu_segment_t segs, const Elf_Ehdr *e)
+{
+ Elf_Shdr *s;
+ grub_efiemu_segment_t cur;
+
+ grub_dprintf ("efiemu", "loading segments\n");
+
+ for (cur=segs; cur; cur = cur->next)
+ {
+ s = (Elf_Shdr *)cur->srcptr;
+
+ if ((s->sh_flags & SHF_ALLOC) && s->sh_size)
+ {
+ void *addr;
+
+ addr = (grub_uint8_t *) grub_efiemu_mm_obtain_request (cur->handle)
+ + cur->off;
+
+ 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;
+ }
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/* Get a string at offset OFFSET from strtab */
+static char *
+grub_efiemu_get_string (unsigned offset, 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_type == SHT_STRTAB && offset < s->sh_size)
+ return (char *) e + s->sh_offset + offset;
+ return 0;
+}
+
+/* Request memory for segments and fill segments info */
+static grub_err_t
+grub_efiemu_init_segments (grub_efiemu_segment_t *segs, 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_efiemu_segment_t seg;
+ seg = (grub_efiemu_segment_t) grub_malloc (sizeof (*seg));
+ if (! seg)
+ return grub_errno;
+
+ if (s->sh_size)
+ {
+ seg->handle
+ = grub_efiemu_request_memalign
+ (s->sh_addralign, s->sh_size,
+ s->sh_flags & SHF_EXECINSTR ? GRUB_EFI_RUNTIME_SERVICES_CODE
+ : GRUB_EFI_RUNTIME_SERVICES_DATA);
+ if (seg->handle < 0)
+ {
+ grub_free (seg);
+ return grub_errno;
+ }
+ seg->off = 0;
+ }
+
+ /*
+ .text-physical doesn't need to be relocated when switching to
+ virtual mode
+ */
+ if (!grub_strcmp (grub_efiemu_get_string (s->sh_name, e),
+ ".text-physical"))
+ seg->ptv_rel_needed = 0;
+ else
+ seg->ptv_rel_needed = 1;
+ seg->size = s->sh_size;
+ seg->section = i;
+ seg->next = *segs;
+ seg->srcptr = s;
+ *segs = seg;
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/* Count symbols and relocators and allocate/request memory for them */
+static grub_err_t
+grub_efiemu_count_symbols (const Elf_Ehdr *e)
+{
+ unsigned i;
+ Elf_Shdr *s;
+ int num = 0;
+
+ /* Symbols */
+ 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_OS, N_("no symbol table"));
+
+ grub_efiemu_nelfsyms = (unsigned) s->sh_size / (unsigned) s->sh_entsize;
+ grub_efiemu_elfsyms = (struct grub_efiemu_elf_sym *)
+ grub_calloc (grub_efiemu_nelfsyms, sizeof (struct grub_efiemu_elf_sym));
+
+ /* Relocators */
+ 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 || s->sh_type == SHT_RELA)
+ num += ((unsigned) s->sh_size) / ((unsigned) s->sh_entsize);
+
+ grub_efiemu_request_symbols (num);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Fill grub_efiemu_elfsyms with symbol values */
+static grub_err_t
+grub_efiemu_resolve_symbols (grub_efiemu_segment_t segs, Elf_Ehdr *e)
+{
+ unsigned i;
+ Elf_Shdr *s;
+ Elf_Sym *sym;
+ const char *str;
+ Elf_Word size, entsize;
+
+ grub_dprintf ("efiemu", "resolving symbols\n");
+
+ 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_OS, N_("no symbol table"));
+
+ sym = (Elf_Sym *) ((char *) e + s->sh_offset);
+ 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);
+ int handle;
+ grub_off_t off;
+ grub_err_t err;
+ const char *name = str + sym->st_name;
+ grub_efiemu_elfsyms[i].section = sym->st_shndx;
+ switch (type)
+ {
+ case STT_NOTYPE:
+ /* Resolve a global symbol. */
+ if (sym->st_name != 0 && sym->st_shndx == 0)
+ {
+ err = grub_efiemu_resolve_symbol (name, &handle, &off);
+ if (err)
+ return err;
+ grub_efiemu_elfsyms[i].handle = handle;
+ grub_efiemu_elfsyms[i].off = off;
+ }
+ else
+ sym->st_value = 0;
+ break;
+
+ case STT_OBJECT:
+ err = grub_efiemu_get_section_addr (segs, sym->st_shndx,
+ &handle, &off);
+ if (err)
+ return err;
+
+ off += sym->st_value;
+ if (bind != STB_LOCAL)
+ {
+ err = grub_efiemu_register_symbol (name, handle, off);
+ if (err)
+ return err;
+ }
+ grub_efiemu_elfsyms[i].handle = handle;
+ grub_efiemu_elfsyms[i].off = off;
+ break;
+
+ case STT_FUNC:
+ err = grub_efiemu_get_section_addr (segs, sym->st_shndx,
+ &handle, &off);
+ if (err)
+ return err;
+
+ off += sym->st_value;
+ if (bind != STB_LOCAL)
+ {
+ err = grub_efiemu_register_symbol (name, handle, off);
+ if (err)
+ return err;
+ }
+ grub_efiemu_elfsyms[i].handle = handle;
+ grub_efiemu_elfsyms[i].off = off;
+ break;
+
+ case STT_SECTION:
+ err = grub_efiemu_get_section_addr (segs, sym->st_shndx,
+ &handle, &off);
+ if (err)
+ {
+ grub_efiemu_elfsyms[i].handle = 0;
+ grub_efiemu_elfsyms[i].off = 0;
+ grub_errno = GRUB_ERR_NONE;
+ break;
+ }
+
+ grub_efiemu_elfsyms[i].handle = handle;
+ grub_efiemu_elfsyms[i].off = off;
+ break;
+
+ case STT_FILE:
+ grub_efiemu_elfsyms[i].handle = 0;
+ grub_efiemu_elfsyms[i].off = 0;
+ break;
+
+ default:
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ "unknown symbol type `%d'", (int) type);
+ }
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load runtime to the memory and request memory for definitive location*/
+grub_err_t
+SUFFIX (grub_efiemu_loadcore_init) (void *core, const char *filename,
+ grub_size_t core_size,
+ grub_efiemu_segment_t *segments)
+{
+ Elf_Ehdr *e = (Elf_Ehdr *) core;
+ grub_err_t err;
+
+ if (e->e_type != ET_REL)
+ return grub_error (GRUB_ERR_BAD_MODULE, N_("this ELF file is not of the right type"));
+
+ /* Make sure that every section is within the core. */
+ if ((grub_size_t) core_size < e->e_shoff + (grub_uint32_t) e->e_shentsize * e->e_shnum)
+ return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ filename);
+
+ err = grub_efiemu_init_segments (segments, core);
+ if (err)
+ return err;
+ err = grub_efiemu_count_symbols (core);
+ if (err)
+ return err;
+
+ grub_efiemu_request_symbols (1);
+ return GRUB_ERR_NONE;
+}
+
+/* Load runtime definitively */
+grub_err_t
+SUFFIX (grub_efiemu_loadcore_load) (void *core,
+ grub_size_t core_size
+ __attribute__ ((unused)),
+ grub_efiemu_segment_t segments)
+{
+ grub_err_t err;
+ err = grub_efiemu_load_segments (segments, core);
+ if (err)
+ return err;
+
+ err = grub_efiemu_resolve_symbols (segments, core);
+ if (err)
+ return err;
+
+ err = SUFFIX (grub_arch_efiemu_relocate_symbols) (segments,
+ grub_efiemu_elfsyms,
+ core);
+ if (err)
+ return err;
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/efiemu/loadcore32.c b/grub-core/efiemu/loadcore32.c
new file mode 100644
index 0000000..c1e033b
--- /dev/null
+++ b/grub-core/efiemu/loadcore32.c
@@ -0,0 +1,22 @@
+/* This file contains definitions so that loadcore.c compiles for 32-bit */
+/*
+ * 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/>.
+ */
+
+#define SUFFIX(x) x ## 32
+#define GRUB_TARGET_WORDSIZE 32
+#include "loadcore.c"
diff --git a/grub-core/efiemu/loadcore64.c b/grub-core/efiemu/loadcore64.c
new file mode 100644
index 0000000..ce7284f
--- /dev/null
+++ b/grub-core/efiemu/loadcore64.c
@@ -0,0 +1,22 @@
+/* This file contains definitions so that loadcore.c compiles for 64-bit */
+/*
+ * 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/>.
+ */
+
+#define SUFFIX(x) x ## 64
+#define GRUB_TARGET_WORDSIZE 64
+#include "loadcore.c"
diff --git a/grub-core/efiemu/loadcore_common.c b/grub-core/efiemu/loadcore_common.c
new file mode 100644
index 0000000..64d7960
--- /dev/null
+++ b/grub-core/efiemu/loadcore_common.c
@@ -0,0 +1,196 @@
+/* Load runtime image of EFIemu. Functions common to 32/64-bit mode */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/cpu/efiemu.h>
+
+/* Are we in 32 or 64-bit mode?*/
+static grub_efiemu_mode_t grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;
+/* Runtime ELF file */
+static grub_ssize_t efiemu_core_size;
+static void *efiemu_core = 0;
+/* Linked list of segments */
+static grub_efiemu_segment_t efiemu_segments = 0;
+
+/* equivalent to sizeof (grub_efi_uintn_t) but taking the mode into account*/
+int
+grub_efiemu_sizeof_uintn_t (void)
+{
+ if (grub_efiemu_mode == GRUB_EFIEMU32)
+ return 4;
+ if (grub_efiemu_mode == GRUB_EFIEMU64)
+ return 8;
+ return 0;
+}
+
+/* Check the header and set mode */
+static grub_err_t
+grub_efiemu_check_header (void *ehdr, grub_size_t size,
+ grub_efiemu_mode_t *mode)
+{
+ /* Check the magic numbers. */
+ if ((*mode == GRUB_EFIEMU_NOTLOADED || *mode == GRUB_EFIEMU32)
+ && grub_efiemu_check_header32 (ehdr,size))
+ {
+ *mode = GRUB_EFIEMU32;
+ return GRUB_ERR_NONE;
+ }
+ if ((*mode == GRUB_EFIEMU_NOTLOADED || *mode == GRUB_EFIEMU64)
+ && grub_efiemu_check_header64 (ehdr,size))
+ {
+ *mode = GRUB_EFIEMU64;
+ return GRUB_ERR_NONE;
+ }
+ return grub_error (GRUB_ERR_BAD_OS, "invalid ELF magic");
+}
+
+/* Unload segments */
+static int
+grub_efiemu_unload_segs (grub_efiemu_segment_t seg)
+{
+ grub_efiemu_segment_t segn;
+ for (; seg; seg = segn)
+ {
+ segn = seg->next;
+ grub_efiemu_mm_return_request (seg->handle);
+ grub_free (seg);
+ }
+ return 1;
+}
+
+
+grub_err_t
+grub_efiemu_loadcore_unload(void)
+{
+ switch (grub_efiemu_mode)
+ {
+ case GRUB_EFIEMU32:
+ grub_efiemu_loadcore_unload32 ();
+ break;
+
+ case GRUB_EFIEMU64:
+ grub_efiemu_loadcore_unload64 ();
+ break;
+
+ default:
+ break;
+ }
+
+ grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;
+
+ grub_free (efiemu_core);
+ efiemu_core = 0;
+
+ grub_efiemu_unload_segs (efiemu_segments);
+ efiemu_segments = 0;
+
+ grub_efiemu_free_syms ();
+
+ return GRUB_ERR_NONE;
+}
+
+/* Load runtime file and do some initial preparations */
+grub_err_t
+grub_efiemu_loadcore_init (grub_file_t file,
+ const char *filename)
+{
+ grub_err_t err;
+
+ efiemu_core_size = grub_file_size (file);
+ efiemu_core = 0;
+ efiemu_core = grub_malloc (efiemu_core_size);
+ if (! efiemu_core)
+ return grub_errno;
+
+ if (grub_file_read (file, efiemu_core, efiemu_core_size)
+ != (int) efiemu_core_size)
+ {
+ grub_free (efiemu_core);
+ efiemu_core = 0;
+ return grub_errno;
+ }
+
+ if (grub_efiemu_check_header (efiemu_core, efiemu_core_size,
+ &grub_efiemu_mode))
+ {
+ grub_free (efiemu_core);
+ efiemu_core = 0;
+ return GRUB_ERR_BAD_MODULE;
+ }
+
+ switch (grub_efiemu_mode)
+ {
+ case GRUB_EFIEMU32:
+ err = grub_efiemu_loadcore_init32 (efiemu_core, filename,
+ efiemu_core_size,
+ &efiemu_segments);
+ if (err)
+ {
+ grub_free (efiemu_core);
+ efiemu_core = 0;
+ grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;
+ return err;
+ }
+ break;
+
+ case GRUB_EFIEMU64:
+ err = grub_efiemu_loadcore_init64 (efiemu_core, filename,
+ efiemu_core_size,
+ &efiemu_segments);
+ if (err)
+ {
+ grub_free (efiemu_core);
+ efiemu_core = 0;
+ grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;
+ return err;
+ }
+ break;
+
+ default:
+ return grub_error (GRUB_ERR_BUG, "unknown EFI runtime");
+ }
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_efiemu_loadcore_load (void)
+{
+ grub_err_t err;
+ switch (grub_efiemu_mode)
+ {
+ case GRUB_EFIEMU32:
+ err = grub_efiemu_loadcore_load32 (efiemu_core, efiemu_core_size,
+ efiemu_segments);
+ if (err)
+ grub_efiemu_loadcore_unload ();
+ return err;
+ case GRUB_EFIEMU64:
+ err = grub_efiemu_loadcore_load64 (efiemu_core, efiemu_core_size,
+ efiemu_segments);
+ if (err)
+ grub_efiemu_loadcore_unload ();
+ return err;
+ default:
+ return grub_error (GRUB_ERR_BUG, "unknown EFI runtime");
+ }
+}
diff --git a/grub-core/efiemu/main.c b/grub-core/efiemu/main.c
new file mode 100644
index 0000000..a819347
--- /dev/null
+++ b/grub-core/efiemu/main.c
@@ -0,0 +1,328 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This is an emulation of EFI runtime services.
+ This allows a more uniform boot on i386 machines.
+ As it emulates only runtime service it isn't able
+ to chainload EFI bootloader on non-EFI system. */
+
+
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/normal.h>
+#include <grub/mm.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* System table. Two version depending on mode */
+grub_efi_system_table32_t *grub_efiemu_system_table32 = 0;
+grub_efi_system_table64_t *grub_efiemu_system_table64 = 0;
+/* Modules may need to execute some actions after memory allocation happens */
+static struct grub_efiemu_prepare_hook *efiemu_prepare_hooks = 0;
+/* Linked list of configuration tables */
+static struct grub_efiemu_configuration_table *efiemu_config_tables = 0;
+static int prepared = 0;
+
+/* Free all allocated space */
+grub_err_t
+grub_efiemu_unload (void)
+{
+ struct grub_efiemu_configuration_table *cur, *d;
+ struct grub_efiemu_prepare_hook *curhook, *d2;
+ grub_efiemu_loadcore_unload ();
+
+ grub_efiemu_mm_unload ();
+
+ for (cur = efiemu_config_tables; cur;)
+ {
+ d = cur->next;
+ if (cur->unload)
+ cur->unload (cur->data);
+ grub_free (cur);
+ cur = d;
+ }
+ efiemu_config_tables = 0;
+
+ for (curhook = efiemu_prepare_hooks; curhook;)
+ {
+ d2 = curhook->next;
+ if (curhook->unload)
+ curhook->unload (curhook->data);
+ grub_free (curhook);
+ curhook = d2;
+ }
+ efiemu_prepare_hooks = 0;
+
+ prepared = 0;
+
+ return GRUB_ERR_NONE;
+}
+
+/* Remove previously registered table from the list */
+grub_err_t
+grub_efiemu_unregister_configuration_table (grub_efi_guid_t guid)
+{
+ struct grub_efiemu_configuration_table *cur, *prev;
+
+ /* Special treating if head is to remove */
+ while (efiemu_config_tables
+ && !grub_memcmp (&(efiemu_config_tables->guid), &guid, sizeof (guid)))
+ {
+ if (efiemu_config_tables->unload)
+ efiemu_config_tables->unload (efiemu_config_tables->data);
+ cur = efiemu_config_tables->next;
+ grub_free (efiemu_config_tables);
+ efiemu_config_tables = cur;
+ }
+ if (!efiemu_config_tables)
+ return GRUB_ERR_NONE;
+
+ /* Remove from chain */
+ for (prev = efiemu_config_tables, cur = prev->next; cur;)
+ if (grub_memcmp (&(cur->guid), &guid, sizeof (guid)) == 0)
+ {
+ if (cur->unload)
+ cur->unload (cur->data);
+ prev->next = cur->next;
+ grub_free (cur);
+ cur = prev->next;
+ }
+ else
+ {
+ prev = cur;
+ cur = cur->next;
+ }
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_efiemu_register_prepare_hook (grub_err_t (*hook) (void *data),
+ void (*unload) (void *data),
+ void *data)
+{
+ struct grub_efiemu_prepare_hook *nhook;
+ nhook = (struct grub_efiemu_prepare_hook *) grub_malloc (sizeof (*nhook));
+ if (! nhook)
+ return grub_errno;
+ nhook->hook = hook;
+ nhook->unload = unload;
+ nhook->data = data;
+ nhook->next = efiemu_prepare_hooks;
+ efiemu_prepare_hooks = nhook;
+ return GRUB_ERR_NONE;
+}
+
+/* Register a configuration table either supplying the address directly
+ or with a hook
+*/
+grub_err_t
+grub_efiemu_register_configuration_table (grub_efi_guid_t guid,
+ void * (*get_table) (void *data),
+ void (*unload) (void *data),
+ void *data)
+{
+ struct grub_efiemu_configuration_table *tbl;
+ grub_err_t err;
+
+ err = grub_efiemu_unregister_configuration_table (guid);
+ if (err)
+ return err;
+
+ tbl = (struct grub_efiemu_configuration_table *) grub_malloc (sizeof (*tbl));
+ if (! tbl)
+ return grub_errno;
+
+ tbl->guid = guid;
+ tbl->get_table = get_table;
+ tbl->unload = unload;
+ tbl->data = data;
+ tbl->next = efiemu_config_tables;
+ efiemu_config_tables = tbl;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_efiemu_unload (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char *args[] __attribute__ ((unused)))
+{
+ return grub_efiemu_unload ();
+}
+
+static grub_err_t
+grub_cmd_efiemu_prepare (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char *args[] __attribute__ ((unused)))
+{
+ return grub_efiemu_prepare ();
+}
+
+
+
+/* Load the runtime from the file FILENAME. */
+static grub_err_t
+grub_efiemu_load_file (const char *filename)
+{
+ grub_file_t file;
+ grub_err_t err;
+
+ file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE);
+ if (! file)
+ return grub_errno;
+
+ err = grub_efiemu_mm_init ();
+ if (err)
+ {
+ grub_file_close (file);
+ grub_efiemu_unload ();
+ return err;
+ }
+
+ grub_dprintf ("efiemu", "mm initialized\n");
+
+ err = grub_efiemu_loadcore_init (file, filename);
+ if (err)
+ {
+ grub_file_close (file);
+ grub_efiemu_unload ();
+ return err;
+ }
+
+ grub_file_close (file);
+
+ /* For configuration tables entry in system table. */
+ grub_efiemu_request_symbols (1);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_efiemu_autocore (void)
+{
+ const char *prefix;
+ char *filename;
+ const char *suffix;
+ grub_err_t err;
+
+ if (grub_efiemu_sizeof_uintn_t () != 0)
+ return GRUB_ERR_NONE;
+
+ prefix = grub_env_get ("prefix");
+
+ if (! prefix)
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+ N_("variable `%s' isn't set"), "prefix");
+
+ suffix = grub_efiemu_get_default_core_name ();
+
+ filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM "/%s",
+ prefix, suffix);
+ if (! filename)
+ return grub_errno;
+
+ err = grub_efiemu_load_file (filename);
+ grub_free (filename);
+ if (err)
+ return err;
+#ifndef GRUB_MACHINE_EMU
+ err = grub_machine_efiemu_init_tables ();
+ if (err)
+ return err;
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_efiemu_prepare (void)
+{
+ grub_err_t err;
+
+ if (prepared)
+ return GRUB_ERR_NONE;
+
+ err = grub_efiemu_autocore ();
+ if (err)
+ return err;
+
+ grub_dprintf ("efiemu", "Preparing %d-bit efiemu\n",
+ 8 * grub_efiemu_sizeof_uintn_t ());
+
+ /* Create NVRAM. */
+ grub_efiemu_pnvram ();
+
+ prepared = 1;
+
+ if (grub_efiemu_sizeof_uintn_t () == 4)
+ return grub_efiemu_prepare32 (efiemu_prepare_hooks, efiemu_config_tables);
+ else
+ return grub_efiemu_prepare64 (efiemu_prepare_hooks, efiemu_config_tables);
+}
+
+
+static grub_err_t
+grub_cmd_efiemu_load (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *args[])
+{
+ grub_err_t err;
+
+ grub_efiemu_unload ();
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+ err = grub_efiemu_load_file (args[0]);
+ if (err)
+ return err;
+#ifndef GRUB_MACHINE_EMU
+ err = grub_machine_efiemu_init_tables ();
+ if (err)
+ return err;
+#endif
+ return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd_loadcore, cmd_prepare, cmd_unload;
+
+GRUB_MOD_INIT(efiemu)
+{
+ cmd_loadcore = grub_register_command ("efiemu_loadcore",
+ grub_cmd_efiemu_load,
+ N_("FILE"),
+ N_("Load and initialize EFI emulator."));
+ cmd_prepare = grub_register_command ("efiemu_prepare",
+ grub_cmd_efiemu_prepare,
+ 0,
+ N_("Finalize loading of EFI emulator."));
+ cmd_unload = grub_register_command ("efiemu_unload", grub_cmd_efiemu_unload,
+ 0,
+ N_("Unload EFI emulator."));
+}
+
+GRUB_MOD_FINI(efiemu)
+{
+ grub_unregister_command (cmd_loadcore);
+ grub_unregister_command (cmd_prepare);
+ grub_unregister_command (cmd_unload);
+}
diff --git a/grub-core/efiemu/mm.c b/grub-core/efiemu/mm.c
new file mode 100644
index 0000000..9b8e0d0
--- /dev/null
+++ b/grub-core/efiemu/mm.c
@@ -0,0 +1,677 @@
+/* Memory management for efiemu */
+/*
+ * 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/>.
+ */
+/*
+ To keep efiemu runtime contiguous this mm is special.
+ It uses deferred allocation.
+ In the first stage you may request memory with grub_efiemu_request_memalign
+ It will give you a handle with which in the second phase you can access your
+ memory with grub_efiemu_mm_obtain_request (handle). It's guaranteed that
+ subsequent calls with the same handle return the same result. You can't request any additional memory once you're in the second phase
+*/
+
+#include <grub/err.h>
+#include <grub/normal.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/memory.h>
+
+struct grub_efiemu_memrequest
+{
+ struct grub_efiemu_memrequest *next;
+ grub_efi_memory_type_t type;
+ grub_size_t size;
+ grub_size_t align_overhead;
+ int handle;
+ void *val;
+};
+/* Linked list of requested memory. */
+static struct grub_efiemu_memrequest *memrequests = 0;
+/* Memory map. */
+static grub_efi_memory_descriptor_t *efiemu_mmap = 0;
+/* Pointer to allocated memory */
+static void *resident_memory = 0;
+/* Size of requested memory per type */
+static grub_size_t requested_memory[GRUB_EFI_MAX_MEMORY_TYPE];
+/* How many slots is allocated for memory_map and how many are already used */
+static int mmap_reserved_size = 0, mmap_num = 0;
+
+/* Add a memory region to map*/
+static grub_err_t
+grub_efiemu_add_to_mmap (grub_uint64_t start, grub_uint64_t size,
+ grub_efi_memory_type_t type)
+{
+ grub_uint64_t page_start, npages;
+
+ /* Extend map if necessary*/
+ if (mmap_num >= mmap_reserved_size)
+ {
+ void *old;
+ mmap_reserved_size = 2 * (mmap_reserved_size + 1);
+ old = efiemu_mmap;
+ efiemu_mmap = (grub_efi_memory_descriptor_t *)
+ grub_realloc (efiemu_mmap, mmap_reserved_size
+ * sizeof (grub_efi_memory_descriptor_t));
+ if (!efiemu_mmap)
+ {
+ grub_free (old);
+ return grub_errno;
+ }
+ }
+
+ /* Fill slot*/
+ page_start = start - (start % GRUB_EFIEMU_PAGESIZE);
+ npages = (size + (start % GRUB_EFIEMU_PAGESIZE) + GRUB_EFIEMU_PAGESIZE - 1)
+ / GRUB_EFIEMU_PAGESIZE;
+ efiemu_mmap[mmap_num].physical_start = page_start;
+ efiemu_mmap[mmap_num].virtual_start = page_start;
+ efiemu_mmap[mmap_num].num_pages = npages;
+ efiemu_mmap[mmap_num].type = type;
+ mmap_num++;
+
+ return GRUB_ERR_NONE;
+}
+
+/* Request a resident memory of type TYPE of size SIZE aligned at ALIGN
+ ALIGN must be a divisor of page size (if it's a divisor of 4096
+ it should be ok on all platforms)
+ */
+int
+grub_efiemu_request_memalign (grub_size_t align, grub_size_t size,
+ grub_efi_memory_type_t type)
+{
+ grub_size_t align_overhead;
+ struct grub_efiemu_memrequest *ret, *cur, *prev;
+ /* Check that the request is correct */
+ if (type <= GRUB_EFI_LOADER_CODE || type == GRUB_EFI_PERSISTENT_MEMORY ||
+ type >= GRUB_EFI_MAX_MEMORY_TYPE)
+ return -2;
+
+ /* Add new size to requested size */
+ align_overhead = align - (requested_memory[type]%align);
+ if (align_overhead == align)
+ align_overhead = 0;
+ requested_memory[type] += align_overhead + size;
+
+ /* Remember the request */
+ ret = grub_zalloc (sizeof (*ret));
+ if (!ret)
+ return -1;
+ ret->type = type;
+ ret->size = size;
+ ret->align_overhead = align_overhead;
+ prev = 0;
+
+ /* Add request to the end of the chain.
+ It should be at the end because otherwise alignment isn't guaranteed */
+ for (cur = memrequests; cur; prev = cur, cur = cur->next);
+ if (prev)
+ {
+ ret->handle = prev->handle + 1;
+ prev->next = ret;
+ }
+ else
+ {
+ ret->handle = 1; /* Avoid 0 handle*/
+ memrequests = ret;
+ }
+ return ret->handle;
+}
+
+/* Really allocate the memory */
+static grub_err_t
+efiemu_alloc_requests (void)
+{
+ grub_size_t align_overhead = 0;
+ grub_uint8_t *curptr, *typestart;
+ struct grub_efiemu_memrequest *cur;
+ grub_size_t total_alloc = 0;
+ unsigned i;
+ /* Order of memory regions */
+ grub_efi_memory_type_t reqorder[] =
+ {
+ /* First come regions usable by OS*/
+ GRUB_EFI_LOADER_CODE,
+ GRUB_EFI_LOADER_DATA,
+ GRUB_EFI_BOOT_SERVICES_CODE,
+ GRUB_EFI_BOOT_SERVICES_DATA,
+ GRUB_EFI_CONVENTIONAL_MEMORY,
+ GRUB_EFI_ACPI_RECLAIM_MEMORY,
+
+ /* Then memory used by runtime */
+ /* This way all our regions are in a single block */
+ GRUB_EFI_RUNTIME_SERVICES_CODE,
+ GRUB_EFI_RUNTIME_SERVICES_DATA,
+ GRUB_EFI_ACPI_MEMORY_NVS,
+
+ /* And then unavailable memory types. This is more for a completeness.
+ You should double think before allocating memory of any of these types
+ */
+ GRUB_EFI_UNUSABLE_MEMORY,
+ GRUB_EFI_MEMORY_MAPPED_IO,
+ GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE,
+ GRUB_EFI_PAL_CODE
+
+ /*
+ * These are not allocatable:
+ * GRUB_EFI_RESERVED_MEMORY_TYPE
+ * GRUB_EFI_PERSISTENT_MEMORY
+ * >= GRUB_EFI_MAX_MEMORY_TYPE
+ */
+ };
+
+ /* Compute total memory needed */
+ for (i = 0; i < sizeof (reqorder) / sizeof (reqorder[0]); i++)
+ {
+ align_overhead = GRUB_EFIEMU_PAGESIZE
+ - (requested_memory[reqorder[i]] % GRUB_EFIEMU_PAGESIZE);
+ if (align_overhead == GRUB_EFIEMU_PAGESIZE)
+ align_overhead = 0;
+ total_alloc += requested_memory[reqorder[i]] + align_overhead;
+ }
+
+ /* Allocate the whole memory in one block */
+ resident_memory = grub_memalign (GRUB_EFIEMU_PAGESIZE, total_alloc);
+ if (!resident_memory)
+ return grub_errno;
+
+ /* Split the memory into blocks by type */
+ curptr = resident_memory;
+ for (i = 0; i < sizeof (reqorder) / sizeof (reqorder[0]); i++)
+ {
+ if (!requested_memory[reqorder[i]])
+ continue;
+ typestart = curptr;
+
+ /* Write pointers to requests */
+ for (cur = memrequests; cur; cur = cur->next)
+ if (cur->type == reqorder[i])
+ {
+ curptr = ((grub_uint8_t *)curptr) + cur->align_overhead;
+ cur->val = curptr;
+ curptr = ((grub_uint8_t *)curptr) + cur->size;
+ }
+
+ /* Ensure that the regions are page-aligned */
+ align_overhead = GRUB_EFIEMU_PAGESIZE
+ - (requested_memory[reqorder[i]] % GRUB_EFIEMU_PAGESIZE);
+ if (align_overhead == GRUB_EFIEMU_PAGESIZE)
+ align_overhead = 0;
+ curptr = ((grub_uint8_t *) curptr) + align_overhead;
+
+ /* Add the region to memory map */
+ grub_efiemu_add_to_mmap ((grub_addr_t) typestart,
+ curptr - typestart, reqorder[i]);
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/* Get a pointer to requested memory from handle */
+void *
+grub_efiemu_mm_obtain_request (int handle)
+{
+ struct grub_efiemu_memrequest *cur;
+ for (cur = memrequests; cur; cur = cur->next)
+ if (cur->handle == handle)
+ return cur->val;
+ return 0;
+}
+
+/* Get type of requested memory by handle */
+grub_efi_memory_type_t
+grub_efiemu_mm_get_type (int handle)
+{
+ struct grub_efiemu_memrequest *cur;
+ for (cur = memrequests; cur; cur = cur->next)
+ if (cur->handle == handle)
+ return cur->type;
+ return 0;
+}
+
+/* Free a request */
+void
+grub_efiemu_mm_return_request (int handle)
+{
+ struct grub_efiemu_memrequest *cur, *prev;
+
+ /* Remove head if necessary */
+ while (memrequests && memrequests->handle == handle)
+ {
+ cur = memrequests->next;
+ grub_free (memrequests);
+ memrequests = cur;
+ }
+ if (!memrequests)
+ return;
+
+ /* Remove request from a middle of chain*/
+ for (prev = memrequests, cur = prev->next; cur;)
+ if (cur->handle == handle)
+ {
+ prev->next = cur->next;
+ grub_free (cur);
+ cur = prev->next;
+ }
+ else
+ {
+ prev = cur;
+ cur = prev->next;
+ }
+}
+
+/* Helper for grub_efiemu_mmap_init. */
+static int
+bounds_hook (grub_uint64_t addr __attribute__ ((unused)),
+ grub_uint64_t size __attribute__ ((unused)),
+ grub_memory_type_t type __attribute__ ((unused)),
+ void *data __attribute__ ((unused)))
+{
+ mmap_reserved_size++;
+ return 0;
+}
+
+/* Reserve space for memory map */
+static grub_err_t
+grub_efiemu_mmap_init (void)
+{
+ // the place for memory used by efiemu itself
+ mmap_reserved_size = GRUB_EFI_MAX_MEMORY_TYPE + 1;
+
+#ifndef GRUB_MACHINE_EMU
+ grub_machine_mmap_iterate (bounds_hook, NULL);
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+/* This is a drop-in replacement of grub_efi_get_memory_map */
+/* 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_efiemu_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)
+{
+ if (!efiemu_mmap)
+ {
+ grub_error (GRUB_ERR_INVALID_COMMAND,
+ "you need to first launch efiemu_prepare");
+ return -1;
+ }
+
+ if (*memory_map_size < mmap_num * sizeof (grub_efi_memory_descriptor_t))
+ {
+ *memory_map_size = mmap_num * sizeof (grub_efi_memory_descriptor_t);
+ return 0;
+ }
+
+ *memory_map_size = mmap_num * sizeof (grub_efi_memory_descriptor_t);
+ grub_memcpy (memory_map, efiemu_mmap, *memory_map_size);
+ if (descriptor_size)
+ *descriptor_size = sizeof (grub_efi_memory_descriptor_t);
+ if (descriptor_version)
+ *descriptor_version = 1;
+ if (map_key)
+ *map_key = 0;
+
+ return 1;
+}
+
+grub_err_t
+grub_efiemu_finish_boot_services (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)
+{
+ int val = grub_efiemu_get_memory_map (memory_map_size,
+ memory_map, map_key,
+ descriptor_size,
+ descriptor_version);
+ if (val == 1)
+ return GRUB_ERR_NONE;
+ if (val == -1)
+ return grub_errno;
+ return grub_error (GRUB_ERR_IO, "memory map buffer is too small");
+}
+
+
+/* Free everything */
+grub_err_t
+grub_efiemu_mm_unload (void)
+{
+ struct grub_efiemu_memrequest *cur, *d;
+ for (cur = memrequests; cur;)
+ {
+ d = cur->next;
+ grub_free (cur);
+ cur = d;
+ }
+ memrequests = 0;
+ grub_memset (&requested_memory, 0, sizeof (requested_memory));
+ grub_free (resident_memory);
+ resident_memory = 0;
+ grub_free (efiemu_mmap);
+ efiemu_mmap = 0;
+ mmap_reserved_size = mmap_num = 0;
+ return GRUB_ERR_NONE;
+}
+
+/* This function should be called before doing any requests */
+grub_err_t
+grub_efiemu_mm_init (void)
+{
+ grub_err_t err;
+
+ err = grub_efiemu_mm_unload ();
+ if (err)
+ return err;
+
+ grub_efiemu_mmap_init ();
+
+ return GRUB_ERR_NONE;
+}
+
+/* Helper for grub_efiemu_mmap_fill. */
+static int
+fill_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type,
+ void *data __attribute__ ((unused)))
+ {
+ switch (type)
+ {
+ case GRUB_MEMORY_AVAILABLE:
+ return grub_efiemu_add_to_mmap (addr, size,
+ GRUB_EFI_CONVENTIONAL_MEMORY);
+
+ case GRUB_MEMORY_ACPI:
+ return grub_efiemu_add_to_mmap (addr, size,
+ GRUB_EFI_ACPI_RECLAIM_MEMORY);
+
+ case GRUB_MEMORY_NVS:
+ return grub_efiemu_add_to_mmap (addr, size,
+ GRUB_EFI_ACPI_MEMORY_NVS);
+
+ case GRUB_MEMORY_PERSISTENT:
+ case GRUB_MEMORY_PERSISTENT_LEGACY:
+ return grub_efiemu_add_to_mmap (addr, size,
+ GRUB_EFI_PERSISTENT_MEMORY);
+ default:
+ grub_dprintf ("efiemu",
+ "Unknown memory type %d. Assuming unusable\n", type);
+ /* FALLTHROUGH */
+ case GRUB_MEMORY_RESERVED:
+ return grub_efiemu_add_to_mmap (addr, size,
+ GRUB_EFI_UNUSABLE_MEMORY);
+ }
+ }
+
+/* Copy host memory map */
+static grub_err_t
+grub_efiemu_mmap_fill (void)
+{
+#ifndef GRUB_MACHINE_EMU
+ grub_machine_mmap_iterate (fill_hook, NULL);
+#endif
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_efiemu_mmap_iterate (grub_memory_hook_t hook, void *hook_data)
+{
+ unsigned i;
+
+ for (i = 0; i < (unsigned) mmap_num; i++)
+ switch (efiemu_mmap[i].type)
+ {
+ case GRUB_EFI_RUNTIME_SERVICES_CODE:
+ hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096,
+ GRUB_MEMORY_CODE, hook_data);
+ break;
+
+ case GRUB_EFI_UNUSABLE_MEMORY:
+ hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096,
+ GRUB_MEMORY_BADRAM, hook_data);
+ break;
+
+ case GRUB_EFI_RESERVED_MEMORY_TYPE:
+ case GRUB_EFI_RUNTIME_SERVICES_DATA:
+ case GRUB_EFI_MEMORY_MAPPED_IO:
+ case GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+ case GRUB_EFI_PAL_CODE:
+ default:
+ hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096,
+ GRUB_MEMORY_RESERVED, hook_data);
+ break;
+
+ case GRUB_EFI_LOADER_CODE:
+ case GRUB_EFI_LOADER_DATA:
+ case GRUB_EFI_BOOT_SERVICES_CODE:
+ case GRUB_EFI_BOOT_SERVICES_DATA:
+ case GRUB_EFI_CONVENTIONAL_MEMORY:
+ hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096,
+ GRUB_MEMORY_AVAILABLE, hook_data);
+ break;
+
+ case GRUB_EFI_ACPI_RECLAIM_MEMORY:
+ hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096,
+ GRUB_MEMORY_ACPI, hook_data);
+ break;
+
+ case GRUB_EFI_ACPI_MEMORY_NVS:
+ hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096,
+ GRUB_MEMORY_NVS, hook_data);
+ break;
+
+ case GRUB_EFI_PERSISTENT_MEMORY:
+ hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096,
+ GRUB_MEMORY_PERSISTENT, hook_data);
+ break;
+
+ }
+
+ return 0;
+}
+
+
+/* This function resolves overlapping regions and sorts the memory map
+ It uses scanline (sweeping) algorithm
+ */
+static grub_err_t
+grub_efiemu_mmap_sort_and_uniq (void)
+{
+ /* If same page is used by multiple types it's resolved
+ according to priority
+ 0 - free memory
+ 1 - memory immediately usable after ExitBootServices
+ 2 - memory usable after loading ACPI tables
+ 3 - efiemu memory
+ 4 - unusable memory
+ */
+ int priority[GRUB_EFI_MAX_MEMORY_TYPE] =
+ {
+ [GRUB_EFI_RESERVED_MEMORY_TYPE] = 4,
+ [GRUB_EFI_LOADER_CODE] = 1,
+ [GRUB_EFI_LOADER_DATA] = 1,
+ [GRUB_EFI_BOOT_SERVICES_CODE] = 1,
+ [GRUB_EFI_BOOT_SERVICES_DATA] = 1,
+ [GRUB_EFI_RUNTIME_SERVICES_CODE] = 3,
+ [GRUB_EFI_RUNTIME_SERVICES_DATA] = 3,
+ [GRUB_EFI_CONVENTIONAL_MEMORY] = 0,
+ [GRUB_EFI_UNUSABLE_MEMORY] = 4,
+ [GRUB_EFI_ACPI_RECLAIM_MEMORY] = 2,
+ [GRUB_EFI_ACPI_MEMORY_NVS] = 3,
+ [GRUB_EFI_MEMORY_MAPPED_IO] = 4,
+ [GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE] = 4,
+ [GRUB_EFI_PAL_CODE] = 4,
+ [GRUB_EFI_PERSISTENT_MEMORY] = 4
+ };
+
+ int i, j, k, done;
+
+ /* Scanline events */
+ struct grub_efiemu_mmap_scan
+ {
+ /* At which memory address*/
+ grub_uint64_t pos;
+ /* 0 = region starts, 1 = region ends */
+ int type;
+ /* Which type of memory region */
+ grub_efi_memory_type_t memtype;
+ };
+ struct grub_efiemu_mmap_scan *scanline_events;
+ struct grub_efiemu_mmap_scan t;
+
+ /* Previous scanline event */
+ grub_uint64_t lastaddr;
+ int lasttype;
+ /* Current scanline event */
+ int curtype;
+ /* how many regions of given type overlap at current location */
+ int present[GRUB_EFI_MAX_MEMORY_TYPE];
+ /* Here is stored the resulting memory map*/
+ grub_efi_memory_descriptor_t *result;
+
+ /* Initialize variables*/
+ grub_memset (present, 0, sizeof (int) * GRUB_EFI_MAX_MEMORY_TYPE);
+ scanline_events = (struct grub_efiemu_mmap_scan *)
+ grub_calloc (mmap_num, sizeof (struct grub_efiemu_mmap_scan) * 2);
+
+ /* Number of chunks can't increase more than by factor of 2 */
+ result = (grub_efi_memory_descriptor_t *)
+ grub_calloc (mmap_num, sizeof (grub_efi_memory_descriptor_t) * 2);
+ if (!result || !scanline_events)
+ {
+ grub_free (result);
+ grub_free (scanline_events);
+ return grub_errno;
+ }
+
+ /* Register scanline events */
+ for (i = 0; i < mmap_num; i++)
+ {
+ scanline_events[2 * i].pos = efiemu_mmap[i].physical_start;
+ scanline_events[2 * i].type = 0;
+ scanline_events[2 * i].memtype = efiemu_mmap[i].type;
+ scanline_events[2 * i + 1].pos = efiemu_mmap[i].physical_start
+ + efiemu_mmap[i].num_pages * GRUB_EFIEMU_PAGESIZE;
+ scanline_events[2 * i + 1].type = 1;
+ scanline_events[2 * i + 1].memtype = efiemu_mmap[i].type;
+ }
+
+ /* Primitive bubble sort. It has complexity O(n^2) but since we're
+ unlikely to have more than 100 chunks it's probably one of the
+ fastest for one purpose */
+ done = 1;
+ while (done)
+ {
+ done = 0;
+ for (i = 0; i < 2 * mmap_num - 1; i++)
+ if (scanline_events[i + 1].pos < scanline_events[i].pos)
+ {
+ t = scanline_events[i + 1];
+ scanline_events[i + 1] = scanline_events[i];
+ scanline_events[i] = t;
+ done = 1;
+ }
+ }
+
+ /* Pointer in resulting memory map */
+ j = 0;
+ lastaddr = scanline_events[0].pos;
+ lasttype = scanline_events[0].memtype;
+ for (i = 0; i < 2 * mmap_num; i++)
+ {
+ /* Process event */
+ if (scanline_events[i].type)
+ present[scanline_events[i].memtype]--;
+ else
+ present[scanline_events[i].memtype]++;
+
+ /* Determine current region type */
+ curtype = -1;
+ for (k = 0; k < GRUB_EFI_MAX_MEMORY_TYPE; k++)
+ if (present[k] && (curtype == -1 || priority[k] > priority[curtype]))
+ curtype = k;
+
+ /* Add memory region to resulting map if necessary */
+ if ((curtype == -1 || curtype != lasttype)
+ && lastaddr != scanline_events[i].pos
+ && lasttype != -1)
+ {
+ result[j].virtual_start = result[j].physical_start = lastaddr;
+ result[j].num_pages = (scanline_events[i].pos - lastaddr)
+ / GRUB_EFIEMU_PAGESIZE;
+ result[j].type = lasttype;
+
+ /* We set runtime attribute on pages we need to be mapped */
+ result[j].attribute
+ = (lasttype == GRUB_EFI_RUNTIME_SERVICES_CODE
+ || lasttype == GRUB_EFI_RUNTIME_SERVICES_DATA)
+ ? GRUB_EFI_MEMORY_RUNTIME : 0;
+ grub_dprintf ("efiemu",
+ "mmap entry: type %d start 0x%llx 0x%llx pages\n",
+ result[j].type,
+ result[j].physical_start, result[j].num_pages);
+ j++;
+ }
+
+ /* Update last values if necessary */
+ if (curtype == -1 || curtype != lasttype)
+ {
+ lasttype = curtype;
+ lastaddr = scanline_events[i].pos;
+ }
+ }
+
+ grub_free (scanline_events);
+
+ /* Shrink resulting memory map to really used size and replace efiemu_mmap
+ by new value */
+ grub_free (efiemu_mmap);
+ efiemu_mmap = grub_realloc (result, j * sizeof (*result));
+ return GRUB_ERR_NONE;
+}
+
+/* This function is called to switch from first to second phase */
+grub_err_t
+grub_efiemu_mm_do_alloc (void)
+{
+ grub_err_t err;
+
+ /* Preallocate mmap */
+ efiemu_mmap = (grub_efi_memory_descriptor_t *)
+ grub_calloc (mmap_reserved_size, sizeof (grub_efi_memory_descriptor_t));
+ if (!efiemu_mmap)
+ {
+ grub_efiemu_unload ();
+ return grub_errno;
+ }
+
+ err = efiemu_alloc_requests ();
+ if (err)
+ return err;
+ err = grub_efiemu_mmap_fill ();
+ if (err)
+ return err;
+ return grub_efiemu_mmap_sort_and_uniq ();
+}
diff --git a/grub-core/efiemu/pnvram.c b/grub-core/efiemu/pnvram.c
new file mode 100644
index 0000000..dd42bc6
--- /dev/null
+++ b/grub-core/efiemu/pnvram.c
@@ -0,0 +1,269 @@
+/* Export pnvram and some variables for runtime */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/normal.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/charset.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/efiemu/runtime.h>
+#include <grub/extcmd.h>
+
+/* Place for final location of variables */
+static int nvram_handle = 0;
+static int nvramsize_handle = 0;
+static int high_monotonic_count_handle = 0;
+static int timezone_handle = 0;
+static int accuracy_handle = 0;
+static int daylight_handle = 0;
+
+static grub_size_t nvramsize;
+
+/* Parse signed value */
+static int
+grub_strtosl (const char *arg, const char ** const end, int base)
+{
+ if (arg[0] == '-')
+ return -grub_strtoul (arg + 1, end, base);
+ return grub_strtoul (arg, end, base);
+}
+
+static inline int
+hextoval (char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
+ return 0;
+}
+
+static inline grub_err_t
+unescape (char *in, char *out, char *outmax, int *len)
+{
+ char *ptr, *dptr;
+ dptr = out;
+ for (ptr = in; *ptr && dptr < outmax; )
+ if (*ptr == '%' && ptr[1] && ptr[2])
+ {
+ *dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2]));
+ ptr += 3;
+ dptr++;
+ }
+ else
+ {
+ *dptr = *ptr;
+ ptr++;
+ dptr++;
+ }
+ if (dptr == outmax)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "too many NVRAM variables for reserved variable space."
+ " Try increasing EfiEmu.pnvram.size");
+ *len = dptr - out;
+ return 0;
+}
+
+/* Export stuff for efiemu */
+static grub_err_t
+nvram_set (void * data __attribute__ ((unused)))
+{
+ const char *env;
+ /* Take definitive pointers */
+ char *nvram = grub_efiemu_mm_obtain_request (nvram_handle);
+ grub_uint32_t *nvramsize_def
+ = grub_efiemu_mm_obtain_request (nvramsize_handle);
+ grub_uint32_t *high_monotonic_count
+ = grub_efiemu_mm_obtain_request (high_monotonic_count_handle);
+ grub_int16_t *timezone
+ = grub_efiemu_mm_obtain_request (timezone_handle);
+ grub_uint8_t *daylight
+ = grub_efiemu_mm_obtain_request (daylight_handle);
+ grub_uint32_t *accuracy
+ = grub_efiemu_mm_obtain_request (accuracy_handle);
+ char *nvramptr;
+ struct grub_env_var *var;
+
+ /* Copy to definitive loaction */
+ grub_dprintf ("efiemu", "preparing pnvram\n");
+
+ env = grub_env_get ("EfiEmu.pnvram.high_monotonic_count");
+ *high_monotonic_count = env ? grub_strtoul (env, 0, 0) : 1;
+ env = grub_env_get ("EfiEmu.pnvram.timezone");
+ *timezone = env ? grub_strtosl (env, 0, 0) : GRUB_EFI_UNSPECIFIED_TIMEZONE;
+ env = grub_env_get ("EfiEmu.pnvram.accuracy");
+ *accuracy = env ? grub_strtoul (env, 0, 0) : 50000000;
+ env = grub_env_get ("EfiEmu.pnvram.daylight");
+ *daylight = env ? grub_strtoul (env, 0, 0) : 0;
+
+ nvramptr = nvram;
+ grub_memset (nvram, 0, nvramsize);
+ FOR_SORTED_ENV (var)
+ {
+ const char *guid;
+ char *attr, *name, *varname;
+ struct efi_variable *efivar;
+ int len = 0;
+ int i;
+ grub_uint64_t guidcomp;
+
+ if (grub_memcmp (var->name, "EfiEmu.pnvram.",
+ sizeof ("EfiEmu.pnvram.") - 1) != 0)
+ continue;
+
+ guid = var->name + sizeof ("EfiEmu.pnvram.") - 1;
+
+ attr = grub_strchr (guid, '.');
+ if (!attr)
+ continue;
+ attr++;
+
+ name = grub_strchr (attr, '.');
+ if (!name)
+ continue;
+ name++;
+
+ efivar = (struct efi_variable *) nvramptr;
+ if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "too many NVRAM variables for reserved variable space."
+ " Try increasing EfiEmu.pnvram.size");
+
+ nvramptr += sizeof (struct efi_variable);
+
+ efivar->guid.data1 = grub_cpu_to_le32 (grub_strtoul (guid, &guid, 16));
+ if (*guid != '-')
+ continue;
+ guid++;
+
+ efivar->guid.data2 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16));
+ if (*guid != '-')
+ continue;
+ guid++;
+
+ efivar->guid.data3 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16));
+ if (*guid != '-')
+ continue;
+ guid++;
+
+ guidcomp = grub_strtoull (guid, 0, 16);
+ for (i = 0; i < 8; i++)
+ efivar->guid.data4[i] = (guidcomp >> (56 - 8 * i)) & 0xff;
+
+ efivar->attributes = grub_strtoull (attr, 0, 16);
+
+ varname = grub_malloc (grub_strlen (name) + 1);
+ if (! varname)
+ return grub_errno;
+
+ if (unescape (name, varname, varname + grub_strlen (name) + 1, &len))
+ break;
+
+ len = grub_utf8_to_utf16 ((grub_uint16_t *) nvramptr,
+ (nvramsize - (nvramptr - nvram)) / 2,
+ (grub_uint8_t *) varname, len, NULL);
+
+ nvramptr += 2 * len;
+ *((grub_uint16_t *) nvramptr) = 0;
+ nvramptr += 2;
+ efivar->namelen = 2 * len + 2;
+
+ if (unescape (var->value, nvramptr, nvram + nvramsize, &len))
+ {
+ efivar->namelen = 0;
+ break;
+ }
+
+ nvramptr += len;
+
+ efivar->size = len;
+ }
+ if (grub_errno)
+ return grub_errno;
+
+ *nvramsize_def = nvramsize;
+
+ /* Register symbols */
+ grub_efiemu_register_symbol ("efiemu_variables", nvram_handle, 0);
+ grub_efiemu_register_symbol ("efiemu_varsize", nvramsize_handle, 0);
+ grub_efiemu_register_symbol ("efiemu_high_monotonic_count",
+ high_monotonic_count_handle, 0);
+ grub_efiemu_register_symbol ("efiemu_time_zone", timezone_handle, 0);
+ grub_efiemu_register_symbol ("efiemu_time_daylight", daylight_handle, 0);
+ grub_efiemu_register_symbol ("efiemu_time_accuracy",
+ accuracy_handle, 0);
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+nvram_unload (void * data __attribute__ ((unused)))
+{
+ grub_efiemu_mm_return_request (nvram_handle);
+ grub_efiemu_mm_return_request (nvramsize_handle);
+ grub_efiemu_mm_return_request (high_monotonic_count_handle);
+ grub_efiemu_mm_return_request (timezone_handle);
+ grub_efiemu_mm_return_request (accuracy_handle);
+ grub_efiemu_mm_return_request (daylight_handle);
+}
+
+grub_err_t
+grub_efiemu_pnvram (void)
+{
+ const char *size;
+ grub_err_t err;
+
+ nvramsize = 0;
+
+ size = grub_env_get ("EfiEmu.pnvram.size");
+ if (size)
+ nvramsize = grub_strtoul (size, 0, 0);
+
+ if (!nvramsize)
+ nvramsize = 2048;
+
+ err = grub_efiemu_register_prepare_hook (nvram_set, nvram_unload, 0);
+ if (err)
+ return err;
+
+ nvram_handle
+ = grub_efiemu_request_memalign (1, nvramsize,
+ GRUB_EFI_RUNTIME_SERVICES_DATA);
+ nvramsize_handle
+ = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
+ GRUB_EFI_RUNTIME_SERVICES_DATA);
+ high_monotonic_count_handle
+ = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
+ GRUB_EFI_RUNTIME_SERVICES_DATA);
+ timezone_handle
+ = grub_efiemu_request_memalign (1, sizeof (grub_uint16_t),
+ GRUB_EFI_RUNTIME_SERVICES_DATA);
+ daylight_handle
+ = grub_efiemu_request_memalign (1, sizeof (grub_uint8_t),
+ GRUB_EFI_RUNTIME_SERVICES_DATA);
+ accuracy_handle
+ = grub_efiemu_request_memalign (1, sizeof (grub_uint32_t),
+ GRUB_EFI_RUNTIME_SERVICES_DATA);
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/efiemu/prepare.c b/grub-core/efiemu/prepare.c
new file mode 100644
index 0000000..99ddb5a
--- /dev/null
+++ b/grub-core/efiemu/prepare.c
@@ -0,0 +1,169 @@
+/* Prepare efiemu. E.g. allocate memory, load the runtime
+ to appropriate place, etc */
+/*
+ * 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/err.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/crypto.h>
+
+grub_err_t
+SUFFIX (grub_efiemu_prepare) (struct grub_efiemu_prepare_hook *prepare_hooks,
+ struct grub_efiemu_configuration_table
+ *config_tables)
+{
+ grub_err_t err;
+ int conftable_handle;
+ struct grub_efiemu_configuration_table *cur;
+ struct grub_efiemu_prepare_hook *curhook;
+
+ int cntconftables = 0;
+ struct SUFFIX (grub_efiemu_configuration_table) *conftables = 0;
+ int i;
+ int handle;
+ grub_off_t off;
+
+ grub_dprintf ("efiemu", "Preparing EfiEmu\n");
+
+ /* Request space for the list of configuration tables */
+ for (cur = config_tables; cur; cur = cur->next)
+ cntconftables++;
+ conftable_handle
+ = grub_efiemu_request_memalign (GRUB_EFIEMU_PAGESIZE,
+ cntconftables * sizeof (*conftables),
+ GRUB_EFI_RUNTIME_SERVICES_DATA);
+
+ /* Switch from phase 1 (counting) to phase 2 (real job) */
+ grub_efiemu_alloc_syms ();
+ grub_efiemu_mm_do_alloc ();
+ grub_efiemu_write_sym_markers ();
+
+ grub_efiemu_system_table32 = 0;
+ grub_efiemu_system_table64 = 0;
+
+ /* Execute hooks */
+ for (curhook = prepare_hooks; curhook; curhook = curhook->next)
+ curhook->hook (curhook->data);
+
+ /* Move runtime to its due place */
+ err = grub_efiemu_loadcore_load ();
+ if (err)
+ {
+ grub_efiemu_unload ();
+ return err;
+ }
+
+ err = grub_efiemu_resolve_symbol ("efiemu_system_table", &handle, &off);
+ if (err)
+ {
+ grub_efiemu_unload ();
+ return err;
+ }
+
+ SUFFIX (grub_efiemu_system_table)
+ = (struct SUFFIX (grub_efi_system_table) *)
+ ((grub_uint8_t *) grub_efiemu_mm_obtain_request (handle) + off);
+
+ /* Put pointer to the list of configuration tables in system table */
+ err = grub_efiemu_write_value
+ (&(SUFFIX (grub_efiemu_system_table)->configuration_table), 0,
+ conftable_handle, 0, 1,
+ sizeof (SUFFIX (grub_efiemu_system_table)->configuration_table));
+ if (err)
+ {
+ grub_efiemu_unload ();
+ return err;
+ }
+
+ SUFFIX(grub_efiemu_system_table)->num_table_entries = cntconftables;
+
+ /* Fill the list of configuration tables */
+ conftables = (struct SUFFIX (grub_efiemu_configuration_table) *)
+ grub_efiemu_mm_obtain_request (conftable_handle);
+ i = 0;
+ for (cur = config_tables; cur; cur = cur->next, i++)
+ {
+ grub_memcpy (&(conftables[i].vendor_guid), &(cur->guid),
+ sizeof (cur->guid));
+ if (cur->get_table)
+ conftables[i].vendor_table = (grub_addr_t) cur->get_table (cur->data);
+ else
+ conftables[i].vendor_table = (grub_addr_t) cur->data;
+ }
+
+ err = SUFFIX (grub_efiemu_crc) ();
+ if (err)
+ {
+ grub_efiemu_unload ();
+ return err;
+ }
+
+ grub_dprintf ("efiemu","system_table = %p, conftables = %p (%d entries)\n",
+ SUFFIX (grub_efiemu_system_table), conftables, cntconftables);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+SUFFIX (grub_efiemu_crc) (void)
+{
+ grub_err_t err;
+ int handle;
+ grub_off_t off;
+ struct SUFFIX (grub_efiemu_runtime_services) *runtime_services;
+ grub_uint32_t crc32_val;
+
+ if (GRUB_MD_CRC32->mdlen != 4)
+ return grub_error (GRUB_ERR_BUG, "incorrect mdlen");
+
+ /* compute CRC32 of runtime_services */
+ err = grub_efiemu_resolve_symbol ("efiemu_runtime_services",
+ &handle, &off);
+ if (err)
+ return err;
+
+ runtime_services = (struct SUFFIX (grub_efiemu_runtime_services) *)
+ ((grub_uint8_t *) grub_efiemu_mm_obtain_request (handle) + off);
+
+ runtime_services->hdr.crc32 = 0;
+
+ grub_crypto_hash (GRUB_MD_CRC32, &crc32_val,
+ runtime_services, runtime_services->hdr.header_size);
+ runtime_services->hdr.crc32 =
+ grub_be_to_cpu32(crc32_val);
+
+ err = grub_efiemu_resolve_symbol ("efiemu_system_table", &handle, &off);
+ if (err)
+ return err;
+
+ /* compute CRC32 of system table */
+ SUFFIX (grub_efiemu_system_table)->hdr.crc32 = 0;
+ grub_crypto_hash (GRUB_MD_CRC32, &crc32_val,
+ SUFFIX (grub_efiemu_system_table),
+ SUFFIX (grub_efiemu_system_table)->hdr.header_size);
+ SUFFIX (grub_efiemu_system_table)->hdr.crc32 =
+ grub_be_to_cpu32(crc32_val);
+
+ grub_dprintf ("efiemu","system_table = %p, runtime_services = %p\n",
+ SUFFIX (grub_efiemu_system_table), runtime_services);
+
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/efiemu/prepare32.c b/grub-core/efiemu/prepare32.c
new file mode 100644
index 0000000..fd6109e
--- /dev/null
+++ b/grub-core/efiemu/prepare32.c
@@ -0,0 +1,22 @@
+/* This file contains definitions so that prepare.c compiles for 32-bit */
+/*
+ * 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/>.
+ */
+
+#define SUFFIX(x) x ## 32
+
+#include "prepare.c"
diff --git a/grub-core/efiemu/prepare64.c b/grub-core/efiemu/prepare64.c
new file mode 100644
index 0000000..811f558
--- /dev/null
+++ b/grub-core/efiemu/prepare64.c
@@ -0,0 +1,22 @@
+/* This file contains definitions so that prepare.c compiles for 64-bit */
+/*
+ * 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/>.
+ */
+
+#define SUFFIX(x) x ## 64
+
+#include "prepare.c"
diff --git a/grub-core/efiemu/runtime/config.h b/grub-core/efiemu/runtime/config.h
new file mode 100644
index 0000000..c9fe027
--- /dev/null
+++ b/grub-core/efiemu/runtime/config.h
@@ -0,0 +1,36 @@
+/* This is a pseudo config.h so that types.h compiles nicely */
+/*
+ * 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/>.
+ */
+
+#define GRUB_TYPES_CPU_HEADER 1
+
+#ifdef __i386__
+# define SIZEOF_VOID_P 4
+# define SIZEOF_LONG 4
+# define GRUB_TARGET_SIZEOF_VOID_P 4
+# define GRUB_TARGET_SIZEOF_LONG 4
+# define EFI_FUNC(x) x
+#elif defined (__x86_64__)
+# define SIZEOF_VOID_P 8
+# define SIZEOF_LONG 8
+# define GRUB_TARGET_SIZEOF_VOID_P 8
+# define GRUB_TARGET_SIZEOF_LONG 8
+# define EFI_FUNC(x) x ## _real
+#else
+#error "Unknown architecture"
+#endif
diff --git a/grub-core/efiemu/runtime/efiemu.S b/grub-core/efiemu/runtime/efiemu.S
new file mode 100644
index 0000000..b502314
--- /dev/null
+++ b/grub-core/efiemu/runtime/efiemu.S
@@ -0,0 +1,159 @@
+/*
+ * 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 <grub/symbol.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, %rsi, %rdx, %rcx, %r8, %r9, 8(%rsp), 16(%rsp), ...
+ *
+ * efi:
+ * %rcx, %rdx, %r8, %r9, 32(%rsp), 40(%rsp), 48(%rsp), ...
+ *
+ */
+
+ .file "efiemu.S"
+ .text
+
+FUNCTION (efiemu_get_time)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ mov %rdx, %rsi
+ call efiemu_get_time_real
+ pop %rsi
+ pop %rdi
+ ret
+
+FUNCTION (efiemu_set_time)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ call efiemu_set_time_real
+ pop %rsi
+ pop %rdi
+ ret
+
+
+FUNCTION (efiemu_get_wakeup_time)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ mov %rdx, %rsi
+ mov %r8, %rdx
+ call efiemu_get_wakeup_time_real
+ pop %rsi
+ pop %rdi
+ ret
+
+FUNCTION (efiemu_set_wakeup_time)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ mov %rdx, %rsi
+ call efiemu_set_wakeup_time_real
+ pop %rsi
+ pop %rdi
+ ret
+
+FUNCTION (efiemu_get_variable)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ mov %rdx, %rsi
+ mov %r8, %rdx
+ mov %r9, %rcx
+ mov 56(%rsp), %r8
+ call efiemu_get_variable_real
+ pop %rsi
+ pop %rdi
+ ret
+
+FUNCTION (efiemu_get_next_variable_name)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ mov %rdx, %rsi
+ mov %r8, %rdx
+ call efiemu_get_next_variable_name_real
+ pop %rsi
+ pop %rdi
+ ret
+
+FUNCTION (efiemu_set_variable)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ mov %rdx, %rsi
+ mov %r8, %rdx
+ mov %r9, %rcx
+ mov 56(%rsp), %r8
+ call efiemu_set_variable_real
+ pop %rsi
+ pop %rdi
+ ret
+
+FUNCTION (efiemu_get_next_high_monotonic_count)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ call efiemu_get_next_high_monotonic_count_real
+ pop %rsi
+ pop %rdi
+ ret
+
+FUNCTION (efiemu_reset_system)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ mov %rdx, %rsi
+ mov %r8, %rdx
+ mov %r9, %rcx
+ call efiemu_reset_system_real
+ pop %rsi
+ pop %rdi
+ ret
+
+ /* The following functions are always called in physical mode */
+ .section ".text-physical", "ax"
+
+FUNCTION (efiemu_set_virtual_address_map)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ mov %rdx, %rsi
+ mov %r8, %rdx
+ mov %r9, %rcx
+ call efiemu_set_virtual_address_map_real
+ pop %rsi
+ pop %rdi
+ ret
+
+FUNCTION (efiemu_convert_pointer)
+ push %rdi
+ push %rsi
+ mov %rcx, %rdi
+ mov %rdx, %rsi
+ call efiemu_convert_pointer_real
+ pop %rsi
+ pop %rdi
+ ret
+
diff --git a/grub-core/efiemu/runtime/efiemu.c b/grub-core/efiemu/runtime/efiemu.c
new file mode 100644
index 0000000..5db1f34
--- /dev/null
+++ b/grub-core/efiemu/runtime/efiemu.c
@@ -0,0 +1,636 @@
+/*
+ * 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/>.
+ */
+
+/* This is an emulation of EFI runtime services.
+ This allows a more uniform boot on i386 machines.
+ As it emulates only runtime serviceit isn't able
+ to chainload EFI bootloader on non-EFI system (TODO) */
+
+#ifdef __i386__
+#include <grub/i386/types.h>
+#else
+#include <grub/x86_64/types.h>
+#endif
+
+#include <grub/symbol.h>
+#include <grub/types.h>
+#include <grub/efi/api.h>
+#include <grub/efiemu/runtime.h>
+
+grub_efi_status_t
+efiemu_get_time (grub_efi_time_t *time,
+ grub_efi_time_capabilities_t *capabilities);
+grub_efi_status_t
+efiemu_set_time (grub_efi_time_t *time);
+
+grub_efi_status_t
+efiemu_get_wakeup_time (grub_efi_boolean_t *enabled,
+ grub_efi_boolean_t *pending,
+ grub_efi_time_t *time);
+grub_efi_status_t
+efiemu_set_wakeup_time (grub_efi_boolean_t enabled,
+ grub_efi_time_t *time);
+
+#ifdef __APPLE__
+#define PHYSICAL_ATTRIBUTE __attribute__ ((section("_text-physical, _text-physical")));
+#else
+#define PHYSICAL_ATTRIBUTE __attribute__ ((section(".text-physical")));
+#endif
+
+grub_efi_status_t
+efiemu_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)
+ PHYSICAL_ATTRIBUTE;
+
+grub_efi_status_t
+efiemu_convert_pointer (grub_efi_uintn_t debug_disposition,
+ void **address)
+ PHYSICAL_ATTRIBUTE;
+
+grub_efi_status_t
+efiemu_get_variable (grub_efi_char16_t *variable_name,
+ const grub_efi_guid_t *vendor_guid,
+ grub_efi_uint32_t *attributes,
+ grub_efi_uintn_t *data_size,
+ void *data);
+
+grub_efi_status_t
+efiemu_get_next_variable_name (grub_efi_uintn_t *variable_name_size,
+ grub_efi_char16_t *variable_name,
+ grub_efi_guid_t *vendor_guid);
+
+grub_efi_status_t
+efiemu_set_variable (grub_efi_char16_t *variable_name,
+ const grub_efi_guid_t *vendor_guid,
+ grub_efi_uint32_t attributes,
+ grub_efi_uintn_t data_size,
+ void *data);
+grub_efi_status_t
+efiemu_get_next_high_monotonic_count (grub_efi_uint32_t *high_count);
+void
+efiemu_reset_system (grub_efi_reset_type_t reset_type,
+ grub_efi_status_t reset_status,
+ grub_efi_uintn_t data_size,
+ grub_efi_char16_t *reset_data);
+
+grub_efi_status_t
+EFI_FUNC (efiemu_set_virtual_address_map) (grub_efi_uintn_t,
+ grub_efi_uintn_t,
+ grub_efi_uint32_t,
+ grub_efi_memory_descriptor_t *)
+ PHYSICAL_ATTRIBUTE;
+grub_efi_status_t
+EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition,
+ void **address)
+ PHYSICAL_ATTRIBUTE;
+static grub_uint32_t
+efiemu_getcrc32 (grub_uint32_t crc, void *buf, int size)
+ PHYSICAL_ATTRIBUTE;
+static void
+init_crc32_table (void)
+ PHYSICAL_ATTRIBUTE;
+static grub_uint32_t
+reflect (grub_uint32_t ref, int len)
+ PHYSICAL_ATTRIBUTE;
+
+/*
+ The log. It's used when examining memory dump
+*/
+static grub_uint8_t loge[1000] = "EFIEMULOG";
+static int logn = 9;
+#define LOG(x) { if (logn<900) loge[logn++]=x; }
+
+/* Interface with grub */
+extern grub_uint8_t efiemu_ptv_relocated;
+struct grub_efi_runtime_services efiemu_runtime_services;
+struct grub_efi_system_table efiemu_system_table;
+extern struct grub_efiemu_ptv_rel efiemu_ptv_relloc[];
+extern grub_uint8_t efiemu_variables[];
+extern grub_uint32_t efiemu_varsize;
+extern grub_uint32_t efiemu_high_monotonic_count;
+extern grub_int16_t efiemu_time_zone;
+extern grub_uint8_t efiemu_time_daylight;
+extern grub_uint32_t efiemu_time_accuracy;
+
+/* Some standard functions because we need to be standalone */
+static void
+efiemu_memcpy (void *to, const void *from, int count)
+{
+ int i;
+ for (i = 0; i < count; i++)
+ ((grub_uint8_t *) to)[i] = ((const grub_uint8_t *) from)[i];
+}
+
+static int
+efiemu_str16equal (grub_uint16_t *a, grub_uint16_t *b)
+{
+ grub_uint16_t *ptr1, *ptr2;
+ for (ptr1=a,ptr2=b; *ptr1 && *ptr2 == *ptr1; ptr1++, ptr2++);
+ return *ptr2 == *ptr1;
+}
+
+static grub_size_t
+efiemu_str16len (grub_uint16_t *a)
+{
+ grub_uint16_t *ptr1;
+ for (ptr1 = a; *ptr1; ptr1++);
+ return ptr1 - a;
+}
+
+static int
+efiemu_memequal (const void *a, const void *b, grub_size_t n)
+{
+ grub_uint8_t *ptr1, *ptr2;
+ for (ptr1 = (grub_uint8_t *) a, ptr2 = (grub_uint8_t *)b;
+ ptr1 < (grub_uint8_t *)a + n && *ptr2 == *ptr1; ptr1++, ptr2++);
+ return ptr1 == a + n;
+}
+
+static void
+efiemu_memset (grub_uint8_t *a, grub_uint8_t b, grub_size_t n)
+{
+ grub_uint8_t *ptr1;
+ for (ptr1=a; ptr1 < a + n; ptr1++)
+ *ptr1 = b;
+}
+
+static inline void
+write_cmos (grub_uint8_t addr, grub_uint8_t val)
+{
+ asm volatile ("outb %%al,$0x70\n"
+ "mov %%cl, %%al\n"
+ "outb %%al,$0x71": :"a" (addr), "c" (val));
+}
+
+static inline grub_uint8_t
+read_cmos (grub_uint8_t addr)
+{
+ grub_uint8_t ret;
+ asm volatile ("outb %%al, $0x70\n"
+ "inb $0x71, %%al": "=a"(ret) :"a" (addr));
+ return ret;
+}
+
+/* Needed by some gcc versions */
+int __stack_chk_fail ()
+{
+ return 0;
+}
+
+/* The function that implement runtime services as specified in
+ EFI specification */
+static inline grub_uint8_t
+bcd_to_hex (grub_uint8_t in)
+{
+ return 10 * ((in & 0xf0) >> 4) + (in & 0x0f);
+}
+
+grub_efi_status_t
+EFI_FUNC (efiemu_get_time) (grub_efi_time_t *time,
+ grub_efi_time_capabilities_t *capabilities)
+{
+ LOG ('a');
+ grub_uint8_t state;
+ state = read_cmos (0xb);
+ if (!(state & (1 << 2)))
+ {
+ time->year = 2000 + bcd_to_hex (read_cmos (0x9));
+ time->month = bcd_to_hex (read_cmos (0x8));
+ time->day = bcd_to_hex (read_cmos (0x7));
+ time->hour = bcd_to_hex (read_cmos (0x4));
+ if (time->hour >= 81)
+ time->hour -= 80 - 12;
+ if (time->hour == 24)
+ time->hour = 0;
+ time->minute = bcd_to_hex (read_cmos (0x2));
+ time->second = bcd_to_hex (read_cmos (0x0));
+ }
+ else
+ {
+ time->year = 2000 + read_cmos (0x9);
+ time->month = read_cmos (0x8);
+ time->day = read_cmos (0x7);
+ time->hour = read_cmos (0x4);
+ if (time->hour >= 0x81)
+ time->hour -= 0x80 - 12;
+ if (time->hour == 24)
+ time->hour = 0;
+ time->minute = read_cmos (0x2);
+ time->second = read_cmos (0x0);
+ }
+ time->nanosecond = 0;
+ time->pad1 = 0;
+ time->pad2 = 0;
+ time->time_zone = efiemu_time_zone;
+ time->daylight = efiemu_time_daylight;
+ capabilities->resolution = 1;
+ capabilities->accuracy = efiemu_time_accuracy;
+ capabilities->sets_to_zero = 0;
+ return GRUB_EFI_SUCCESS;
+}
+
+grub_efi_status_t
+EFI_FUNC (efiemu_set_time) (grub_efi_time_t *time)
+{
+ LOG ('b');
+ grub_uint8_t state;
+ state = read_cmos (0xb);
+ write_cmos (0xb, state | 0x6);
+ write_cmos (0x9, time->year - 2000);
+ write_cmos (0x8, time->month);
+ write_cmos (0x7, time->day);
+ write_cmos (0x4, time->hour);
+ write_cmos (0x2, time->minute);
+ write_cmos (0x0, time->second);
+ efiemu_time_zone = time->time_zone;
+ efiemu_time_daylight = time->daylight;
+ return GRUB_EFI_SUCCESS;
+}
+
+/* Following 2 functions are vendor specific. So announce it as unsupported */
+grub_efi_status_t
+EFI_FUNC (efiemu_get_wakeup_time) (grub_efi_boolean_t *enabled,
+ grub_efi_boolean_t *pending,
+ grub_efi_time_t *time)
+{
+ LOG ('c');
+ return GRUB_EFI_UNSUPPORTED;
+}
+
+grub_efi_status_t
+EFI_FUNC (efiemu_set_wakeup_time) (grub_efi_boolean_t enabled,
+ grub_efi_time_t *time)
+{
+ LOG ('d');
+ return GRUB_EFI_UNSUPPORTED;
+}
+
+static grub_uint32_t crc32_table [256];
+
+static grub_uint32_t
+reflect (grub_uint32_t ref, int len)
+{
+ grub_uint32_t result = 0;
+ int i;
+
+ for (i = 1; i <= len; i++)
+ {
+ if (ref & 1)
+ result |= 1 << (len - i);
+ ref >>= 1;
+ }
+
+ return result;
+}
+
+static void
+init_crc32_table (void)
+{
+ grub_uint32_t polynomial = 0x04c11db7;
+ int i, j;
+
+ for(i = 0; i < 256; i++)
+ {
+ crc32_table[i] = reflect(i, 8) << 24;
+ for (j = 0; j < 8; j++)
+ crc32_table[i] = (crc32_table[i] << 1) ^
+ (crc32_table[i] & (1 << 31) ? polynomial : 0);
+ crc32_table[i] = reflect(crc32_table[i], 32);
+ }
+}
+
+static grub_uint32_t
+efiemu_getcrc32 (grub_uint32_t crc, void *buf, int size)
+{
+ int i;
+ grub_uint8_t *data = buf;
+
+ if (! crc32_table[1])
+ init_crc32_table ();
+
+ crc^= 0xffffffff;
+
+ for (i = 0; i < size; i++)
+ {
+ crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ *data];
+ data++;
+ }
+
+ return crc ^ 0xffffffff;
+}
+
+
+grub_efi_status_t EFI_FUNC
+(efiemu_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)
+{
+ struct grub_efiemu_ptv_rel *cur_relloc;
+
+ LOG ('e');
+
+ /* Ensure that we are called only once */
+ if (efiemu_ptv_relocated)
+ return GRUB_EFI_UNSUPPORTED;
+ efiemu_ptv_relocated = 1;
+
+ /* Correct addresses using information supplied by grub */
+ for (cur_relloc = efiemu_ptv_relloc; cur_relloc->size;cur_relloc++)
+ {
+ grub_int64_t corr = 0;
+ grub_efi_memory_descriptor_t *descptr;
+
+ /* Compute correction */
+ for (descptr = virtual_map;
+ ((grub_uint8_t *) descptr - (grub_uint8_t *) virtual_map)
+ < memory_map_size;
+ descptr = (grub_efi_memory_descriptor_t *)
+ ((grub_uint8_t *) descptr + descriptor_size))
+ {
+ if (descptr->type == cur_relloc->plustype)
+ corr += descptr->virtual_start - descptr->physical_start;
+ if (descptr->type == cur_relloc->minustype)
+ corr -= descptr->virtual_start - descptr->physical_start;
+ }
+
+ /* Apply correction */
+ switch (cur_relloc->size)
+ {
+ case 8:
+ *((grub_uint64_t *) (grub_addr_t) cur_relloc->addr) += corr;
+ break;
+ case 4:
+ *((grub_uint32_t *) (grub_addr_t) cur_relloc->addr) += corr;
+ break;
+ case 2:
+ *((grub_uint16_t *) (grub_addr_t) cur_relloc->addr) += corr;
+ break;
+ case 1:
+ *((grub_uint8_t *) (grub_addr_t) cur_relloc->addr) += corr;
+ break;
+ }
+ }
+
+ /* Recompute crc32 of system table and runtime services */
+ efiemu_system_table.hdr.crc32 = 0;
+ efiemu_system_table.hdr.crc32 = efiemu_getcrc32
+ (0, &efiemu_system_table, sizeof (efiemu_system_table));
+
+ efiemu_runtime_services.hdr.crc32 = 0;
+ efiemu_runtime_services.hdr.crc32 = efiemu_getcrc32
+ (0, &efiemu_runtime_services, sizeof (efiemu_runtime_services));
+
+ return GRUB_EFI_SUCCESS;
+}
+
+/* since efiemu_set_virtual_address_map corrects all the pointers
+ we don't need efiemu_convert_pointer */
+grub_efi_status_t
+EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition,
+ void **address)
+{
+ LOG ('f');
+ return GRUB_EFI_UNSUPPORTED;
+}
+
+/* Next comes variable services. Because we have no vendor-independent
+ way to store these variables we have no non-volatility */
+
+/* Find variable by name and GUID. */
+static struct efi_variable *
+find_variable (const grub_efi_guid_t *vendor_guid,
+ grub_efi_char16_t *variable_name)
+{
+ grub_uint8_t *ptr;
+ struct efi_variable *efivar;
+
+ for (ptr = efiemu_variables; ptr < efiemu_variables + efiemu_varsize; )
+ {
+ efivar = (struct efi_variable *) ptr;
+ if (!efivar->namelen)
+ return 0;
+ if (efiemu_str16equal((grub_efi_char16_t *)(efivar + 1), variable_name)
+ && efiemu_memequal (&(efivar->guid), vendor_guid,
+ sizeof (efivar->guid)))
+ return efivar;
+ ptr += efivar->namelen + efivar->size + sizeof (*efivar);
+ }
+ return 0;
+}
+
+grub_efi_status_t
+EFI_FUNC (efiemu_get_variable) (grub_efi_char16_t *variable_name,
+ const grub_efi_guid_t *vendor_guid,
+ grub_efi_uint32_t *attributes,
+ grub_efi_uintn_t *data_size,
+ void *data)
+{
+ struct efi_variable *efivar;
+ LOG ('g');
+ efivar = find_variable (vendor_guid, variable_name);
+ if (!efivar)
+ return GRUB_EFI_NOT_FOUND;
+ if (*data_size < efivar->size)
+ {
+ *data_size = efivar->size;
+ return GRUB_EFI_BUFFER_TOO_SMALL;
+ }
+ *data_size = efivar->size;
+ efiemu_memcpy (data, (grub_uint8_t *)(efivar + 1) + efivar->namelen,
+ efivar->size);
+ *attributes = efivar->attributes;
+
+ return GRUB_EFI_SUCCESS;
+}
+
+grub_efi_status_t EFI_FUNC
+(efiemu_get_next_variable_name) (grub_efi_uintn_t *variable_name_size,
+ grub_efi_char16_t *variable_name,
+ grub_efi_guid_t *vendor_guid)
+{
+ struct efi_variable *efivar;
+ LOG ('l');
+
+ if (!variable_name_size || !variable_name || !vendor_guid)
+ return GRUB_EFI_INVALID_PARAMETER;
+ if (variable_name[0])
+ {
+ efivar = find_variable (vendor_guid, variable_name);
+ if (!efivar)
+ return GRUB_EFI_NOT_FOUND;
+ efivar = (struct efi_variable *)((grub_uint8_t *)efivar
+ + efivar->namelen
+ + efivar->size + sizeof (*efivar));
+ }
+ else
+ efivar = (struct efi_variable *) (efiemu_variables);
+
+ LOG ('m');
+ if ((grub_uint8_t *)efivar >= efiemu_variables + efiemu_varsize
+ || !efivar->namelen)
+ return GRUB_EFI_NOT_FOUND;
+ if (*variable_name_size < efivar->namelen)
+ {
+ *variable_name_size = efivar->namelen;
+ return GRUB_EFI_BUFFER_TOO_SMALL;
+ }
+
+ efiemu_memcpy (variable_name, efivar + 1, efivar->namelen);
+ efiemu_memcpy (vendor_guid, &(efivar->guid),
+ sizeof (efivar->guid));
+
+ LOG('h');
+ return GRUB_EFI_SUCCESS;
+}
+
+grub_efi_status_t
+EFI_FUNC (efiemu_set_variable) (grub_efi_char16_t *variable_name,
+ const grub_efi_guid_t *vendor_guid,
+ grub_efi_uint32_t attributes,
+ grub_efi_uintn_t data_size,
+ void *data)
+{
+ struct efi_variable *efivar;
+ grub_uint8_t *ptr;
+ LOG('i');
+ if (!variable_name[0])
+ return GRUB_EFI_INVALID_PARAMETER;
+ efivar = find_variable (vendor_guid, variable_name);
+
+ /* Delete variable if any */
+ if (efivar)
+ {
+ efiemu_memcpy (efivar, (grub_uint8_t *)(efivar + 1)
+ + efivar->namelen + efivar->size,
+ (efiemu_variables + efiemu_varsize)
+ - ((grub_uint8_t *)(efivar + 1)
+ + efivar->namelen + efivar->size));
+ efiemu_memset (efiemu_variables + efiemu_varsize
+ - (sizeof (*efivar) + efivar->namelen + efivar->size),
+ 0, (sizeof (*efivar) + efivar->namelen + efivar->size));
+ }
+
+ if (!data_size)
+ return GRUB_EFI_SUCCESS;
+
+ for (ptr = efiemu_variables; ptr < efiemu_variables + efiemu_varsize; )
+ {
+ efivar = (struct efi_variable *) ptr;
+ ptr += efivar->namelen + efivar->size + sizeof (*efivar);
+ if (!efivar->namelen)
+ break;
+ }
+ if ((grub_uint8_t *)(efivar + 1) + data_size
+ + 2 * (efiemu_str16len (variable_name) + 1)
+ >= efiemu_variables + efiemu_varsize)
+ return GRUB_EFI_OUT_OF_RESOURCES;
+
+ efiemu_memcpy (&(efivar->guid), vendor_guid, sizeof (efivar->guid));
+ efivar->namelen = 2 * (efiemu_str16len (variable_name) + 1);
+ efivar->size = data_size;
+ efivar->attributes = attributes;
+ efiemu_memcpy (efivar + 1, variable_name,
+ 2 * (efiemu_str16len (variable_name) + 1));
+ efiemu_memcpy ((grub_uint8_t *)(efivar + 1)
+ + 2 * (efiemu_str16len (variable_name) + 1),
+ data, data_size);
+
+ return GRUB_EFI_SUCCESS;
+}
+
+grub_efi_status_t EFI_FUNC
+(efiemu_get_next_high_monotonic_count) (grub_efi_uint32_t *high_count)
+{
+ LOG ('j');
+ if (!high_count)
+ return GRUB_EFI_INVALID_PARAMETER;
+ *high_count = ++efiemu_high_monotonic_count;
+ return GRUB_EFI_SUCCESS;
+}
+
+/* To implement it with APM we need to go to real mode. It's too much hassle
+ Besides EFI specification says that this function shouldn't be used
+ on systems supporting ACPI
+ */
+void
+EFI_FUNC (efiemu_reset_system) (grub_efi_reset_type_t reset_type,
+ grub_efi_status_t reset_status,
+ grub_efi_uintn_t data_size,
+ grub_efi_char16_t *reset_data)
+{
+ LOG ('k');
+}
+
+struct grub_efi_runtime_services efiemu_runtime_services =
+{
+ .hdr =
+ {
+ .signature = GRUB_EFIEMU_RUNTIME_SERVICES_SIGNATURE,
+ .revision = 0x0001000a,
+ .header_size = sizeof (struct grub_efi_runtime_services),
+ .crc32 = 0, /* filled later*/
+ .reserved = 0
+ },
+ .get_time = efiemu_get_time,
+ .set_time = efiemu_set_time,
+ .get_wakeup_time = efiemu_get_wakeup_time,
+ .set_wakeup_time = efiemu_set_wakeup_time,
+
+ .set_virtual_address_map = efiemu_set_virtual_address_map,
+ .convert_pointer = efiemu_convert_pointer,
+
+ .get_variable = efiemu_get_variable,
+ .get_next_variable_name = efiemu_get_next_variable_name,
+ .set_variable = efiemu_set_variable,
+ .get_next_high_monotonic_count = efiemu_get_next_high_monotonic_count,
+
+ .reset_system = efiemu_reset_system
+};
+
+
+static grub_uint16_t efiemu_vendor[] =
+ {'G', 'R', 'U', 'B', ' ', 'E', 'F', 'I', ' ',
+ 'R', 'U', 'N', 'T', 'I', 'M', 'E', 0};
+
+struct grub_efi_system_table efiemu_system_table =
+{
+ .hdr =
+ {
+ .signature = GRUB_EFIEMU_SYSTEM_TABLE_SIGNATURE,
+ .revision = 0x0001000a,
+ .header_size = sizeof (struct grub_efi_system_table),
+ .crc32 = 0, /* filled later*/
+ .reserved = 0
+ },
+ .firmware_vendor = efiemu_vendor,
+ .firmware_revision = 0x0001000a,
+ .console_in_handler = 0,
+ .con_in = 0,
+ .console_out_handler = 0,
+ .con_out = 0,
+ .standard_error_handle = 0,
+ .std_err = 0,
+ .runtime_services = &efiemu_runtime_services,
+ .boot_services = 0,
+ .num_table_entries = 0,
+ .configuration_table = 0
+};
+
diff --git a/grub-core/efiemu/symbols.c b/grub-core/efiemu/symbols.c
new file mode 100644
index 0000000..cc14855
--- /dev/null
+++ b/grub-core/efiemu/symbols.c
@@ -0,0 +1,272 @@
+/* Code for managing symbols and pointers in efiemu */
+/*
+ * 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/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/efiemu/efiemu.h>
+#include <grub/efiemu/runtime.h>
+#include <grub/i18n.h>
+
+static int ptv_written = 0;
+static int ptv_alloc = 0;
+static int ptv_handle = 0;
+static int relocated_handle = 0;
+static int ptv_requested = 0;
+static struct grub_efiemu_sym *efiemu_syms = 0;
+
+struct grub_efiemu_sym
+{
+ struct grub_efiemu_sym *next;
+ char *name;
+ int handle;
+ grub_off_t off;
+};
+
+void
+grub_efiemu_free_syms (void)
+{
+ struct grub_efiemu_sym *cur, *d;
+ for (cur = efiemu_syms; cur;)
+ {
+ d = cur->next;
+ grub_free (cur->name);
+ grub_free (cur);
+ cur = d;
+ }
+ efiemu_syms = 0;
+ ptv_written = 0;
+ ptv_alloc = 0;
+ ptv_requested = 0;
+ grub_efiemu_mm_return_request (ptv_handle);
+ ptv_handle = 0;
+ grub_efiemu_mm_return_request (relocated_handle);
+ relocated_handle = 0;
+}
+
+/* Announce that the module will need NUM allocators */
+/* Because of deferred memory allocation all the relocators have to be
+ announced during phase 1*/
+grub_err_t
+grub_efiemu_request_symbols (int num)
+{
+ if (ptv_alloc)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ "symbols have already been allocated");
+ if (num < 0)
+ return grub_error (GRUB_ERR_BUG,
+ "can't request negative symbols");
+ ptv_requested += num;
+ return GRUB_ERR_NONE;
+}
+
+/* Resolve the symbol name NAME and set HANDLE and OFF accordingly */
+grub_err_t
+grub_efiemu_resolve_symbol (const char *name, int *handle, grub_off_t *off)
+{
+ struct grub_efiemu_sym *cur;
+ for (cur = efiemu_syms; cur; cur = cur->next)
+ if (!grub_strcmp (name, cur->name))
+ {
+ *handle = cur->handle;
+ *off = cur->off;
+ return GRUB_ERR_NONE;
+ }
+ grub_dprintf ("efiemu", "%s not found\n", name);
+ return grub_error (GRUB_ERR_BAD_OS, N_("symbol `%s' not found"), name);
+}
+
+/* Register symbol named NAME in memory handle HANDLE at offset OFF */
+grub_err_t
+grub_efiemu_register_symbol (const char *name, int handle, grub_off_t off)
+{
+ struct grub_efiemu_sym *cur;
+ cur = (struct grub_efiemu_sym *) grub_malloc (sizeof (*cur));
+ grub_dprintf ("efiemu", "registering symbol '%s'\n", name);
+ if (!cur)
+ return grub_errno;
+ cur->name = grub_strdup (name);
+ cur->next = efiemu_syms;
+ cur->handle = handle;
+ cur->off = off;
+ efiemu_syms = cur;
+
+ return 0;
+}
+
+/* Go from phase 1 to phase 2. Must be called before similar function in mm.c */
+grub_err_t
+grub_efiemu_alloc_syms (void)
+{
+ ptv_alloc = ptv_requested;
+ ptv_handle = grub_efiemu_request_memalign
+ (1, (ptv_requested + 1) * sizeof (struct grub_efiemu_ptv_rel),
+ GRUB_EFI_RUNTIME_SERVICES_DATA);
+ relocated_handle = grub_efiemu_request_memalign
+ (1, sizeof (grub_uint8_t), GRUB_EFI_RUNTIME_SERVICES_DATA);
+
+ grub_efiemu_register_symbol ("efiemu_ptv_relocated", relocated_handle, 0);
+ grub_efiemu_register_symbol ("efiemu_ptv_relloc", ptv_handle, 0);
+ return grub_errno;
+}
+
+grub_err_t
+grub_efiemu_write_sym_markers (void)
+{
+ struct grub_efiemu_ptv_rel *ptv_rels
+ = grub_efiemu_mm_obtain_request (ptv_handle);
+ grub_uint8_t *relocated = grub_efiemu_mm_obtain_request (relocated_handle);
+ grub_memset (ptv_rels, 0, (ptv_requested + 1)
+ * sizeof (struct grub_efiemu_ptv_rel));
+ *relocated = 0;
+ return GRUB_ERR_NONE;
+}
+
+/* Write value (pointer to memory PLUS_HANDLE)
+ - (pointer to memory MINUS_HANDLE) + VALUE to ADDR assuming that the
+ size SIZE bytes. If PTV_NEEDED is 1 then announce it to runtime that this
+ value needs to be recomputed before going to virtual mode
+*/
+grub_err_t
+grub_efiemu_write_value (void *addr, grub_uint32_t value, int plus_handle,
+ int minus_handle, int ptv_needed, int size)
+{
+ /* Announce relocator to runtime */
+ if (ptv_needed)
+ {
+ struct grub_efiemu_ptv_rel *ptv_rels
+ = grub_efiemu_mm_obtain_request (ptv_handle);
+
+ if (ptv_needed && ptv_written >= ptv_alloc)
+ return grub_error (GRUB_ERR_BUG,
+ "your module didn't declare efiemu "
+ " relocators correctly");
+
+ if (minus_handle)
+ ptv_rels[ptv_written].minustype
+ = grub_efiemu_mm_get_type (minus_handle);
+ else
+ ptv_rels[ptv_written].minustype = 0;
+
+ if (plus_handle)
+ ptv_rels[ptv_written].plustype
+ = grub_efiemu_mm_get_type (plus_handle);
+ else
+ ptv_rels[ptv_written].plustype = 0;
+
+ ptv_rels[ptv_written].addr = (grub_addr_t) addr;
+ ptv_rels[ptv_written].size = size;
+ ptv_written++;
+
+ /* memset next value to zero to mark the end */
+ grub_memset (&ptv_rels[ptv_written], 0, sizeof (ptv_rels[ptv_written]));
+ }
+
+ /* Compute the value */
+ if (minus_handle)
+ value -= (grub_addr_t) grub_efiemu_mm_obtain_request (minus_handle);
+
+ if (plus_handle)
+ value += (grub_addr_t) grub_efiemu_mm_obtain_request (plus_handle);
+
+ /* Write the value */
+ switch (size)
+ {
+ case 8:
+ *((grub_uint64_t *) addr) = value;
+ break;
+ case 4:
+ *((grub_uint32_t *) addr) = value;
+ break;
+ case 2:
+ *((grub_uint16_t *) addr) = value;
+ break;
+ case 1:
+ *((grub_uint8_t *) addr) = value;
+ break;
+ default:
+ return grub_error (GRUB_ERR_BUG, "wrong symbol size");
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_efiemu_set_virtual_address_map (grub_efi_uintn_t memory_map_size,
+ grub_efi_uintn_t descriptor_size,
+ grub_efi_uint32_t descriptor_version
+ __attribute__ ((unused)),
+ grub_efi_memory_descriptor_t *virtual_map)
+{
+ grub_uint8_t *ptv_relocated;
+ struct grub_efiemu_ptv_rel *cur_relloc;
+ struct grub_efiemu_ptv_rel *ptv_rels;
+
+ ptv_relocated = grub_efiemu_mm_obtain_request (relocated_handle);
+ ptv_rels = grub_efiemu_mm_obtain_request (ptv_handle);
+
+ /* Ensure that we are called only once */
+ if (*ptv_relocated)
+ return grub_error (GRUB_ERR_BUG, "EfiEmu is already relocated");
+ *ptv_relocated = 1;
+
+ /* Correct addresses using information supplied by grub */
+ for (cur_relloc = ptv_rels; cur_relloc->size; cur_relloc++)
+ {
+ grub_int64_t corr = 0;
+ grub_efi_memory_descriptor_t *descptr;
+
+ /* Compute correction */
+ for (descptr = virtual_map;
+ (grub_size_t) ((grub_uint8_t *) descptr
+ - (grub_uint8_t *) virtual_map) < memory_map_size;
+ descptr = (grub_efi_memory_descriptor_t *)
+ ((grub_uint8_t *) descptr + descriptor_size))
+ {
+ if (descptr->type == cur_relloc->plustype)
+ corr += descptr->virtual_start - descptr->physical_start;
+ if (descptr->type == cur_relloc->minustype)
+ corr -= descptr->virtual_start - descptr->physical_start;
+ }
+
+ /* Apply correction */
+ switch (cur_relloc->size)
+ {
+ case 8:
+ *((grub_uint64_t *) (grub_addr_t) cur_relloc->addr) += corr;
+ break;
+ case 4:
+ *((grub_uint32_t *) (grub_addr_t) cur_relloc->addr) += corr;
+ break;
+ case 2:
+ *((grub_uint16_t *) (grub_addr_t) cur_relloc->addr) += corr;
+ break;
+ case 1:
+ *((grub_uint8_t *) (grub_addr_t) cur_relloc->addr) += corr;
+ break;
+ }
+ }
+
+ /* Recompute crc32 of system table and runtime services */
+
+ if (grub_efiemu_sizeof_uintn_t () == 4)
+ return grub_efiemu_crc32 ();
+ else
+ return grub_efiemu_crc64 ();
+}