2009-05-02 Vladimir Serbinenko <phcoder@gmail.com>
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
This commit is contained in:
parent
f8efe3ad2d
commit
5caf964d75
29 changed files with 4068 additions and 14 deletions
41
ChangeLog
41
ChangeLog
|
@ -1,3 +1,44 @@
|
|||
2009-05-02 Vladimir Serbinenko <phcoder@gmail.com>
|
||||
|
||||
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-05-02 Vladimir Serbinenko <phcoder@gmail.com>
|
||||
|
||||
ACPI spoofing
|
||||
|
|
|
@ -185,7 +185,17 @@ pkglib_MODULES = biosdisk.mod chain.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
|
||||
|
@ -370,5 +380,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
|
||||
|
|
60
efiemu/i386/coredetect.c
Normal file
60
efiemu/i386/coredetect.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/machine/efiemu.h>
|
||||
#include <grub/command.h>
|
||||
|
||||
#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";
|
||||
}
|
114
efiemu/i386/loadcore32.c
Normal file
114
efiemu/i386/loadcore32.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/cpu/efiemu.h>
|
||||
#include <grub/elf.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
120
efiemu/i386/loadcore64.c
Normal file
120
efiemu/i386/loadcore64.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/cpu/efiemu.h>
|
||||
#include <grub/elf.h>
|
||||
|
||||
/* 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;
|
||||
}
|
76
efiemu/i386/pc/cfgtables.c
Normal file
76
efiemu/i386/pc/cfgtables.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* Register SMBIOS and ACPI tables. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/machine/efiemu.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/acpi.h>
|
||||
|
||||
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;
|
||||
|
||||
err = grub_efiemu_unregister_configuration_table (smbios);
|
||||
if (err)
|
||||
return err;
|
||||
err = grub_efiemu_unregister_configuration_table (acpi);
|
||||
if (err)
|
||||
return err;
|
||||
err = grub_efiemu_unregister_configuration_table (acpi20);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
table = grub_acpi_get_rsdpv1 ();
|
||||
if (table)
|
||||
{
|
||||
err = grub_efiemu_register_configuration_table (acpi, 0, 0, table);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
table = grub_acpi_get_rsdpv2 ();
|
||||
if (table)
|
||||
{
|
||||
err = grub_efiemu_register_configuration_table (acpi20, 0, 0, table);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
365
efiemu/loadcore.c
Normal file
365
efiemu/loadcore.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/cpu/efiemu.h>
|
||||
#include <grub/machine/efiemu.h>
|
||||
#include <grub/elf.h>
|
||||
|
||||
/* ELF symbols and their values */
|
||||
static struct grub_efiemu_elf_sym *grub_efiemu_elfsyms = 0;
|
||||
static int grub_efiemu_nelfsyms = 0;
|
||||
|
||||
/* Return the address of a section whose index is N. */
|
||||
static grub_err_t
|
||||
grub_efiemu_get_section_addr (grub_efiemu_segment_t segs, unsigned n,
|
||||
int *handle, grub_off_t *off)
|
||||
{
|
||||
grub_efiemu_segment_t seg;
|
||||
|
||||
for (seg = segs; seg; seg = seg->next)
|
||||
if (seg->section == n)
|
||||
{
|
||||
*handle = seg->handle;
|
||||
*off = seg->off;
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
return grub_error (GRUB_ERR_BAD_OS, "section %d not found", n);
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
SUFFIX (grub_efiemu_loadcore_unload) (void)
|
||||
{
|
||||
grub_free (grub_efiemu_elfsyms);
|
||||
grub_efiemu_elfsyms = 0;
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/* Check if EHDR is a valid ELF header. */
|
||||
int
|
||||
SUFFIX (grub_efiemu_check_header) (void *ehdr, grub_size_t size)
|
||||
{
|
||||
Elf_Ehdr *e = ehdr;
|
||||
|
||||
/* Check the header size. */
|
||||
if (size < sizeof (Elf_Ehdr))
|
||||
return 0;
|
||||
|
||||
/* Check the magic numbers. */
|
||||
if (!SUFFIX (grub_arch_efiemu_check_header) (ehdr)
|
||||
|| e->e_ident[EI_MAG0] != ELFMAG0
|
||||
|| e->e_ident[EI_MAG1] != ELFMAG1
|
||||
|| e->e_ident[EI_MAG2] != ELFMAG2
|
||||
|| e->e_ident[EI_MAG3] != ELFMAG3
|
||||
|| e->e_ident[EI_VERSION] != EV_CURRENT
|
||||
|| e->e_version != EV_CURRENT)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Load all segments from memory specified by E. */
|
||||
static grub_err_t
|
||||
grub_efiemu_load_segments (grub_efiemu_segment_t segs, const Elf_Ehdr *e)
|
||||
{
|
||||
Elf_Shdr *s;
|
||||
grub_efiemu_segment_t cur;
|
||||
|
||||
grub_dprintf ("efiemu", "loading segments\n");
|
||||
|
||||
for (cur=segs; cur; cur = cur->next)
|
||||
{
|
||||
s = (Elf_Shdr *)cur->srcptr;
|
||||
|
||||
if ((s->sh_flags & SHF_ALLOC) && s->sh_size)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
addr = (grub_uint8_t *) grub_efiemu_mm_obtain_request (cur->handle)
|
||||
+ cur->off;
|
||||
|
||||
switch (s->sh_type)
|
||||
{
|
||||
case SHT_PROGBITS:
|
||||
grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
|
||||
break;
|
||||
case SHT_NOBITS:
|
||||
grub_memset (addr, 0, s->sh_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/* Get a string at offset OFFSET from strtab */
|
||||
static char *
|
||||
grub_efiemu_get_string (unsigned offset, const Elf_Ehdr *e)
|
||||
{
|
||||
unsigned i;
|
||||
Elf_Shdr *s;
|
||||
|
||||
for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
|
||||
if (s->sh_type == SHT_STRTAB && offset < s->sh_size)
|
||||
return (char *) e + s->sh_offset + offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Request memory for segments and fill segments info */
|
||||
static grub_err_t
|
||||
grub_efiemu_init_segments (grub_efiemu_segment_t *segs, const Elf_Ehdr *e)
|
||||
{
|
||||
unsigned i;
|
||||
Elf_Shdr *s;
|
||||
|
||||
for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
|
||||
i < e->e_shnum;
|
||||
i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
|
||||
{
|
||||
if (s->sh_flags & SHF_ALLOC)
|
||||
{
|
||||
grub_efiemu_segment_t seg;
|
||||
seg = (grub_efiemu_segment_t) grub_malloc (sizeof (*seg));
|
||||
if (! seg)
|
||||
return grub_errno;
|
||||
|
||||
if (s->sh_size)
|
||||
{
|
||||
seg->handle
|
||||
= grub_efiemu_request_memalign
|
||||
(s->sh_addralign, s->sh_size,
|
||||
s->sh_flags & SHF_EXECINSTR ? GRUB_EFI_RUNTIME_SERVICES_CODE
|
||||
: GRUB_EFI_RUNTIME_SERVICES_DATA);
|
||||
if (seg->handle < 0)
|
||||
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;
|
||||
}
|
27
efiemu/loadcore32.c
Normal file
27
efiemu/loadcore32.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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"
|
27
efiemu/loadcore64.c
Normal file
27
efiemu/loadcore64.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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"
|
189
efiemu/loadcore_common.c
Normal file
189
efiemu/loadcore_common.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/file.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/cpu/efiemu.h>
|
||||
|
||||
/* Are we in 32 or 64-bit mode?*/
|
||||
static grub_efiemu_mode_t grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;
|
||||
/* Runtime ELF file */
|
||||
static grub_ssize_t efiemu_core_size;
|
||||
static void *efiemu_core = 0;
|
||||
/* Linked list of segments */
|
||||
static grub_efiemu_segment_t efiemu_segments = 0;
|
||||
|
||||
/* equivalent to sizeof (grub_efi_uintn_t) but taking the mode into account*/
|
||||
int
|
||||
grub_efiemu_sizeof_uintn_t (void)
|
||||
{
|
||||
if (grub_efiemu_mode == GRUB_EFIEMU32)
|
||||
return 4;
|
||||
if (grub_efiemu_mode == GRUB_EFIEMU64)
|
||||
return 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check the header and set mode */
|
||||
static grub_err_t
|
||||
grub_efiemu_check_header (void *ehdr, grub_size_t size,
|
||||
grub_efiemu_mode_t *mode)
|
||||
{
|
||||
/* Check the magic numbers. */
|
||||
if ((*mode == GRUB_EFIEMU_NOTLOADED || *mode == GRUB_EFIEMU32)
|
||||
&& grub_efiemu_check_header32 (ehdr,size))
|
||||
{
|
||||
*mode = GRUB_EFIEMU32;
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
if ((*mode == GRUB_EFIEMU_NOTLOADED || *mode == GRUB_EFIEMU64)
|
||||
&& grub_efiemu_check_header64 (ehdr,size))
|
||||
{
|
||||
*mode = GRUB_EFIEMU64;
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
return grub_error (GRUB_ERR_BAD_OS, "invalid ELF magic");
|
||||
}
|
||||
|
||||
/* Unload segments */
|
||||
static int
|
||||
grub_efiemu_unload_segs (grub_efiemu_segment_t seg)
|
||||
{
|
||||
grub_efiemu_segment_t segn;
|
||||
for (; seg; seg = segn)
|
||||
{
|
||||
segn = seg->next;
|
||||
grub_efiemu_mm_return_request (seg->handle);
|
||||
grub_free (seg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
grub_err_t
|
||||
grub_efiemu_loadcore_unload(void)
|
||||
{
|
||||
switch (grub_efiemu_mode)
|
||||
{
|
||||
case GRUB_EFIEMU32:
|
||||
grub_efiemu_loadcore_unload32 ();
|
||||
break;
|
||||
|
||||
case GRUB_EFIEMU64:
|
||||
grub_efiemu_loadcore_unload64 ();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
grub_efiemu_mode = GRUB_EFIEMU_NOTLOADED;
|
||||
|
||||
grub_free (efiemu_core);
|
||||
efiemu_core = 0;
|
||||
|
||||
grub_efiemu_unload_segs (efiemu_segments);
|
||||
efiemu_segments = 0;
|
||||
|
||||
grub_efiemu_free_syms ();
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
/* Load runtime file and do some initial preparations */
|
||||
grub_err_t
|
||||
grub_efiemu_loadcore_init (grub_file_t file)
|
||||
{
|
||||
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");
|
||||
}
|
||||
}
|
345
efiemu/main.c
Normal file
345
efiemu/main.c
Normal file
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This is an emulation of EFI runtime services.
|
||||
This allows a more uniform boot on i386 machines.
|
||||
As it emulates only runtime service it isn't able
|
||||
to chainload EFI bootloader on non-EFI system. */
|
||||
|
||||
|
||||
#include <grub/file.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/machine/efiemu.h>
|
||||
#include <grub/command.h>
|
||||
|
||||
/* 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_autocore (void)
|
||||
{
|
||||
const char *prefix;
|
||||
char *filename;
|
||||
char *suffix;
|
||||
grub_err_t err;
|
||||
|
||||
if (grub_efiemu_sizeof_uintn_t () != 0)
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
prefix = grub_env_get ("prefix");
|
||||
|
||||
if (! prefix)
|
||||
return grub_error (GRUB_ERR_FILE_NOT_FOUND,
|
||||
"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
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_efiemu_prepare (void)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
grub_dprintf ("efiemu", "Preparing %d-bit efiemu\n",
|
||||
8 * grub_efiemu_sizeof_uintn_t ());
|
||||
|
||||
err = grub_efiemu_autocore ();
|
||||
|
||||
/* Create NVRAM if not yet done. */
|
||||
grub_efiemu_pnvram ();
|
||||
|
||||
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 ();
|
||||
}
|
635
efiemu/mm.c
Normal file
635
efiemu/mm.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*
|
||||
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 <grub/err.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/machine/memory.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
|
||||
struct grub_efiemu_memrequest
|
||||
{
|
||||
struct grub_efiemu_memrequest *next;
|
||||
grub_efi_memory_type_t type;
|
||||
grub_size_t size;
|
||||
grub_size_t align_overhead;
|
||||
int handle;
|
||||
void *val;
|
||||
};
|
||||
/* Linked list of requested memory. */
|
||||
static struct grub_efiemu_memrequest *memrequests = 0;
|
||||
/* Memory map. */
|
||||
static grub_efi_memory_descriptor_t *efiemu_mmap = 0;
|
||||
/* Pointer to allocated memory */
|
||||
static void *resident_memory = 0;
|
||||
/* Size of requested memory per type */
|
||||
static grub_size_t requested_memory[GRUB_EFI_MAX_MEMORY_TYPE];
|
||||
/* How many slots is allocated for memory_map and how many are already used */
|
||||
static int mmap_reserved_size = 0, mmap_num = 0;
|
||||
|
||||
/* Add a memory region to map*/
|
||||
static grub_err_t
|
||||
grub_efiemu_add_to_mmap (grub_uint64_t start, grub_uint64_t size,
|
||||
grub_efi_memory_type_t type)
|
||||
{
|
||||
grub_uint64_t page_start, npages;
|
||||
|
||||
/* Extend map if necessary*/
|
||||
if (mmap_num >= mmap_reserved_size)
|
||||
{
|
||||
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 ();
|
||||
}
|
402
efiemu/pnvram.c
Normal file
402
efiemu/pnvram.c
Normal file
|
@ -0,0 +1,402 @@
|
|||
/* Export pnvram and some variables for runtime */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/file.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/efiemu/runtime.h>
|
||||
#include <grub/extcmd.h>
|
||||
|
||||
/* Place for final location of variables */
|
||||
static int nvram_handle = 0;
|
||||
static int nvramsize_handle = 0;
|
||||
static int high_monotonic_count_handle = 0;
|
||||
static int timezone_handle = 0;
|
||||
static int accuracy_handle = 0;
|
||||
static int daylight_handle = 0;
|
||||
|
||||
/* 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_autocore ();
|
||||
if (err)
|
||||
{
|
||||
grub_free (nvram);
|
||||
return 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);
|
||||
}
|
127
efiemu/prepare.c
Normal file
127
efiemu/prepare.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/lib/crc.h>
|
||||
|
||||
grub_err_t
|
||||
SUFFIX (grub_efiemu_prepare) (struct grub_efiemu_prepare_hook *prepare_hooks,
|
||||
struct grub_efiemu_configuration_table
|
||||
*config_tables)
|
||||
{
|
||||
grub_err_t err;
|
||||
int conftable_handle;
|
||||
struct grub_efiemu_configuration_table *cur;
|
||||
struct grub_efiemu_prepare_hook *curhook;
|
||||
|
||||
int cntconftables = 0;
|
||||
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;
|
||||
}
|
23
efiemu/prepare32.c
Normal file
23
efiemu/prepare32.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define SUFFIX(x) x ## 32
|
||||
#define TYPE(x) x ## 32_t
|
||||
|
||||
#include "prepare.c"
|
23
efiemu/prepare64.c
Normal file
23
efiemu/prepare64.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define SUFFIX(x) x ## 64
|
||||
#define TYPE(x) x ## 64_t
|
||||
|
||||
#include "prepare.c"
|
34
efiemu/runtime/config.h
Normal file
34
efiemu/runtime/config.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
159
efiemu/runtime/efiemu.S
Normal file
159
efiemu/runtime/efiemu.S
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2006,2007,2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/symbol.h>
|
||||
|
||||
/*
|
||||
* x86_64 uses registry to pass parameters. Unfortunately, gcc and efi use
|
||||
* different call conversion, so we need to do some conversion.
|
||||
*
|
||||
* gcc:
|
||||
* %rdi, %rsi, %rdx, %rcx, %r8, %r9, 8(%rsp), 16(%rsp), ...
|
||||
*
|
||||
* efi:
|
||||
* %rcx, %rdx, %r8, %r9, 32(%rsp), 40(%rsp), 48(%rsp), ...
|
||||
*
|
||||
*/
|
||||
|
||||
.file "efiemu.S"
|
||||
.text
|
||||
|
||||
FUNCTION (efiemu_get_time)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
mov %rdx, %rsi
|
||||
call efiemu_get_time_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
FUNCTION (efiemu_set_time)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
call efiemu_set_time_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
|
||||
FUNCTION (efiemu_get_wakeup_time)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
mov %rdx, %rsi
|
||||
mov %r8, %rdx
|
||||
call efiemu_get_wakeup_time_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
FUNCTION (efiemu_set_wakeup_time)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
mov %rdx, %rsi
|
||||
call efiemu_set_wakeup_time_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
FUNCTION (efiemu_get_variable)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
mov %rdx, %rsi
|
||||
mov %r8, %rdx
|
||||
mov %r9, %rcx
|
||||
mov 56(%rsp), %r8
|
||||
call efiemu_get_variable_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
FUNCTION (efiemu_get_next_variable_name)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
mov %rdx, %rsi
|
||||
mov %r8, %rdx
|
||||
call efiemu_get_next_variable_name_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
FUNCTION (efiemu_set_variable)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
mov %rdx, %rsi
|
||||
mov %r8, %rdx
|
||||
mov %r9, %rcx
|
||||
mov 56(%rsp), %r8
|
||||
call efiemu_set_variable_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
FUNCTION (efiemu_get_next_high_monotonic_count)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
call efiemu_get_next_high_monotonic_count_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
FUNCTION (efiemu_reset_system)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
mov %rdx, %rsi
|
||||
mov %r8, %rdx
|
||||
mov %r9, %rcx
|
||||
call efiemu_reset_system_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
/* The following functions are always called in physical mode */
|
||||
.section ".text-physical", "ax"
|
||||
|
||||
FUNCTION (efiemu_set_virtual_address_map)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
mov %rdx, %rsi
|
||||
mov %r8, %rdx
|
||||
mov %r9, %rcx
|
||||
call efiemu_set_virtual_address_map_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
||||
FUNCTION (efiemu_convert_pointer)
|
||||
push %rdi
|
||||
push %rsi
|
||||
mov %rcx, %rdi
|
||||
mov %rdx, %rsi
|
||||
call efiemu_convert_pointer_real
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
ret
|
||||
|
623
efiemu/runtime/efiemu.c
Normal file
623
efiemu/runtime/efiemu.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This is an emulation of EFI runtime services.
|
||||
This allows a more uniform boot on i386 machines.
|
||||
As it emulates only runtime serviceit isn't able
|
||||
to chainload EFI bootloader on non-EFI system (TODO) */
|
||||
|
||||
#include <grub/symbol.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/efi/api.h>
|
||||
#include <grub/efiemu/runtime.h>
|
||||
|
||||
grub_efi_status_t
|
||||
efiemu_get_time (grub_efi_time_t *time,
|
||||
grub_efi_time_capabilities_t *capabilities);
|
||||
grub_efi_status_t
|
||||
efiemu_set_time (grub_efi_time_t *time);
|
||||
|
||||
grub_efi_status_t
|
||||
efiemu_get_wakeup_time (grub_efi_boolean_t *enabled,
|
||||
grub_efi_boolean_t *pending,
|
||||
grub_efi_time_t *time);
|
||||
grub_efi_status_t
|
||||
efiemu_set_wakeup_time (grub_efi_boolean_t enabled,
|
||||
grub_efi_time_t *time);
|
||||
|
||||
|
||||
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
|
||||
};
|
||||
|
4
efiemu/runtime/efiemu.sh
Normal file
4
efiemu/runtime/efiemu.sh
Normal file
|
@ -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
|
188
efiemu/symbols.c
Normal file
188
efiemu/symbols.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <grub/err.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/efiemu/efiemu.h>
|
||||
#include <grub/efiemu/runtime.h>
|
||||
|
||||
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;
|
||||
}
|
75
include/grub/autoefi.h
Normal file
75
include/grub/autoefi.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* 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 <grub/machine/machine.h>
|
||||
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
# include <grub/efi/efi.h>
|
||||
# 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 <grub/efiemu/efiemu.h>
|
||||
# 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
|
|
@ -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
|
||||
|
|
276
include/grub/efiemu/efiemu.h
Normal file
276
include/grub/efiemu/efiemu.h
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* GRUB is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GRUB is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GRUB_EFI_EMU_HEADER
|
||||
#define GRUB_EFI_EMU_HEADER 1
|
||||
|
||||
#include <grub/efi/api.h>
|
||||
#include <grub/file.h>
|
||||
|
||||
#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);
|
||||
grub_err_t grub_efiemu_autocore (void);
|
||||
#endif /* ! GRUB_EFI_EMU_HEADER */
|
37
include/grub/efiemu/runtime.h
Normal file
37
include/grub/efiemu/runtime.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 */
|
33
include/grub/i386/efiemu.h
Normal file
33
include/grub/i386/efiemu.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
24
include/grub/i386/pc/efiemu.h
Normal file
24
include/grub/i386/pc/efiemu.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GRUB_MACHINE_EFI_EMU_HEADER
|
||||
#define GRUB_MACHINE_EFI_EMU_HEADER 1
|
||||
|
||||
grub_err_t grub_machine_efiemu_init_tables (void);
|
||||
|
||||
#endif
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
${input_dir}/handler.lst ${input_dir}/parttool.lst \
|
||||
${aux_dir}/boot/grub/
|
||||
|
|
Loading…
Reference in a new issue