diff --git a/ChangeLog b/ChangeLog index a68b970..cc77f4a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,44 @@ +2009-04-26 Vladimir Serbinenko + + Efiemu + + * conf/i386-pc.rmk: new modules efiemu, efiemu_acpi, efiemu_pnvram, + _linux_efi, linux_efi. + new files in grub-emu + new targets efiemu32.o and efiemu64.o + * loader/linux_normal_efiemu.c: likewise + * loader/i386/efi/linux.c: added preliminary efiemu support + * util/i386/pc/grub-install.in: add efiemu??.o to the list of + files to copy + * include/grub/autoefi.h: new file + * nclude/grub/i386/efiemu.h: likewise + * include/grub/i386/pc/efiemu.h: likewise + * include/grub/efi/api.h: add LL suffix when necessary + new definitions relating to tables + * include/grub/efiemu/efiemu.h: new file + * include/grub/efiemu/runtime.h: likewise + * efiemu/prepare.c: likewise + * efiemu/loadcore_common.c: likewise + * efiemu/loadcore64.c: likewise + * efiemu/runtime/efiemu.sh: likewise + * efiemu/runtime/efiemu.S: likewise + * efiemu/runtime/efiemu.c: likewise + * efiemu/runtime/config.h: likewise + * efiemu/prepare32.c: likewise + * efiemu/main.c: likewise + * efiemu/modules/pnvram.c: likewise + * efiemu/modules/i386: likewise + * efiemu/modules/i386/pc: likewise + * efiemu/modules/acpi.c: likewise + * efiemu/i386/pc/cfgtables.c: likewise + * efiemu/i386/loadcore64.c: likewise + * efiemu/i386/loadcore32.c: likewise + * efiemu/prepare64.c: likewise + * efiemu/loadcore.c: likewise + * efiemu/symbols.c: likewise + * efiemu/mm.c: likewise + * efiemu/loadcore32.c: likewise + 2009-04-26 Vladimir Serbinenko ACPI spoofing diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 8f4dbe9..2893fb9 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -183,7 +183,17 @@ pkglib_MODULES = biosdisk.mod chain.mod normal.mod \ aout.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \ datehook.mod lsmmap.mod ata_pthru.mod hdparm.mod \ usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod usb_keyboard.mod \ - mmap.mod acpi.mod + efiemu.mod mmap.mod acpi.mod + +# For efiemu.mod. +efiemu_mod_SOURCES = efiemu/main.c efiemu/i386/loadcore32.c \ + efiemu/i386/loadcore64.c efiemu/i386/pc/cfgtables.c \ + efiemu/mm.c efiemu/loadcore_common.c efiemu/symbols.c \ + efiemu/loadcore32.c efiemu/loadcore64.c \ + efiemu/prepare32.c efiemu/prepare64.c efiemu/pnvram.c \ + efiemu/i386/coredetect.c +efiemu_mod_CFLAGS = $(COMMON_CFLAGS) -Werror -Wall +efiemu_mod_LDFLAGS = $(COMMON_LDFLAGS) # For acpi.mod. acpi_mod_SOURCES = commands/acpi.c commands/i386/pc/acpi.c @@ -389,5 +399,19 @@ hdparm_mod_SOURCES = commands/hdparm.c lib/hexdump.c hdparm_mod_CFLAGS = $(COMMON_CFLAGS) hdparm_mod_LDFLAGS = $(COMMON_LDFLAGS) +efiemu32.o: efiemu/runtime/efiemu.c + $(CC) -c -m32 -DELF32 -o $@ -Wall -Werror efiemu/runtime/efiemu.c -nostdlib -O2 -Iefiemu/runtime -Iinclude +efiemu64_c.o: efiemu/runtime/efiemu.c + $(CC) -c -m64 -DELF64 -o $@ -Wall -Werror efiemu/runtime/efiemu.c -nostdlib -mcmodel=large -O2 -Iefiemu/runtime -Iinclude + +efiemu64_s.o: efiemu/runtime/efiemu.S + $(CC) -c -m64 -DELF64 -o $@ -Wall -Werror efiemu/runtime/efiemu.S -nostdlib -mcmodel=large -O2 -Iefiemu/runtime -Iinclude + +efiemu64.o: efiemu64_c.o efiemu64_s.o + ld -melf_x86_64 -o $@ -r efiemu64_c.o efiemu64_s.o -nostdlib + +CLEANFILES += efiemu32.o efiemu64.o efiemu64_c.o efiemu64_s.o +lib_DATA += efiemu32.o efiemu64.o + include $(srcdir)/conf/i386.mk include $(srcdir)/conf/common.mk diff --git a/efiemu/i386/coredetect.c b/efiemu/i386/coredetect.c new file mode 100644 index 0000000..828508d --- /dev/null +++ b/efiemu/i386/coredetect.c @@ -0,0 +1,60 @@ +/* + * 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 . + */ + +#include +#include +#include + +#define cpuid(num,a,b,c,d) \ + asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (num)) + +#define bit_LM (1 << 29) + +char * +grub_efiemu_get_default_core_name (void) +{ + + unsigned int eax, ebx, ecx, edx; + unsigned int max_level; + unsigned int ext_level; + + /* See if we can use cpuid. */ + asm volatile ("pushfl; pushfl; popl %0; movl %0,%1; xorl %2,%0;" + "pushl %0; popfl; pushfl; popl %0; popfl" + : "=&r" (eax), "=&r" (ebx) + : "i" (0x00200000)); + if (((eax ^ ebx) & 0x00200000) == 0) + return "efiemu32.o"; + + /* Check the highest input value for eax. */ + cpuid (0, eax, ebx, ecx, edx); + /* We only look at the first four characters. */ + max_level = eax; + if (max_level == 0) + return "efiemu32.o"; + + cpuid (0x80000000, eax, ebx, ecx, edx); + ext_level = eax; + if (ext_level < 0x80000000) + return "efiemu32.o"; + + cpuid (0x80000001, eax, ebx, ecx, edx); + return (edx & bit_LM) ? "efiemu64.o" : "efiemu32.o"; +} diff --git a/efiemu/i386/loadcore32.c b/efiemu/i386/loadcore32.c new file mode 100644 index 0000000..d46cd4c --- /dev/null +++ b/efiemu/i386/loadcore32.c @@ -0,0 +1,114 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include + +/* 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: + if ((err = grub_efiemu_write_value + (addr, sym.off + *addr, sym.handle, 0, + seg->ptv_rel_needed, sizeof (grub_uint32_t)))) + return err; + + break; + + case R_386_PC32: + if ((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)))) + return err; + break; + default: + return grub_error (GRUB_ERR_BAD_OS, + "unrecognised relocation"); + } + } + } + } + + return GRUB_ERR_NONE; +} + + diff --git a/efiemu/i386/loadcore64.c b/efiemu/i386/loadcore64.c new file mode 100644 index 0000000..6c0e0ca --- /dev/null +++ b/efiemu/i386/loadcore64.c @@ -0,0 +1,120 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include + +/* 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: + if ((err = grub_efiemu_write_value + (addr, *addr64 + rel->r_addend + sym.off, sym.handle, + 0, seg->ptv_rel_needed, sizeof (grub_uint64_t)))) + return err; + break; + + case R_X86_64_PC32: + if ((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)))) + return err; + break; + + case R_X86_64_32: + case R_X86_64_32S: + if ((err = grub_efiemu_write_value + (addr, *addr32 + rel->r_addend + sym.off, sym.handle, + 0, seg->ptv_rel_needed, sizeof (grub_uint32_t)))) + return err; + break; + default: + return grub_error (GRUB_ERR_BAD_OS, + "unrecognised relocation"); + } + } + } + } + + return GRUB_ERR_NONE; +} diff --git a/efiemu/i386/pc/cfgtables.c b/efiemu/i386/pc/cfgtables.c new file mode 100644 index 0000000..ef7e65e --- /dev/null +++ b/efiemu/i386/pc/cfgtables.c @@ -0,0 +1,65 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include + +grub_err_t +grub_machine_efiemu_init_tables () +{ + grub_uint8_t *ptr; + 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; + + if ((err = grub_efiemu_unregister_configuration_table (smbios))) + return err; + if ((err = grub_efiemu_unregister_configuration_table (acpi))) + return err; + if ((err = grub_efiemu_unregister_configuration_table (acpi20))) + return err; + + if ((table = grub_acpi_get_rsdpv1 ()) + && (err = grub_efiemu_register_configuration_table (acpi, 0, 0, table))) + return err; + if ((table = grub_acpi_get_rsdpv2 ()) + && (err = grub_efiemu_register_configuration_table (acpi20, 0, 0, table))) + return err; + + for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; + ptr += 16) + if (grub_memcmp (ptr, "_SM_", 4) == 0 + && grub_byte_checksum (ptr, *(ptr + 5)) == 0) + break; + + if (ptr < (grub_uint8_t *) 0x100000) + { + grub_dprintf ("efiemu", "Registering SMBIOS\n"); + if ((err = grub_efiemu_register_configuration_table (smbios, 0, 0, ptr))) + return err; + } + + return GRUB_ERR_NONE; +} diff --git a/efiemu/loadcore.c b/efiemu/loadcore.c new file mode 100644 index 0000000..36f27a2 --- /dev/null +++ b/efiemu/loadcore.c @@ -0,0 +1,365 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +/* 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) + 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, "no symbol table"); + + grub_efiemu_nelfsyms = (unsigned) s->sh_size / (unsigned) s->sh_entsize; + grub_efiemu_elfsyms = (struct grub_efiemu_elf_sym *) + grub_malloc (sizeof (struct grub_efiemu_elf_sym) * grub_efiemu_nelfsyms); + + /* 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, "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) + { + if ((err = grub_efiemu_resolve_symbol (name, &handle, &off))) + return err; + grub_efiemu_elfsyms[i].handle = handle; + grub_efiemu_elfsyms[i].off = off; + } + else + sym->st_value = 0; + break; + + case STT_OBJECT: + if ((err = grub_efiemu_get_section_addr + (segs, sym->st_shndx, &handle, &off))) + return err; + + off += sym->st_value; + if (bind != STB_LOCAL) + if ((err = grub_efiemu_register_symbol (name, handle, off))) + return err; + grub_efiemu_elfsyms[i].handle = handle; + grub_efiemu_elfsyms[i].off = off; + break; + + case STT_FUNC: + if ((err = grub_efiemu_get_section_addr + (segs, sym->st_shndx, &handle, &off))) + return err; + + off += sym->st_value; + if (bind != STB_LOCAL) + if ((err = grub_efiemu_register_symbol (name, handle, off))) + return err; + grub_efiemu_elfsyms[i].handle = handle; + grub_efiemu_elfsyms[i].off = off; + break; + + case STT_SECTION: + if ((err = grub_efiemu_get_section_addr + (segs, sym->st_shndx, &handle, &off))) + { + 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, 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, "invalid ELF file type"); + + /* Make sure that every section is within the core. */ + if ((grub_size_t) core_size < e->e_shoff + e->e_shentsize * e->e_shnum) + return grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core"); + + if ((err = grub_efiemu_init_segments (segments, core))) + return err; + if ((err = grub_efiemu_count_symbols (core))) + 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; + if ((err = grub_efiemu_load_segments (segments, core))) + return err; + if ((err = grub_efiemu_resolve_symbols (segments, core))) + return err; + if ((err = SUFFIX (grub_arch_efiemu_relocate_symbols) (segments, + grub_efiemu_elfsyms, + core))) + return err; + + return GRUB_ERR_NONE; +} diff --git a/efiemu/loadcore32.c b/efiemu/loadcore32.c new file mode 100644 index 0000000..b4f61c7 --- /dev/null +++ b/efiemu/loadcore32.c @@ -0,0 +1,27 @@ +/* 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 . + */ + +#define SUFFIX(x) x ## 32 +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Word Elf32_Word +#define ELF_ST_TYPE ELF32_ST_TYPE +#define ELF_ST_BIND ELF32_ST_BIND +#include "loadcore.c" diff --git a/efiemu/loadcore64.c b/efiemu/loadcore64.c new file mode 100644 index 0000000..097276c --- /dev/null +++ b/efiemu/loadcore64.c @@ -0,0 +1,27 @@ +/* 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 . + */ + +#define SUFFIX(x) x ## 64 +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#define Elf_Word Elf64_Word +#define ELF_ST_TYPE ELF64_ST_TYPE +#define ELF_ST_BIND ELF64_ST_BIND +#include "loadcore.c" diff --git a/efiemu/loadcore_common.c b/efiemu/loadcore_common.c new file mode 100644 index 0000000..9de1b39 --- /dev/null +++ b/efiemu/loadcore_common.c @@ -0,0 +1,189 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include + +/* 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) +{ + 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: + if ((err = grub_efiemu_loadcore_init32 (efiemu_core, efiemu_core_size, + &efiemu_segments))) + { + grub_free (efiemu_core); + efiemu_core = 0; + grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED; + return err; + } + break; + + case GRUB_EFIEMU64: + if ((err = grub_efiemu_loadcore_init64 (efiemu_core, efiemu_core_size, + &efiemu_segments))) + { + grub_free (efiemu_core); + efiemu_core = 0; + grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED; + return err; + } + break; + + default: + return grub_error (GRUB_ERR_BAD_OS, "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: + if ((err = grub_efiemu_loadcore_load32 (efiemu_core, efiemu_core_size, + efiemu_segments))) + grub_efiemu_loadcore_unload (); + return err; + case GRUB_EFIEMU64: + if ((err = grub_efiemu_loadcore_load64 (efiemu_core, efiemu_core_size, + efiemu_segments))) + grub_efiemu_loadcore_unload (); + return err; + default: + return grub_error (GRUB_ERR_BAD_OS, "unknown EFI runtime"); + } +} diff --git a/efiemu/main.c b/efiemu/main.c new file mode 100644 index 0000000..0d7457e --- /dev/null +++ b/efiemu/main.c @@ -0,0 +1,335 @@ +/* + * 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 . + */ + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; + +/* 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; + + 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; + if (! hook) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "you must supply the hook"); + nhook = (struct grub_efiemu_prepare_hook *) grub_malloc (sizeof (*nhook)); + if (! nhook) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't prepare hook"); + 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; + + if (! get_table && ! data) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "you must set at least get_table or data"); + if ((err = grub_efiemu_unregister_configuration_table (guid))) + return err; + + tbl = (struct grub_efiemu_configuration_table *) grub_malloc (sizeof (*tbl)); + if (! tbl) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't register table"); + + 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 (); +} + + + + +int +grub_efiemu_exit_boot_services (grub_efi_uintn_t map_key + __attribute__ ((unused))) +{ + /* Nothing to do here yet */ + return 1; +} + +int +grub_efiemu_finish_boot_services (void) +{ + /* Nothing to do here yet */ + return 1; +} + +/* 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); + if (! file) + return 0; + + err = grub_efiemu_mm_init (); + if (err) + { + grub_file_close (file); + grub_efiemu_unload (); + return grub_error (grub_errno, "Couldn't init memory management"); + } + + grub_dprintf ("efiemu", "mm inited\n"); + + err = grub_efiemu_loadcore_init (file); + 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_prepare (void) +{ + grub_err_t err; + const char *prefix; + char *filename; + char *suffix; + + grub_dprintf ("efiemu", "Preparing %d-bit efiemu\n", + 8 * grub_efiemu_sizeof_uintn_t ()); + + /* Create NVRAM if not yet done. */ + grub_efiemu_pnvram (); + + if (grub_efiemu_sizeof_uintn_t () == 0) + { + prefix = grub_env_get ("prefix"); + + if (! prefix) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + "couldn't find efiemu core because prefix " + "isn't set"); + + suffix = grub_efiemu_get_default_core_name (); + + filename = grub_malloc (grub_strlen (prefix) + grub_strlen (suffix) + 2); + if (! filename) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate temporary space"); + + grub_sprintf (filename, "%s/%s", prefix, suffix); + + err = grub_efiemu_load_file (filename); + grub_free (filename); + if (err) + return err; +#ifndef GRUB_UTIL + err = grub_machine_efiemu_init_tables (); + if (err) + return err; +#endif + } + + 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, "filename required"); + + err = grub_efiemu_load_file (args[0]); + if (err) + return err; +#ifndef GRUB_UTIL + 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; + +void +grub_efiemu_pnvram_cmd_register (void); + +GRUB_MOD_INIT(efiemu) +{ + (void) mod; /* To stop warning. */ + cmd_loadcore = grub_register_command ("efiemu_loadcore", + grub_cmd_efiemu_load, + "efiemu_loadcore FILE", + "Load and initialize EFI emulator"); + cmd_prepare = grub_register_command ("efiemu_prepare", + grub_cmd_efiemu_prepare, + "efiemu_prepare", + "Finalize loading of EFI emulator"); + cmd_unload = grub_register_command ("efiemu_unload", grub_cmd_efiemu_unload, + "efiemu_unload", + "Unload EFI emulator"); + grub_efiemu_pnvram_cmd_register (); +} + +GRUB_MOD_FINI(efiemu) +{ + grub_unregister_command (cmd_loadcore); + grub_unregister_command (cmd_prepare); + grub_unregister_command (cmd_unload); + grub_efiemu_pnvram_cmd_unregister (); +} diff --git a/efiemu/mm.c b/efiemu/mm.c new file mode 100644 index 0000000..e0364e6 --- /dev/null +++ b/efiemu/mm.c @@ -0,0 +1,635 @@ +/* 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 . + */ +/* + To keep efiemu runtime contiguous this mm is special. + It uses deffered 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 +#include +#include +#include +#include +#include + +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) + { + efiemu_mmap = (grub_efi_memory_descriptor_t *) + grub_realloc (efiemu_mmap, (++mmap_reserved_size) + * sizeof (grub_efi_memory_descriptor_t)); + if (!efiemu_mmap) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Not enough space for memory map"); + } + + /* 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_MAX_MEMORY_TYPE || type <= GRUB_EFI_LOADER_CODE) + 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_malloc (sizeof (*ret)); + if (!ret) + return -1; + ret->type = type; + ret->size = size; + ret->align_overhead = align_overhead; + ret->val = 0; + ret->next = 0; + 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 + }; + + /* 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_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate resident memory"); + + /* 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 (PTR_TO_UINT64 (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; + } +} + +/* Reserve space for memory map */ +static grub_err_t +grub_efiemu_mmap_init (void) +{ + auto int NESTED_FUNC_ATTR bounds_hook (grub_uint64_t, grub_uint64_t, + grub_uint32_t); + int NESTED_FUNC_ATTR bounds_hook (grub_uint64_t addr __attribute__ ((unused)), + grub_uint64_t size __attribute__ ((unused)), + grub_uint32_t type __attribute__ ((unused))) + { + mmap_reserved_size++; + return 0; + } + + // the place for memory used by efiemu itself + mmap_reserved_size = GRUB_EFI_MAX_MEMORY_TYPE + 1; + +#ifndef GRUB_UTIL + grub_machine_mmap_iterate (bounds_hook); +#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; +} + +/* 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; +} + +/* Copy host memory map */ +static grub_err_t +grub_efiemu_mmap_fill (void) +{ + auto int NESTED_FUNC_ATTR fill_hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); + int NESTED_FUNC_ATTR fill_hook (grub_uint64_t addr, + grub_uint64_t size, + grub_uint32_t type) + { + switch (type) + { + case GRUB_MACHINE_MEMORY_AVAILABLE: + return grub_efiemu_add_to_mmap (addr, size, + GRUB_EFI_CONVENTIONAL_MEMORY); + +#ifdef GRUB_MACHINE_MEMORY_ACPI + case GRUB_MACHINE_MEMORY_ACPI: + return grub_efiemu_add_to_mmap (addr, size, + GRUB_EFI_ACPI_RECLAIM_MEMORY); +#endif + +#ifdef GRUB_MACHINE_MEMORY_NVS + case GRUB_MACHINE_MEMORY_NVS: + return grub_efiemu_add_to_mmap (addr, size, + GRUB_EFI_ACPI_MEMORY_NVS); +#endif + + default: + grub_printf ("Unknown memory type %d. Marking as unusable\n", type); + case GRUB_MACHINE_MEMORY_RESERVED: + return grub_efiemu_add_to_mmap (addr, size, + GRUB_EFI_UNUSABLE_MEMORY); + } + } + +#ifndef GRUB_UTIL + grub_machine_mmap_iterate (fill_hook); +#endif + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_efiemu_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, + grub_uint64_t, + grub_uint32_t)) +{ + 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_EFIEMU_MEMORY_CODE); + break; + + case GRUB_EFI_RESERVED_MEMORY_TYPE: + case GRUB_EFI_RUNTIME_SERVICES_DATA: + case GRUB_EFI_UNUSABLE_MEMORY: + case GRUB_EFI_MEMORY_MAPPED_IO: + case GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE: + case GRUB_EFI_PAL_CODE: + case GRUB_EFI_MAX_MEMORY_TYPE: + hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, + GRUB_EFIEMU_MEMORY_RESERVED); + 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_EFIEMU_MEMORY_AVAILABLE); + break; + + case GRUB_EFI_ACPI_RECLAIM_MEMORY: + hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, + GRUB_EFIEMU_MEMORY_ACPI); + break; + + case GRUB_EFI_ACPI_MEMORY_NVS: + hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, + GRUB_EFIEMU_MEMORY_NVS); + 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 + }; + + 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_malloc (sizeof (struct grub_efiemu_mmap_scan) * 2 * mmap_num); + + /* Number of chunks can't increase more than by factor of 2 */ + result = (grub_efi_memory_descriptor_t *) + grub_malloc (sizeof (grub_efi_memory_descriptor_t) * 2 * mmap_num); + if (!result || !scanline_events) + { + grub_free (result); + grub_free (scanline_events); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate space for new memory map"); + } + + /* 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_malloc (mmap_reserved_size * sizeof (grub_efi_memory_descriptor_t)); + if (!efiemu_mmap) + { + grub_efiemu_unload (); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Couldn't initilaize mmap"); + } + + if ((err = efiemu_alloc_requests ())) + return err; + if ((err = grub_efiemu_mmap_fill ())) + return err; + return grub_efiemu_mmap_sort_and_uniq (); +} diff --git a/efiemu/pnvram.c b/efiemu/pnvram.c new file mode 100644 index 0000000..6b740f2 --- /dev/null +++ b/efiemu/pnvram.c @@ -0,0 +1,395 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; + +/* Temporary place */ +static grub_uint8_t *nvram; +static grub_size_t nvramsize; +static grub_uint32_t high_monotonic_count; +static grub_int16_t timezone; +static grub_uint8_t daylight; +static grub_uint32_t accuracy; + +static const struct grub_arg_option options[] = { + {"size", 's', 0, "number of bytes to reserve for pseudo NVRAM", 0, + ARG_TYPE_INT}, + {"high-monotonic-count", 'm', 0, + "Initial value of high monotonic count", 0, ARG_TYPE_INT}, + {"timezone", 't', 0, + "Timezone, offset in minutes from GMT", 0, ARG_TYPE_INT}, + {"accuracy", 'a', 0, + "Accuracy of clock, in 1e-12 units", 0, ARG_TYPE_INT}, + {"daylight", 'd', 0, + "Daylight value, as per EFI specifications", 0, ARG_TYPE_INT}, + {0, 0, 0, 0, 0, 0} +}; + +/* Parse signed value */ +static int +grub_strtosl (char *arg, char **end, int base) +{ + if (arg[0] == '-') + return -grub_strtoul (arg + 1, end, base); + return grub_strtoul (arg, end, base); +} + +/* Export stuff for efiemu */ +static grub_err_t +nvram_set (void * data __attribute__ ((unused))) +{ + /* Take definitive pointers */ + grub_uint8_t *nvram_def = 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_def + = grub_efiemu_mm_obtain_request (high_monotonic_count_handle); + grub_int16_t *timezone_def + = grub_efiemu_mm_obtain_request (timezone_handle); + grub_uint8_t *daylight_def + = grub_efiemu_mm_obtain_request (daylight_handle); + grub_uint32_t *accuracy_def + = grub_efiemu_mm_obtain_request (accuracy_handle); + + /* Copy to definitive loaction */ + grub_dprintf ("efiemu", "preparing pnvram\n"); + grub_memcpy (nvram_def, nvram, nvramsize); + *nvramsize_def = nvramsize; + *high_monotonic_count_def = high_monotonic_count; + *timezone_def = timezone; + *daylight_def = daylight; + *accuracy_def = accuracy; + + /* 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_free (nvram); + nvram = 0; +} + +/* Load the variables file It's in format + guid1:attr1:name1:data1; + guid2:attr2:name2:data2; + ... + Where all fields are in hex +*/ +static grub_err_t +read_pnvram (char *filename) +{ + char *buf, *ptr, *ptr2; + grub_file_t file; + grub_size_t size; + grub_uint8_t *nvramptr = nvram; + struct efi_variable *efivar; + grub_size_t guidlen, datalen; + unsigned i, j; + + file = grub_file_open (filename); + if (!file) + return grub_error (GRUB_ERR_BAD_OS, "couldn't read pnvram"); + size = grub_file_size (file); + buf = grub_malloc (size + 1); + if (!buf) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't read pnvram"); + if (grub_file_read (file, buf, size) != (grub_ssize_t) size) + return grub_error (GRUB_ERR_BAD_OS, "couldn't read pnvram"); + buf[size] = 0; + grub_file_close (file); + + for (ptr = buf; *ptr; ) + { + if (grub_isspace (*ptr)) + { + ptr++; + continue; + } + + efivar = (struct efi_variable *) nvramptr; + if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "file is too large for reserved variable space"); + + nvramptr += sizeof (struct efi_variable); + + /* look ahow long guid field is*/ + guidlen = 0; + for (ptr2 = ptr; (grub_isspace (*ptr2) + || (*ptr2 >= '0' && *ptr2 <= '9') + || (*ptr2 >= 'a' && *ptr2 <= 'f') + || (*ptr2 >= 'A' && *ptr2 <= 'F')); + ptr2++) + if (!grub_isspace (*ptr2)) + guidlen++; + guidlen /= 2; + + /* Read guid */ + if (guidlen != sizeof (efivar->guid)) + { + grub_free (buf); + return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename); + } + for (i = 0; i < 2 * sizeof (efivar->guid); i++) + { + int hex = 0; + while (grub_isspace (*ptr)) + ptr++; + if (*ptr >= '0' && *ptr <= '9') + hex = *ptr - '0'; + if (*ptr >= 'a' && *ptr <= 'f') + hex = *ptr - 'a' + 10; + if (*ptr >= 'A' && *ptr <= 'F') + hex = *ptr - 'A' + 10; + + if (i%2 == 0) + ((grub_uint8_t *)&(efivar->guid))[i/2] = hex << 4; + else + ((grub_uint8_t *)&(efivar->guid))[i/2] |= hex; + ptr++; + } + + while (grub_isspace (*ptr)) + ptr++; + if (*ptr != ':') + { + grub_dprintf ("efiemu", "Not colon\n"); + grub_free (buf); + return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename); + } + ptr++; + while (grub_isspace (*ptr)) + ptr++; + + /* Attributes can be just parsed by existing functions */ + efivar->attributes = grub_strtoul (ptr, &ptr, 16); + + while (grub_isspace (*ptr)) + ptr++; + if (*ptr != ':') + { + grub_dprintf ("efiemu", "Not colon\n"); + grub_free (buf); + return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename); + } + ptr++; + while (grub_isspace (*ptr)) + ptr++; + + /* Read name and value */ + for (j = 0; j < 2; j++) + { + /* Look the length */ + datalen = 0; + for (ptr2 = ptr; *ptr2 && (grub_isspace (*ptr2) + || (*ptr2 >= '0' && *ptr2 <= '9') + || (*ptr2 >= 'a' && *ptr2 <= 'f') + || (*ptr2 >= 'A' && *ptr2 <= 'F')); + ptr2++) + if (!grub_isspace (*ptr2)) + datalen++; + datalen /= 2; + + if (nvramptr - nvram + datalen > nvramsize) + { + grub_free (buf); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "file is too large for reserved " + " variable space"); + } + + for (i = 0; i < 2 * datalen; i++) + { + int hex = 0; + while (grub_isspace (*ptr)) + ptr++; + if (*ptr >= '0' && *ptr <= '9') + hex = *ptr - '0'; + if (*ptr >= 'a' && *ptr <= 'f') + hex = *ptr - 'a' + 10; + if (*ptr >= 'A' && *ptr <= 'F') + hex = *ptr - 'A' + 10; + + if (i%2 == 0) + nvramptr[i/2] = hex << 4; + else + nvramptr[i/2] |= hex; + ptr++; + } + nvramptr += datalen; + while (grub_isspace (*ptr)) + ptr++; + if (*ptr != (j ? ';' : ':')) + { + grub_free (buf); + grub_dprintf ("efiemu", j?"Not semicolon\n":"Not colon\n"); + return grub_error (GRUB_ERR_BAD_OS, "can't parse %s", filename); + } + if (j) + efivar->size = datalen; + else + efivar->namelen = datalen; + + ptr++; + } + } + grub_free (buf); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_efiemu_make_nvram (void) +{ + grub_err_t err; + err = grub_efiemu_register_prepare_hook (nvram_set, nvram_unload, 0); + + if (err) + { + grub_free (nvram); + 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); + + grub_efiemu_request_symbols (6); + return GRUB_ERR_NONE; +} + +grub_err_t +grub_efiemu_pnvram (void) +{ + if (nvram) + return GRUB_ERR_NONE; + + nvramsize = 2048; + high_monotonic_count = 1; + timezone = GRUB_EFI_UNSPECIFIED_TIMEZONE; + accuracy = 50000000; + daylight = 0; + + nvram = grub_malloc (nvramsize); + if (!nvram) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Couldn't allocate space for temporary pnvram storage"); + grub_memset (nvram, 0, nvramsize); + + return grub_efiemu_make_nvram (); +} + +static grub_err_t +grub_cmd_efiemu_pnvram (struct grub_extcmd *cmd, + int argc, char **args) +{ + struct grub_arg_list *state = cmd->state; + grub_err_t err; + + if (argc > 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one argument expected"); + + nvramsize = state[0].set ? grub_strtoul (state[0].arg, 0, 0) : 2048; + high_monotonic_count = state[1].set ? grub_strtoul (state[1].arg, 0, 0) : 1; + timezone = state[2].set ? grub_strtosl (state[2].arg, 0, 0) + : GRUB_EFI_UNSPECIFIED_TIMEZONE; + accuracy = state[3].set ? grub_strtoul (state[3].arg, 0, 0) : 50000000; + daylight = state[4].set ? grub_strtoul (state[4].arg, 0, 0) : 0; + + nvram = grub_malloc (nvramsize); + if (!nvram) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Couldn't allocate space for temporary pnvram storage"); + grub_memset (nvram, 0, nvramsize); + + if (argc == 1 && (err = read_pnvram (args[0]))) + { + grub_free (nvram); + return err; + } + return grub_efiemu_make_nvram (); +} + +static grub_extcmd_t cmd; + +void grub_efiemu_pnvram_cmd_register (void); +void grub_efiemu_pnvram_cmd_unregister (void); + +void +grub_efiemu_pnvram_cmd_register (void) +{ + cmd = grub_register_extcmd ("efiemu_pnvram", grub_cmd_efiemu_pnvram, + GRUB_COMMAND_FLAG_BOTH, + "efiemu_pnvram [FILENAME]", + "Initialise pseudo-NVRAM and load variables " + "from FILE", + options); +} + +void +grub_efiemu_pnvram_cmd_unregister (void) +{ + grub_unregister_extcmd (cmd); +} diff --git a/efiemu/prepare.c b/efiemu/prepare.c new file mode 100644 index 0000000..d078729 --- /dev/null +++ b/efiemu/prepare.c @@ -0,0 +1,127 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include + +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; + TYPE (grub_efiemu_configuration_table) *conftables = 0; + TYPE (grub_efiemu_runtime_services) *runtime_services; + 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_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 */ + if ((err = grub_efiemu_loadcore_load ())) + { + grub_efiemu_unload (); + return err; + } + + if ((err = grub_efiemu_resolve_symbol ("efiemu_system_table", + &handle, &off))) + { + grub_efiemu_unload (); + return err; + } + + SUFFIX (grub_efiemu_system_table) + = (TYPE (grub_efi_system_table) *) + ((grub_uint8_t *)grub_efiemu_mm_obtain_request (handle) + off); + + /* compute CRC32 of runtime_services */ + if ((err = grub_efiemu_resolve_symbol ("efiemu_runtime_services", + &handle, &off))) + return err; + runtime_services = (TYPE (grub_efiemu_runtime_services) *) + ((grub_uint8_t *)grub_efiemu_mm_obtain_request (handle) + off); + runtime_services->hdr.crc32 = 0; + runtime_services->hdr.crc32 = grub_getcrc32 + (0, runtime_services, runtime_services->hdr.header_size); + + /* Put pointer to the list of configuration tables in system table */ + grub_efiemu_write_value + (&(SUFFIX (grub_efiemu_system_table)->configuration_table), 0, + conftable_handle, 0, 1, + sizeof (SUFFIX (grub_efiemu_system_table)->configuration_table)); + SUFFIX(grub_efiemu_system_table)->num_table_entries = cntconftables; + + /* Fill the list of configuration tables */ + conftables = (TYPE (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 + = PTR_TO_UINT64 (cur->get_table (cur->data)); + else + conftables[i].vendor_table = PTR_TO_UINT64 (cur->data); + } + + /* compute CRC32 of system table */ + SUFFIX (grub_efiemu_system_table)->hdr.crc32 = 0; + SUFFIX (grub_efiemu_system_table)->hdr.crc32 + = grub_getcrc32 (0, SUFFIX (grub_efiemu_system_table), + SUFFIX (grub_efiemu_system_table)->hdr.header_size); + + grub_dprintf ("efiemu","system_table = %p, runtime_services = %p," + " conftables = %p (%d entries)\n", + SUFFIX (grub_efiemu_system_table), runtime_services, + conftables, cntconftables); + + return GRUB_ERR_NONE; +} diff --git a/efiemu/prepare32.c b/efiemu/prepare32.c new file mode 100644 index 0000000..48c177f --- /dev/null +++ b/efiemu/prepare32.c @@ -0,0 +1,23 @@ +/* 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 . + */ + +#define SUFFIX(x) x ## 32 +#define TYPE(x) x ## 32_t + +#include "prepare.c" diff --git a/efiemu/prepare64.c b/efiemu/prepare64.c new file mode 100644 index 0000000..f1399bd --- /dev/null +++ b/efiemu/prepare64.c @@ -0,0 +1,23 @@ +/* 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 . + */ + +#define SUFFIX(x) x ## 64 +#define TYPE(x) x ## 64_t + +#include "prepare.c" diff --git a/efiemu/runtime/config.h b/efiemu/runtime/config.h new file mode 100644 index 0000000..26fb2ff --- /dev/null +++ b/efiemu/runtime/config.h @@ -0,0 +1,34 @@ +/* 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 . + */ + +#define GRUB_TYPES_CPU_HEADER 1 + +#ifdef ELF32 +# 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 +#else +# 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 +#endif diff --git a/efiemu/runtime/efiemu.S b/efiemu/runtime/efiemu.S new file mode 100644 index 0000000..e0923cc --- /dev/null +++ b/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 . + */ + +#include + +/* + * 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/efiemu/runtime/efiemu.c b/efiemu/runtime/efiemu.c new file mode 100644 index 0000000..81fb562 --- /dev/null +++ b/efiemu/runtime/efiemu.c @@ -0,0 +1,623 @@ +/* + * 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 . + */ + +/* 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) */ + +#include +#include +#include +#include + +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); + + +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) + __attribute__ ((section(".text-physical"))); + +grub_efi_status_t +efiemu_convert_pointer (grub_efi_uintn_t debug_disposition, + void **address) + __attribute__ ((section(".text-physical"))); + +grub_efi_status_t +efiemu_get_variable (grub_efi_char16_t *variable_name, + 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, + 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 *) + __attribute__ ((section(".text-physical"))); +grub_efi_status_t +EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition, + void **address) + __attribute__ ((section(".text-physical"))); +static grub_uint32_t +efiemu_getcrc32 (grub_uint32_t crc, void *buf, int size) + __attribute__ ((section(".text-physical"))); +static void +init_crc32_table (void) + __attribute__ ((section(".text-physical"))); + +/* + 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; } + +static int ptv_relocated = 0; + +/* Interface with grub */ +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 stadalone */ +static void +efiemu_memcpy (void *to, void *from, int count) +{ + int i; + for (i = 0; i < count; i++) + ((grub_uint8_t *) to)[i] = ((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 (void *a, 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 %%bl, %%al\n" + "outb %%al,$0x71": :"a" (addr), "b" (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; +} + +/* Folowing 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 void +init_crc32_table (void) +{ + auto grub_uint32_t reflect (grub_uint32_t ref, int len); + 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; + } + + 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 (ptv_relocated) + return GRUB_EFI_UNSUPPORTED; + 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 *) UINT_TO_PTR (cur_relloc->addr)) += corr; + break; + case 4: + *((grub_uint32_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + break; + case 2: + *((grub_uint16_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + break; + case 1: + *((grub_uint8_t *) UINT_TO_PTR (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 (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, + 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, + 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/efiemu/runtime/efiemu.sh b/efiemu/runtime/efiemu.sh new file mode 100644 index 0000000..1ae059a --- /dev/null +++ b/efiemu/runtime/efiemu.sh @@ -0,0 +1,4 @@ +gcc -c -m32 -DELF32 -o efiemu32.o ./efiemu.c -Wall -Werror -nostdlib -O2 -I. -I../../include +gcc -c -m64 -DELF64 -o efiemu64_c.o ./efiemu.c -Wall -Werror -mcmodel=large -O2 -I. -I../../include +gcc -c -m64 -DELF64 -o efiemu64_s.o ./efiemu.S -Wall -Werror -mcmodel=large -O2 -I. -I../../include +ld -o efiemu64.o -r efiemu64_s.o efiemu64_c.o -nostdlib diff --git a/efiemu/symbols.c b/efiemu/symbols.c new file mode 100644 index 0000000..b3111b6 --- /dev/null +++ b/efiemu/symbols.c @@ -0,0 +1,188 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include + +static int ptv_written = 0; +static int ptv_alloc = 0; +static int ptv_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; +} + +/* 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_BAD_ARGUMENT, + "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, "symbol %s isn't 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_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't register symbol"); + 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); + grub_efiemu_register_symbol ("efiemu_ptv_relloc", ptv_handle, 0); + return grub_errno; +} + +/* 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_OUT_OF_MEMORY, + "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 = PTR_TO_UINT64 (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 -= PTR_TO_UINT32 (grub_efiemu_mm_obtain_request (minus_handle)); + + if (plus_handle) + value += PTR_TO_UINT32 (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_BAD_ARGUMENT, "wrong symbol size"); + } + + return GRUB_ERR_NONE; +} diff --git a/include/grub/autoefi.h b/include/grub/autoefi.h new file mode 100644 index 0000000..f9c552a --- /dev/null +++ b/include/grub/autoefi.h @@ -0,0 +1,75 @@ +/* + * 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 . + */ +/* This file provides some abstractions so that the same code compiles with + both efi and efiemu + */ +#ifndef GRUB_AUTOEFI_HEADER +#define GRUB_AUTOEFI_HEADER 1 + +#include + +#ifdef GRUB_MACHINE_EFI +# include +# define grub_autoefi_get_memory_map grub_efi_get_memory_map +# define grub_autoefi_finish_boot_services grub_efi_finish_boot_services +# define grub_autoefi_system_table grub_efi_system_table +# define grub_autoefi_mmap_iterate grub_machine_mmap_iterate +static inline grub_err_t grub_autoefi_prepare (void) +{ + return GRUB_ERR_NONE; +}; +# define GRUB_AUTOEFI_MEMORY_AVAILABLE GRUB_MACHINE_MEMORY_AVAILABLE +# define GRUB_AUTOEFI_MEMORY_RESERVED GRUB_MACHINE_MEMORY_RESERVED +# ifdef GRUB_MACHINE_MEMORY_ACPI +# define GRUB_AUTOEFI_MEMORY_ACPI GRUB_MACHINE_MEMORY_ACPI +# endif +# ifdef GRUB_MACHINE_MEMORY_NVS +# define GRUB_AUTOEFI_MEMORY_NVS GRUB_MACHINE_MEMORY_NVS +# endif +# ifdef GRUB_MACHINE_MEMORY_CODE +# define GRUB_AUTOEFI_MEMORY_CODE GRUB_MACHINE_MEMORY_CODE +# endif +# define SYSTEM_TABLE_SIZEOF(x) (sizeof(grub_efi_system_table->x)) +# define SYSTEM_TABLE_VAR(x) ((void *)&(grub_efi_system_table->x)) +# define SYSTEM_TABLE_PTR(x) ((void *)(grub_efi_system_table->x)) +# define SIZEOF_OF_UINTN sizeof (grub_efi_uintn_t) +# define SYSTEM_TABLE(x) (grub_efi_system_table->x) +# define EFI_PRESENT 1 +#else +# include +# define grub_autoefi_get_memory_map grub_efiemu_get_memory_map +# define grub_autoefi_finish_boot_services grub_efiemu_finish_boot_services +# define grub_autoefi_system_table grub_efiemu_system_table +# define grub_autoefi_mmap_iterate grub_efiemu_mmap_iterate +# define grub_autoefi_prepare grub_efiemu_prepare +# define GRUB_AUTOEFI_MEMORY_AVAILABLE GRUB_EFIEMU_MEMORY_AVAILABLE +# define GRUB_AUTOEFI_MEMORY_RESERVED GRUB_EFIEMU_MEMORY_RESERVED +# define GRUB_AUTOEFI_MEMORY_ACPI GRUB_EFIEMU_MEMORY_ACPI +# define GRUB_AUTOEFI_MEMORY_NVS GRUB_EFIEMU_MEMORY_NVS +# define GRUB_AUTOEFI_MEMORY_CODE GRUB_EFIEMU_MEMORY_CODE +# define SYSTEM_TABLE_SIZEOF GRUB_EFIEMU_SYSTEM_TABLE_SIZEOF +# define SYSTEM_TABLE_VAR GRUB_EFIEMU_SYSTEM_TABLE_VAR +# define SYSTEM_TABLE_PTR GRUB_EFIEMU_SYSTEM_TABLE_PTR +# define SIZEOF_OF_UINTN GRUB_EFIEMU_SIZEOF_OF_UINTN +# define SYSTEM_TABLE GRUB_EFIEMU_SYSTEM_TABLE +# define grub_efi_allocate_pages(x,y) (x) +# define grub_efi_free_pages(x,y) GRUB_EFI_SUCCESS +# define EFI_PRESENT 1 +#endif + +#endif diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 598c7c3..432266b 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -40,15 +40,15 @@ #define GRUB_EFI_TPL_NOTIFY 16 #define GRUB_EFI_TPL_HIGH_LEVEL 31 -#define GRUB_EFI_MEMORY_UC 0x0000000000000001 -#define GRUB_EFI_MEMORY_WC 0x0000000000000002 -#define GRUB_EFI_MEMORY_WT 0x0000000000000004 -#define GRUB_EFI_MEMORY_WB 0x0000000000000008 -#define GRUB_EFI_MEMORY_UCE 0x0000000000000010 -#define GRUB_EFI_MEMORY_WP 0x0000000000001000 -#define GRUB_EFI_MEMORY_RP 0x0000000000002000 -#define GRUB_EFI_MEMORY_XP 0x0000000000004000 -#define GRUB_EFI_MEMORY_RUNTIME 0x8000000000000000 +#define GRUB_EFI_MEMORY_UC 0x0000000000000001LL +#define GRUB_EFI_MEMORY_WC 0x0000000000000002LL +#define GRUB_EFI_MEMORY_WT 0x0000000000000004LL +#define GRUB_EFI_MEMORY_WB 0x0000000000000008LL +#define GRUB_EFI_MEMORY_UCE 0x0000000000000010LL +#define GRUB_EFI_MEMORY_WP 0x0000000000001000LL +#define GRUB_EFI_MEMORY_RP 0x0000000000002000LL +#define GRUB_EFI_MEMORY_XP 0x0000000000004000LL +#define GRUB_EFI_MEMORY_RUNTIME 0x8000000000000000LL #define GRUB_EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001 #define GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002 @@ -619,7 +619,7 @@ struct grub_efi_time grub_efi_int16_t time_zone; grub_efi_uint8_t daylight; grub_efi_uint8_t pad2; -}; +} __attribute__ ((packed)); typedef struct grub_efi_time grub_efi_time_t; struct grub_efi_time_capabilities @@ -936,6 +936,9 @@ struct grub_efi_configuration_table } __attribute__ ((packed)); typedef struct grub_efi_configuration_table grub_efi_configuration_table_t; +#define GRUB_EFIEMU_SYSTEM_TABLE_SIGNATURE 0x5453595320494249LL +#define GRUB_EFIEMU_RUNTIME_SERVICES_SIGNATURE 0x56524553544e5552LL + struct grub_efi_simple_input_interface { grub_efi_status_t diff --git a/include/grub/efiemu/efiemu.h b/include/grub/efiemu/efiemu.h new file mode 100644 index 0000000..0fd080b --- /dev/null +++ b/include/grub/efiemu/efiemu.h @@ -0,0 +1,275 @@ +/* + * 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 . + */ + +#ifndef GRUB_EFI_EMU_HEADER +#define GRUB_EFI_EMU_HEADER 1 + +#include +#include + +#define GRUB_EFIEMU_PAGESIZE 4096 + +/* EFI api defined in 32-bit and 64-bit version*/ +struct grub_efi_system_table32 +{ + grub_efi_table_header_t hdr; + grub_efi_uint32_t firmware_vendor; + grub_efi_uint32_t firmware_revision; + grub_efi_uint32_t console_in_handler; + grub_efi_uint32_t con_in; + grub_efi_uint32_t console_out_handler; + grub_efi_uint32_t con_out; + grub_efi_uint32_t standard_error_handle; + grub_efi_uint32_t std_err; + grub_efi_uint32_t runtime_services; + grub_efi_uint32_t boot_services; + grub_efi_uint32_t num_table_entries; + grub_efi_uint32_t configuration_table; +} __attribute__ ((packed)); +typedef struct grub_efi_system_table32 grub_efi_system_table32_t; + +struct grub_efi_system_table64 +{ + grub_efi_table_header_t hdr; + grub_efi_uint64_t firmware_vendor; + grub_efi_uint32_t firmware_revision; + grub_efi_uint32_t pad; + grub_efi_uint64_t console_in_handler; + grub_efi_uint64_t con_in; + grub_efi_uint64_t console_out_handler; + grub_efi_uint64_t con_out; + grub_efi_uint64_t standard_error_handle; + grub_efi_uint64_t std_err; + grub_efi_uint64_t runtime_services; + grub_efi_uint64_t boot_services; + grub_efi_uint64_t num_table_entries; + grub_efi_uint64_t configuration_table; +} __attribute__ ((packed)); +typedef struct grub_efi_system_table64 grub_efi_system_table64_t; + +struct grub_efiemu_runtime_services32 +{ + grub_efi_table_header_t hdr; + grub_efi_uint32_t get_time; + grub_efi_uint32_t set_time; + grub_efi_uint32_t get_wakeup_time; + grub_efi_uint32_t set_wakeup_time; + grub_efi_uint32_t set_virtual_address_map; + grub_efi_uint32_t convert_pointer; + grub_efi_uint32_t get_variable; + grub_efi_uint32_t get_next_variable_name; + grub_efi_uint32_t set_variable; + grub_efi_uint32_t get_next_high_monotonic_count; + grub_efi_uint32_t reset_system; +} __attribute__ ((packed)); +typedef struct grub_efiemu_runtime_services32 grub_efiemu_runtime_services32_t; + +struct grub_efiemu_runtime_services64 +{ + grub_efi_table_header_t hdr; + grub_efi_uint64_t get_time; + grub_efi_uint64_t set_time; + grub_efi_uint64_t get_wakeup_time; + grub_efi_uint64_t set_wakeup_time; + grub_efi_uint64_t set_virtual_address_map; + grub_efi_uint64_t convert_pointer; + grub_efi_uint64_t get_variable; + grub_efi_uint64_t get_next_variable_name; + grub_efi_uint64_t set_variable; + grub_efi_uint64_t get_next_high_monotonic_count; + grub_efi_uint64_t reset_system; +} __attribute__ ((packed)); +typedef struct grub_efiemu_runtime_services64 grub_efiemu_runtime_services64_t; + +extern grub_efi_system_table32_t *grub_efiemu_system_table32; +extern grub_efi_system_table64_t *grub_efiemu_system_table64; + +/* Convenience macroses to access currently loaded efiemu */ +#define grub_efiemu_system_table ((grub_efiemu_sizeof_uintn_t () == 8) \ + ? (void *) grub_efiemu_system_table64 \ + : (void *) grub_efiemu_system_table32) +#define GRUB_EFIEMU_SIZEOF_OF_UINTN (grub_efiemu_sizeof_uintn_t ()) +#define GRUB_EFIEMU_SYSTEM_TABLE(x) ((grub_efiemu_sizeof_uintn_t () == 8) \ + ? grub_efiemu_system_table64->x \ + : grub_efiemu_system_table32->x) +#define GRUB_EFIEMU_SYSTEM_TABLE_SET(x,y) ((grub_efiemu_sizeof_uintn_t () == 8)\ + ? (grub_efiemu_system_table64->x \ + = (y)) \ + : (grub_efiemu_system_table32->x \ + = (y))) +#define GRUB_EFIEMU_SYSTEM_TABLE_PTR(x) ((grub_efiemu_sizeof_uintn_t () == 8)\ + ? UINT_TO_PTR \ + (grub_efiemu_system_table64->x) \ + : UINT_TO_PTR \ + (grub_efiemu_system_table32->x)) +#define GRUB_EFIEMU_SYSTEM_TABLE_VAR(x) ((grub_efiemu_sizeof_uintn_t () == 8) \ + ? (void *) \ + &(grub_efiemu_system_table64->x) \ + : (void *) \ + &(grub_efiemu_system_table32->x)) +#define GRUB_EFIEMU_SYSTEM_TABLE_SIZEOF(x) \ + ((grub_efiemu_sizeof_uintn_t () == 8) \ + ? sizeof(grub_efiemu_system_table64->x)\ + : sizeof(grub_efiemu_system_table32->x)) +#define GRUB_EFIEMU_SYSTEM_TABLE_SIZEOF_TOTAL ((grub_efiemu_sizeof_uintn_t () == 8) ? sizeof(*grub_efiemu_system_table64):sizeof(*grub_efiemu_system_table32)) + +/* ELF management definitions and functions */ + +struct grub_efiemu_segment +{ + struct grub_efiemu_segment *next; + grub_size_t size; + unsigned section; + int handle; + int ptv_rel_needed; + grub_off_t off; + void *srcptr; +}; +typedef struct grub_efiemu_segment *grub_efiemu_segment_t; + +struct grub_efiemu_elf_sym +{ + int handle; + grub_off_t off; + unsigned section; +}; + +int grub_efiemu_check_header32 (void *ehdr, grub_size_t size); +int grub_efiemu_check_header64 (void *ehdr, grub_size_t size); +grub_err_t grub_efiemu_loadcore_init32 (void *core, grub_size_t core_size, + grub_efiemu_segment_t *segments); +grub_err_t grub_efiemu_loadcore_init64 (void *core, grub_size_t core_size, + grub_efiemu_segment_t *segments); +grub_err_t grub_efiemu_loadcore_load32 (void *core, + grub_size_t core_size, + grub_efiemu_segment_t segments); +grub_err_t grub_efiemu_loadcore_load64 (void *core, + grub_size_t core_size, + grub_efiemu_segment_t segments); +grub_err_t grub_efiemu_loadcore_unload32 (void); +grub_err_t grub_efiemu_loadcore_unload64 (void); +grub_err_t grub_efiemu_loadcore_unload(void); +grub_err_t grub_efiemu_loadcore_init (grub_file_t file); +grub_err_t grub_efiemu_loadcore_load (void); + +/* Configuration tables manipulation. Definitions and functions */ +struct grub_efiemu_configuration_table +{ + struct grub_efiemu_configuration_table *next; + grub_efi_guid_t guid; + void * (*get_table) (void *data); + void (*unload) (void *data); + void *data; +}; +struct grub_efiemu_configuration_table32 +{ + grub_efi_guid_t vendor_guid; + grub_efi_uint32_t vendor_table; +} __attribute__ ((packed)); +typedef struct grub_efiemu_configuration_table32 grub_efiemu_configuration_table32_t; +struct grub_efiemu_configuration_table64 +{ + grub_efi_guid_t vendor_guid; + grub_efi_uint64_t vendor_table; +} __attribute__ ((packed)); +typedef struct grub_efiemu_configuration_table32 grub_efiemu_configuration_table64_t; +grub_err_t grub_efiemu_unregister_configuration_table (grub_efi_guid_t guid); +grub_err_t +grub_efiemu_register_configuration_table (grub_efi_guid_t guid, + void * (*get_table) (void *data), + void (*unload) (void *data), + void *data); + +/* Memory management functions */ +int grub_efiemu_request_memalign (grub_size_t align, grub_size_t size, + grub_efi_memory_type_t type); +void *grub_efiemu_mm_obtain_request (int handle); +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); +grub_err_t grub_efiemu_mm_unload (void); +grub_err_t grub_efiemu_mm_do_alloc (void); +grub_err_t grub_efiemu_mm_init (void); +void *grub_efiemu_mm_obtain_request (int handle); +void grub_efiemu_mm_return_request (int handle); +grub_efi_memory_type_t grub_efiemu_mm_get_type (int handle); + +/* Drop-in replacements for grub_efi_* and grub_machine_* */ +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); +grub_err_t +grub_efiemu_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, + grub_uint64_t, + grub_uint32_t)); +int grub_efiemu_sizeof_uintn_t (void); +int grub_efiemu_exit_boot_services (grub_efi_uintn_t map_key); +int grub_efiemu_finish_boot_services (void); +grub_err_t +grub_efiemu_get_lower_upper_memory (grub_uint64_t *lower, grub_uint64_t *upper); +#define GRUB_EFIEMU_MEMORY_AVAILABLE 1 +#define GRUB_EFIEMU_MEMORY_RESERVED 2 +#define GRUB_EFIEMU_MEMORY_ACPI 3 +#define GRUB_EFIEMU_MEMORY_NVS 4 +#define GRUB_EFIEMU_MEMORY_CODE 5 + +/* efiemu main control definitions and functions*/ +typedef enum {GRUB_EFIEMU_NOTLOADED, + GRUB_EFIEMU32, GRUB_EFIEMU64} grub_efiemu_mode_t; +struct grub_efiemu_prepare_hook +{ + struct grub_efiemu_prepare_hook *next; + grub_err_t (*hook) (void *data); + void (*unload) (void *data); + void *data; +}; +grub_err_t grub_efiemu_prepare32 (struct grub_efiemu_prepare_hook + *prepare_hooks, + struct grub_efiemu_configuration_table + *config_tables); +grub_err_t grub_efiemu_prepare64 (struct grub_efiemu_prepare_hook + *prepare_hooks, + struct grub_efiemu_configuration_table + *config_tables); +grub_err_t grub_efiemu_unload (void); +grub_err_t grub_efiemu_prepare (void); +grub_err_t +grub_efiemu_register_prepare_hook (grub_err_t (*hook) (void *data), + void (*unload) (void *data), + void *data); + +/* symbols and pointers */ +grub_err_t grub_efiemu_alloc_syms (void); +grub_err_t grub_efiemu_request_symbols (int num); +grub_err_t grub_efiemu_resolve_symbol (const char *name, + int *handle, grub_off_t *off); +grub_err_t grub_efiemu_register_symbol (const char *name, + int handle, grub_off_t off); +void grub_efiemu_free_syms (void); +grub_err_t grub_efiemu_write_value (void * addr, grub_uint32_t value, + int plus_handle, + int minus_handle, int ptv_needed, int size); +grub_err_t grub_efiemu_pnvram (void); +grub_err_t grub_efiemu_prepare (void); +char *grub_efiemu_get_default_core_name (void); +void grub_efiemu_pnvram_cmd_unregister (void); +#endif /* ! GRUB_EFI_EMU_HEADER */ diff --git a/include/grub/efiemu/runtime.h b/include/grub/efiemu/runtime.h new file mode 100644 index 0000000..1eb474a --- /dev/null +++ b/include/grub/efiemu/runtime.h @@ -0,0 +1,37 @@ +/* + * 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 . + */ + +#ifndef GRUB_EFI_EMU_RUNTIME_HEADER +#define GRUB_EFI_EMU_RUNTIME_HEADER 1 + +struct grub_efiemu_ptv_rel +{ + grub_uint64_t addr; + grub_efi_memory_type_t plustype; + grub_efi_memory_type_t minustype; + grub_uint32_t size; +} __attribute__ ((packed)); + +struct efi_variable +{ + grub_efi_guid_t guid; + grub_uint32_t namelen; + grub_uint32_t size; + grub_efi_uint32_t attributes; +} __attribute__ ((packed)); +#endif /* ! GRUB_EFI_EMU_RUNTIME_HEADER */ diff --git a/include/grub/i386/efiemu.h b/include/grub/i386/efiemu.h new file mode 100644 index 0000000..1b29f45 --- /dev/null +++ b/include/grub/i386/efiemu.h @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +#ifndef GRUB_ARCH_EFI_EMU_HEADER +#define GRUB_ARCH_EFI_EMU_HEADER 1 + +grub_err_t +grub_arch_efiemu_relocate_symbols32 (grub_efiemu_segment_t segs, + struct grub_efiemu_elf_sym *elfsyms, + void *ehdr); +grub_err_t +grub_arch_efiemu_relocate_symbols64 (grub_efiemu_segment_t segs, + struct grub_efiemu_elf_sym *elfsyms, + void *ehdr); + +int grub_arch_efiemu_check_header32 (void *ehdr); +int grub_arch_efiemu_check_header64 (void *ehdr); +#endif diff --git a/include/grub/i386/pc/efiemu.h b/include/grub/i386/pc/efiemu.h new file mode 100644 index 0000000..f269dd0 --- /dev/null +++ b/include/grub/i386/pc/efiemu.h @@ -0,0 +1,24 @@ +/* + * 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 . + */ + +#ifndef GRUB_MACHINE_EFI_EMU_HEADER +#define GRUB_MACHINE_EFI_EMU_HEADER 1 + +grub_err_t grub_machine_efiemu_init_tables (void); + +#endif diff --git a/util/i386/pc/grub-install.in b/util/i386/pc/grub-install.in index 8a7a4da..31c2dc7 100644 --- a/util/i386/pc/grub-install.in +++ b/util/i386/pc/grub-install.in @@ -211,7 +211,7 @@ if test -n "$tmp"; then fi # Copy the GRUB images to the GRUB directory. -for file in ${grubdir}/*.mod ${grubdir}/*.lst ${grubdir}/*.img; do +for file in ${grubdir}/*.mod ${grubdir}/*.lst ${grubdir}/*.img ${grubdir}/efiemu??.o; do if test -f $file && [ "`basename $file`" != menu.lst ]; then rm -f $file || exit 1 fi @@ -220,7 +220,7 @@ for file in ${pkglibdir}/*.mod ${pkglibdir}/*.lst; do cp -f $file ${grubdir} || exit 1 done if [ "${target_cpu}-${platform}" = "i386-pc" ] ; then - for file in ${pkglibdir}/*.img; do + for file in ${pkglibdir}/*.img ${pkglibdir}/efiemu??.o; do cp -f $file ${grubdir} || exit 1 done fi diff --git a/util/i386/pc/grub-mkrescue.in b/util/i386/pc/grub-mkrescue.in index 358b37b..31b1f81 100644 --- a/util/i386/pc/grub-mkrescue.in +++ b/util/i386/pc/grub-mkrescue.in @@ -116,7 +116,7 @@ fi aux_dir=`mktemp -d` mkdir -p ${aux_dir}/boot/grub -cp ${input_dir}/*.mod \ +cp ${input_dir}/*.mod ${input_dir}/efiemu??.o \ ${input_dir}/command.lst ${input_dir}/moddep.lst ${input_dir}/fs.lst \ ${aux_dir}/boot/grub/