diff options
29 files changed, 2064 insertions, 23 deletions
diff --git a/xen/Makefile b/xen/Makefile index 19ada7ef54..ad52e7998e 100644 --- a/xen/Makefile +++ b/xen/Makefile @@ -12,6 +12,8 @@ export XEN_DOMAIN ?= $(shell ([ -x /bin/dnsdomainname ] && /bin/dnsdomainname) | export BASEDIR := $(CURDIR) export XEN_ROOT := $(BASEDIR)/.. +EFI_MOUNTPOINT ?= /boot/efi + .PHONY: default default: build @@ -33,6 +35,13 @@ _install: $(TARGET).gz ln -f -s $(notdir $(TARGET))-$(XEN_FULLVERSION).gz $(DESTDIR)/boot/$(notdir $(TARGET))-$(XEN_VERSION).gz ln -f -s $(notdir $(TARGET))-$(XEN_FULLVERSION).gz $(DESTDIR)/boot/$(notdir $(TARGET)).gz $(INSTALL_DATA) $(TARGET)-syms $(DESTDIR)/boot/$(notdir $(TARGET))-syms-$(XEN_FULLVERSION) + if [ -r $(TARGET).efi -a -n "$(EFI_MOUNTPOINT)" ]; then \ + if [ -n '$(EFI_VENDOR)' ]; then \ + $(INSTALL_DATA) $(TARGET).efi $(DESTDIR)$(EFI_MOUNTPOINT)/efi/$(EFI_VENDOR)/$(notdir $(TARGET))-$(XEN_FULLVERSION).efi; \ + elif [ "$(DESTDIR)" = "$(patsubst $(shell cd $(XEN_ROOT) && pwd)/%,%,$(DESTDIR))" ]; then \ + echo 'EFI installation not done (EFI_VENDOR not set)' >&2; \ + fi; \ + fi .PHONY: _debug _debug: diff --git a/xen/Rules.mk b/xen/Rules.mk index fedda9181b..59c7dd7577 100644 --- a/xen/Rules.mk +++ b/xen/Rules.mk @@ -158,7 +158,7 @@ _clean_%/: FORCE SPECIAL_DATA_SECTIONS := rodata $(foreach n,1 2 4 8,rodata.str1.$(n)) \ $(foreach r,rel rel.ro,data.$(r) data.$(r).local) -$(filter %.init.o,$(obj-y) $(obj-bin-y)): %.init.o: %.o Makefile +$(filter %.init.o,$(obj-y) $(obj-bin-y) $(extra-y)): %.init.o: %.o Makefile $(OBJDUMP) -h $< | sed -n '/[0-9]/{s,00*,0,g;p}' | while read idx name sz rest; do \ case "$$name" in \ .text|.text.*|.data|.data.*|.bss) \ diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile index 8691cd3248..466ecee5b8 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -62,28 +62,43 @@ obj-$(crash_debug) += gdbstub.o x86_emulate.o: x86_emulate/x86_emulate.c x86_emulate/x86_emulate.h -$(TARGET): $(TARGET)-syms boot/mkelf32 +efi-$(x86_64) := $(shell if [ ! -r $(BASEDIR)/include/xen/compile.h -o \ + -O $(BASEDIR)/include/xen/compile.h ]; then \ + echo '$(TARGET).efi'; fi) + +$(TARGET): $(TARGET)-syms $(efi-y) boot/mkelf32 ./boot/mkelf32 $(TARGET)-syms $(TARGET) 0x100000 \ `$(NM) -nr $(TARGET)-syms | head -n 1 | sed -e 's/^\([^ ]*\).*/0x\1/'` -ALL_OBJS := $(BASEDIR)/arch/x86/boot/built_in.o $(ALL_OBJS) +ALL_OBJS := $(BASEDIR)/arch/x86/boot/built_in.o $(BASEDIR)/arch/x86/efi/built_in.o $(ALL_OBJS) ifeq ($(lto),y) # Gather all LTO objects together prelink_lto.o: $(ALL_OBJS) $(LD_LTO) -r -o $@ $^ +prelink-efi_lto.o: $(ALL_OBJS) efi/runtime.o efi/compat.o + $(guard) $(LD_LTO) -r -o $@ $(filter-out %/efi/built_in.o,$^) + # Link it with all the binary objects prelink.o: $(patsubst %/built_in.o,%/built_in_bin.o,$(ALL_OBJS)) prelink_lto.o $(LD) $(LDFLAGS) -r -o $@ $^ + +prelink-efi.o: $(patsubst %/built_in.o,%/built_in_bin.o,$(ALL_OBJS)) prelink-efi_lto.o efi/boot.init.o + $(guard) $(LD) $(LDFLAGS) -r -o $@ $^ else prelink.o: $(ALL_OBJS) $(LD) $(LDFLAGS) -r -o $@ $^ + +prelink-efi.o: $(ALL_OBJS) efi/boot.init.o efi/runtime.o efi/compat.o + $(guard) $(LD) $(LDFLAGS) -r -o $@ $(filter-out %/efi/built_in.o,$^) endif -$(TARGET)-syms: prelink.o xen.lds - $(MAKE) -f $(BASEDIR)/Rules.mk $(BASEDIR)/common/symbols-dummy.o +$(BASEDIR)/common/symbols-dummy.o: + $(MAKE) -f $(BASEDIR)/Rules.mk -C $(BASEDIR)/common symbols-dummy.o + +$(TARGET)-syms: prelink.o xen.lds $(BASEDIR)/common/symbols-dummy.o $(LD) $(LDFLAGS) -T xen.lds -N prelink.o \ $(BASEDIR)/common/symbols-dummy.o -o $(@D)/.$(@F).0 $(NM) -n $(@D)/.$(@F).0 | $(BASEDIR)/tools/symbols >$(@D)/.$(@F).0.S @@ -96,6 +111,39 @@ $(TARGET)-syms: prelink.o xen.lds $(@D)/.$(@F).1.o -o $@ rm -f $(@D)/.$(@F).[0-9]* +EFI_LDFLAGS = $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 +EFI_LDFLAGS += --image-base=$(1) --stack=0,0 --heap=0,0 --strip-debug +EFI_LDFLAGS += --section-alignment=0x200000 --file-alignment=0x20 +EFI_LDFLAGS += --major-image-version=$(XEN_VERSION) +EFI_LDFLAGS += --minor-image-version=$(XEN_SUBVERSION) +EFI_LDFLAGS += --major-os-version=2 --minor-os-version=0 +EFI_LDFLAGS += --major-subsystem-version=2 --minor-subsystem-version=0 + +$(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p') +$(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p') +# Don't use $(wildcard ...) here - at least make 3.80 expands this too early! +$(TARGET).efi: guard = $(if $(shell echo efi/dis* | grep disabled),:) +$(TARGET).efi: prelink-efi.o efi.lds efi/relocs-dummy.o $(BASEDIR)/common/symbols-dummy.o efi/mkreloc + $(foreach base, $(VIRT_BASE) $(ALT_BASE), \ + $(guard) $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< efi/relocs-dummy.o \ + $(BASEDIR)/common/symbols-dummy.o -o $(@D)/.$(@F).$(base).0 &&) : + $(guard) efi/mkreloc $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).0) >$(@D)/.$(@F).0r.S + $(guard) $(NM) -n $(@D)/.$(@F).$(VIRT_BASE).0 | $(guard) $(BASEDIR)/tools/symbols >$(@D)/.$(@F).0s.S + $(guard) $(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).0r.o $(@D)/.$(@F).0s.o + $(foreach base, $(VIRT_BASE) $(ALT_BASE), \ + $(guard) $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< \ + $(@D)/.$(@F).0r.o $(@D)/.$(@F).0s.o -o $(@D)/.$(@F).$(base).1 &&) : + $(guard) efi/mkreloc $(foreach base,$(VIRT_BASE) $(ALT_BASE),$(@D)/.$(@F).$(base).1) >$(@D)/.$(@F).1r.S + $(guard) $(NM) -n $(@D)/.$(@F).$(VIRT_BASE).1 | $(guard) $(BASEDIR)/tools/symbols >$(@D)/.$(@F).1s.S + $(guard) $(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).1r.o $(@D)/.$(@F).1s.o + $(guard) $(LD) $(call EFI_LDFLAGS,$(VIRT_BASE)) -T efi.lds -N $< \ + $(@D)/.$(@F).1r.o $(@D)/.$(@F).1s.o -o $@ + if $(guard) false; then rm -f $@; echo 'EFI support disabled'; fi + rm -f $(@D)/.$(@F).[0-9]* + +efi/boot.init.o efi/runtime.o efi/compat.o: $(BASEDIR)/arch/x86/efi/built_in.o +efi/boot.init.o efi/runtime.o efi/compat.o: ; + asm-offsets.s: $(TARGET_SUBARCH)/asm-offsets.c $(CC) $(filter-out -flto,$(CFLAGS)) -S -o $@ $< @@ -104,11 +152,20 @@ xen.lds: xen.lds.S sed -e 's/xen\.lds\.o:/xen\.lds:/g' <.xen.lds.d >.xen.lds.d.new mv -f .xen.lds.d.new .xen.lds.d +efi.lds: xen.lds.S + $(CC) -P -E -Ui386 -DEFI $(AFLAGS) -o $@ $< + sed -e 's/efi\.lds\.o:/efi\.lds:/g' <.$(@F).d >.$(@F).d.new + mv -f .$(@F).d.new .$(@F).d + boot/mkelf32: boot/mkelf32.c $(HOSTCC) $(HOSTCFLAGS) -o $@ $< +efi/mkreloc: efi/mkreloc.c + $(HOSTCC) $(HOSTCFLAGS) -g -o $@ $< + .PHONY: clean clean:: rm -f asm-offsets.s xen.lds boot/*.o boot/*~ boot/core boot/mkelf32 rm -f $(BASEDIR)/.xen-syms.[0-9]* boot/.*.d + rm -f $(BASEDIR)/.xen.efi.[0-9]* efi/*.o efi/mkreloc rm -f boot/reloc.S boot/reloc.lnk boot/reloc.bin diff --git a/xen/arch/x86/boot/trampoline.S b/xen/arch/x86/boot/trampoline.S index ad15f5c4e8..e5b4dbef45 100644 --- a/xen/arch/x86/boot/trampoline.S +++ b/xen/arch/x86/boot/trampoline.S @@ -38,6 +38,7 @@ trampoline_gdt: .long 0x0000ffff | ((BOOT_TRAMPOLINE & 0x00ffff) << 16) .long 0x00009200 | ((BOOT_TRAMPOLINE & 0xff0000) >> 16) + .globl cpuid_ext_features cpuid_ext_features: .long 0 diff --git a/xen/arch/x86/boot/x86_64.S b/xen/arch/x86/boot/x86_64.S index edf7891610..3c0958bcf2 100644 --- a/xen/arch/x86/boot/x86_64.S +++ b/xen/arch/x86/boot/x86_64.S @@ -84,11 +84,13 @@ multiboot_ptr: .long 0 .word 0 + .globl gdt_descr gdt_descr: .word LAST_RESERVED_GDT_BYTE .quad boot_cpu_gdt_table - FIRST_RESERVED_GDT_BYTE .word 0,0,0 + .globl idt_descr idt_descr: .word 256*16-1 .quad idt_table diff --git a/xen/arch/x86/dmi_scan.c b/xen/arch/x86/dmi_scan.c index f73a9a4c52..75b3507ac9 100644 --- a/xen/arch/x86/dmi_scan.c +++ b/xen/arch/x86/dmi_scan.c @@ -9,6 +9,7 @@ #include <asm/io.h> #include <asm/system.h> #include <xen/dmi.h> +#include <xen/efi.h> #define bt_ioremap(b,l) ((void *)__acpi_map_table(b,l)) #define bt_iounmap(b,l) ((void)0) @@ -122,11 +123,39 @@ static inline bool_t __init dmi_checksum(const void __iomem *buf, return sum == 0; } +static u32 __initdata efi_dmi_address; +static u32 __initdata efi_dmi_size; + +/* + * Important: This function gets called while still in EFI + * (pseudo-)physical mode. + */ +void __init dmi_efi_get_table(void *smbios) +{ + struct smbios_eps *eps = smbios; + + if (memcmp(eps->anchor, "_SM_", 4) && + dmi_checksum(eps, eps->length) && + memcmp(eps->dmi.anchor, "_DMI_", 5) == 0 && + dmi_checksum(&eps->dmi, sizeof(eps->dmi))) { + efi_dmi_address = eps->dmi.address; + efi_dmi_size = eps->dmi.size; + } +} + int __init dmi_get_table(u32 *base, u32 *len) { struct dmi_eps eps; char __iomem *p, *q; + if (efi_enabled) { + if (!efi_dmi_size) + return -1; + *base = efi_dmi_address; + *len = efi_dmi_size; + return 0; + } + p = maddr_to_virt(0xF0000); for (q = p; q < p + 0x10000; q += 16) { memcpy_fromio(&eps, q, 15); @@ -178,6 +207,39 @@ static int __init dmi_iterate(void (*decode)(struct dmi_header *)) return -1; } +static int __init dmi_efi_iterate(void (*decode)(struct dmi_header *)) +{ + struct smbios_eps eps; + const struct smbios_eps __iomem *p; + int ret = -1; + + if (efi.smbios == EFI_INVALID_TABLE_ADDR) + return -1; + + p = bt_ioremap(efi.smbios, sizeof(eps)); + if (!p) + return -1; + memcpy_fromio(&eps, p, sizeof(eps)); + bt_iounmap(p, sizeof(eps)); + + if (memcmp(eps.anchor, "_SM_", 4)) + return -1; + + p = bt_ioremap(efi.smbios, eps.length); + if (!p) + return -1; + if (dmi_checksum(p, eps.length) && + memcmp(eps.dmi.anchor, "_DMI_", 5) == 0 && + dmi_checksum(&eps.dmi, sizeof(eps.dmi))) { + printk(KERN_INFO "SMBIOS %d.%d present.\n", + eps.major, eps.minor); + ret = _dmi_iterate(&eps.dmi, p, decode); + } + bt_iounmap(p, eps.length); + + return ret; +} + static char *__initdata dmi_ident[DMI_STRING_MAX]; /* @@ -418,8 +480,8 @@ static void __init dmi_decode(struct dmi_header *dm) void __init dmi_scan_machine(void) { - int err = dmi_iterate(dmi_decode); - if(err == 0) + if ((!efi_enabled ? dmi_iterate(dmi_decode) : + dmi_efi_iterate(dmi_decode)) == 0) dmi_check_system(dmi_blacklist); else printk(KERN_INFO "DMI not present.\n"); diff --git a/xen/arch/x86/efi/Makefile b/xen/arch/x86/efi/Makefile new file mode 100644 index 0000000000..6e3f94c208 --- /dev/null +++ b/xen/arch/x86/efi/Makefile @@ -0,0 +1,17 @@ +CFLAGS += -fshort-wchar -mno-sse + +obj-y += stub.o + +create = test -e $(1) || touch -t 199901010000 $(1) + +efi := $(filter y,$(x86_64)$(shell rm -f disabled)) +efi := $(if $(efi),$(shell $(CC) -c -Werror check.c 2>disabled && echo y)) +efi := $(if $(efi),$(shell $(LD) -mi386pep --subsystem=10 -o check.efi check.o 2>disabled && echo y)) +efi := $(if $(efi),$(shell rm disabled)y,$(shell $(call create,boot.init.o); $(call create,runtime.o))) + +extra-$(efi) += boot.init.o relocs-dummy.o runtime.o compat.o + +stub.o: $(extra-y) + +clean:: + rm -f disabled *.efi diff --git a/xen/arch/x86/efi/boot.c b/xen/arch/x86/efi/boot.c new file mode 100644 index 0000000000..50ea3c8ee3 --- /dev/null +++ b/xen/arch/x86/efi/boot.c @@ -0,0 +1,1221 @@ +#include "efi.h" +#include <efi/efiprot.h> +#include <public/xen.h> +#include <xen/compile.h> +#include <xen/ctype.h> +#include <xen/dmi.h> +#include <xen/init.h> +#include <xen/keyhandler.h> +#include <xen/lib.h> +#include <xen/multiboot.h> +#include <xen/pfn.h> +#if EFI_PAGE_SIZE != PAGE_SIZE +# error Cannot use xen/pfn.h here! +#endif +#include <xen/string.h> +#include <xen/stringify.h> +#include <xen/vga.h> +#include <asm/e820.h> +#include <asm/msr.h> +#include <asm/processor.h> + +extern char start[]; +extern u32 cpuid_ext_features; + +union string { + CHAR16 *w; + char *s; + const char *cs; +}; + +struct file { + UINTN size; + union { + EFI_PHYSICAL_ADDRESS addr; + void *ptr; + }; +}; + +static EFI_BOOT_SERVICES *__initdata efi_bs; +static EFI_HANDLE __initdata efi_ih; + +static SIMPLE_TEXT_OUTPUT_INTERFACE __initdata *StdOut; +static SIMPLE_TEXT_OUTPUT_INTERFACE __initdata *StdErr; + +static UINT32 __initdata mdesc_ver; + +static struct file __initdata cfg; +static struct file __initdata kernel; +static struct file __initdata ramdisk; +static struct file __initdata xsm; + +static multiboot_info_t __initdata mbi = { + .flags = MBI_MODULES | MBI_LOADERNAME +}; +static module_t __initdata mb_modules[3]; + +static CHAR16 __initdata newline[] = L"\r\n"; + +#define PrintStr(s) StdOut->OutputString(StdOut, s) +#define PrintErr(s) StdErr->OutputString(StdErr, s) + +static CHAR16 *__init FormatDec(UINT64 Val, CHAR16 *Buffer) +{ + if ( Val >= 10 ) + Buffer = FormatDec(Val / 10, Buffer); + *Buffer = (CHAR16)(L'0' + Val % 10); + return Buffer + 1; +} + +static CHAR16 *__init FormatHex(UINT64 Val, UINTN Width, CHAR16 *Buffer) +{ + if ( Width > 1 || Val >= 0x10 ) + Buffer = FormatHex(Val >> 4, Width ? Width - 1 : 0, Buffer); + *Buffer = (CHAR16)((Val &= 0xf) < 10 ? L'0' + Val : L'a' + Val - 10); + return Buffer + 1; +} + +static void __init DisplayUint(UINT64 Val, INTN Width) +{ + CHAR16 PrintString[32], *end; + + if (Width < 0) + end = FormatDec(Val, PrintString); + else + { + PrintStr(L"0x"); + end = FormatHex(Val, Width, PrintString); + } + *end = 0; + PrintStr(PrintString); +} + +static CHAR16 *__init wstrcpy(CHAR16 *d, const CHAR16 *s) +{ + CHAR16 *r = d; + + while ( (*d++ = *s++) != 0 ) + ; + return r; +} + +static int __init wstrcmp(const CHAR16 *s1, const CHAR16 *s2) +{ + while ( *s1 && *s1 == *s2 ) + { + ++s1; + ++s2; + } + return *s1 - *s2; +} + +static int __init wstrncmp(const CHAR16 *s1, const CHAR16 *s2, UINTN n) +{ + while ( n && *s1 && *s1 == *s2 ) + { + --n; + ++s1; + ++s2; + } + return n ? *s1 - *s2 : 0; +} + +static CHAR16 *__init s2w(union string *str) +{ + const char *s = str->s; + CHAR16 *w; + void *ptr; + + if ( efi_bs->AllocatePool(EfiLoaderData, (strlen(s) + 1) * sizeof(*w), + &ptr) != EFI_SUCCESS ) + return NULL; + + w = str->w = ptr; + do { + *w = *s++; + } while ( *w++ ); + + return str->w; +} + +static char *__init w2s(const union string *str) +{ + const CHAR16 *w = str->w; + char *s = str->s; + + do { + if ( *w > 0x007f ) + return NULL; + *s = *w++; + } while ( *s++ ); + + return str->s; +} + +static bool_t __init match_guid(const EFI_GUID *guid1, const EFI_GUID *guid2) +{ + return guid1->Data1 == guid2->Data1 && + guid1->Data2 == guid2->Data2 && + guid1->Data3 == guid2->Data3 && + !memcmp(guid1->Data4, guid2->Data4, sizeof(guid1->Data4)); +} + +static void __init __attribute__((__noreturn__)) blexit(const CHAR16 *str) +{ + if ( str ) + PrintStr((CHAR16 *)str); + PrintStr(newline); + + if ( cfg.addr ) + efi_bs->FreePages(cfg.addr, PFN_UP(cfg.size)); + if ( kernel.addr ) + efi_bs->FreePages(kernel.addr, PFN_UP(kernel.size)); + if ( ramdisk.addr ) + efi_bs->FreePages(ramdisk.addr, PFN_UP(ramdisk.size)); + if ( xsm.addr ) + efi_bs->FreePages(xsm.addr, PFN_UP(xsm.size)); + + efi_bs->Exit(efi_ih, EFI_SUCCESS, 0, NULL); + for( ; ; ); /* not reached */ +} + +/* generic routine for printing error messages */ +static void __init PrintErrMesg(const CHAR16 *mesg, EFI_STATUS ErrCode) +{ + StdOut = StdErr; + PrintErr((CHAR16 *)mesg); + PrintErr(L": "); + + switch (ErrCode) + { + case EFI_NOT_FOUND: + mesg = L"Not found"; + break; + case EFI_NO_MEDIA: + mesg = L"The device has no media"; + break; + case EFI_MEDIA_CHANGED: + mesg = L"Media changed"; + break; + case EFI_DEVICE_ERROR: + mesg = L"Device error"; + break; + case EFI_VOLUME_CORRUPTED: + mesg = L"Volume corrupted"; + break; + case EFI_ACCESS_DENIED: + mesg = L"Access denied"; + break; + case EFI_OUT_OF_RESOURCES: + mesg = L"Out of resources"; + break; + case EFI_VOLUME_FULL: + mesg = L"Volume is full"; + break; + default: + PrintErr(L"ErrCode: "); + DisplayUint(ErrCode, 0); + mesg = NULL; + break; + } + blexit(mesg); +} + +static void __init place_string(u32 *addr, const char *s) +{ + static char *__initdata alloc = start; + + if ( s && *s ) + { + size_t len1 = strlen(s) + 1; + const char *old = (char *)(long)*addr; + size_t len2 = *addr ? strlen(old) + 1 : 0; + + alloc -= len1 + len2; + /* + * Insert new string before already existing one. This is needed + * for options passed on the command line to override options from + * the configuration file. + */ + memcpy(alloc, s, len1); + if ( *addr ) + { + alloc[len1 - 1] = ' '; + memcpy(alloc + len1, old, len2); + } + } + *addr = (long)alloc; +} + +static unsigned int __init get_argv(unsigned int argc, CHAR16 **argv, + CHAR16 *cmdline, UINTN cmdsize) +{ + CHAR16 *ptr = (CHAR16 *)(argv + argc + 1), *prev = NULL; + bool_t prev_sep = TRUE; + + for ( ; cmdsize > sizeof(*cmdline) && *cmdline; + cmdsize -= sizeof(*cmdline), ++cmdline ) + { + bool_t cur_sep = *cmdline == L' ' || *cmdline == L'\t'; + + if ( !prev_sep ) + { + if ( cur_sep ) + ++ptr; + else if ( argv ) + { + *ptr = *cmdline; + *++ptr = 0; + } + } + else if ( !cur_sep ) + { + if ( !argv ) + ++argc; + else if ( prev && wstrcmp(prev, L"--") == 0 ) + { + union string rest = { .w = cmdline }; + + --argv; + place_string(&mbi.cmdline, w2s(&rest)); + break; + } + else + { + *argv++ = prev = ptr; + *ptr = *cmdline; + *++ptr = 0; + } + } + prev_sep = cur_sep; + } + if ( argv ) + *argv = NULL; + return argc; +} + +static EFI_FILE_HANDLE __init get_parent_handle(EFI_LOADED_IMAGE *loaded_image, + CHAR16 **leaf) +{ + static EFI_GUID __initdata fs_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL; + EFI_FILE_HANDLE dir_handle; + EFI_DEVICE_PATH *dp; + CHAR16 *pathend, *ptr; + EFI_STATUS ret; + + do { + EFI_FILE_IO_INTERFACE *fio; + + /* Get the file system interface. */ + ret = efi_bs->HandleProtocol(loaded_image->DeviceHandle, + &fs_protocol, (void **)&fio); + if ( EFI_ERROR(ret) ) + blexit(L"Couldn't obtain the File System Protocol Interface"); + ret = fio->OpenVolume(fio, &dir_handle); + } while ( ret == EFI_MEDIA_CHANGED ); + if ( ret != EFI_SUCCESS ) + blexit(L"OpenVolume failure"); + +#define buffer ((CHAR16 *)keyhandler_scratch) +#define BUFFERSIZE sizeof(keyhandler_scratch) + for ( dp = loaded_image->FilePath, *buffer = 0; + DevicePathType(dp) != END_DEVICE_PATH_TYPE; + dp = (void *)dp + DevicePathNodeLength(dp) ) + { + FILEPATH_DEVICE_PATH *fp; + + if ( DevicePathType(dp) != MEDIA_DEVICE_PATH || + DevicePathSubType(dp) != MEDIA_FILEPATH_DP ) + blexit(L"Unsupported device path component"); + + if ( *buffer ) + { + EFI_FILE_HANDLE new_handle; + + ret = dir_handle->Open(dir_handle, &new_handle, buffer, + EFI_FILE_MODE_READ, 0); + if ( ret != EFI_SUCCESS ) + { + PrintErr(L"Open failed for "); + PrintErrMesg(buffer, ret); + } + dir_handle->Close(dir_handle); + dir_handle = new_handle; + } + fp = (void *)dp; + if ( BUFFERSIZE < DevicePathNodeLength(dp) - + sizeof(*dp) + sizeof(*buffer) ) + blexit(L"Increase BUFFERSIZE"); + memcpy(buffer, fp->PathName, DevicePathNodeLength(dp) - sizeof(*dp)); + buffer[(DevicePathNodeLength(dp) - sizeof(*dp)) / sizeof(*buffer)] = 0; + } + for ( ptr = buffer, pathend = NULL; *ptr; ++ptr ) + if ( *ptr == L'\\' ) + pathend = ptr; + if ( pathend ) + { + *pathend = 0; + *leaf = pathend + 1; + if ( *buffer ) + { + EFI_FILE_HANDLE new_handle; + + ret = dir_handle->Open(dir_handle, &new_handle, buffer, + EFI_FILE_MODE_READ, 0); + if ( ret != EFI_SUCCESS ) { + PrintErr(L"Open failed for "); + PrintErrMesg(buffer, ret); + } + dir_handle->Close(dir_handle); + dir_handle = new_handle; + } + } + else + *leaf = buffer; +#undef BUFFERSIZE +#undef buffer + + return dir_handle; +} + +static CHAR16 *__init point_tail(CHAR16 *fn) +{ + CHAR16 *tail = NULL; + + for ( ; ; ++fn ) + switch ( *fn ) + { + case 0: + return tail; + case L'.': + case L'-': + case L'_': + tail = fn; + break; + } +} + +static bool_t __init read_file(EFI_FILE_HANDLE dir_handle, CHAR16 *name, + struct file *file) +{ + EFI_FILE_HANDLE FileHandle = NULL; + UINT64 size; + EFI_STATUS ret; + CHAR16 *what = NULL; + + if ( !name ) + PrintErrMesg(L"No filename", EFI_OUT_OF_RESOURCES); + ret = dir_handle->Open(dir_handle, &FileHandle, name, + EFI_FILE_MODE_READ, 0); + if ( file == &cfg && ret == EFI_NOT_FOUND ) + return 0; + if ( EFI_ERROR(ret) ) + what = L"Open"; + else + ret = FileHandle->SetPosition(FileHandle, -1); + if ( EFI_ERROR(ret) ) + what = what ?: L"Seek"; + else + ret = FileHandle->GetPosition(FileHandle, &size); + if ( EFI_ERROR(ret) ) + what = what ?: L"Get size"; + else + ret = FileHandle->SetPosition(FileHandle, 0); + if ( EFI_ERROR(ret) ) + what = what ?: L"Seek"; + else + { + file->addr = (EFI_PHYSICAL_ADDRESS)1 << (32 + PAGE_SHIFT); + ret = efi_bs->AllocatePages(AllocateMaxAddress, EfiLoaderData, + PFN_UP(size), &file->addr); + } + if ( EFI_ERROR(ret) ) + { + file->addr = 0; + what = what ?: L"Allocation"; + } + else + { + if ( file != &cfg ) + { + PrintStr(name); + PrintStr(L": "); + DisplayUint(file->addr, 2 * sizeof(file->addr)); + PrintStr(L"-"); + DisplayUint(file->addr + size, 2 * sizeof(file->addr)); + PrintStr(newline); + mb_modules[mbi.mods_count].mod_start = file->addr >> PAGE_SHIFT; + mb_modules[mbi.mods_count].mod_end = size; + ++mbi.mods_count; + } + + file->size = size; + ret = FileHandle->Read(FileHandle, &file->size, file->ptr); + if ( !EFI_ERROR(ret) && file->size != size ) + ret = EFI_ABORTED; + if ( EFI_ERROR(ret) ) + what = L"Read"; + } + + if ( FileHandle ) + FileHandle->Close(FileHandle); + + if ( what ) + { + PrintErr(what); + PrintErr(L" failed for "); + PrintErrMesg(name, ret); + } + + return 1; +} + +static void __init pre_parse(const struct file *cfg) +{ + char *ptr = cfg->ptr, *end = ptr + cfg->size; + bool_t start = 1, comment = 0; + + for ( ; ptr < end; ++ptr ) + { + if ( iscntrl(*ptr) ) + { + comment = 0; + start = 1; + *ptr = 0; + } + else if ( comment || (start && isspace(*ptr)) ) + *ptr = 0; + else if ( *ptr == '#' || (start && *ptr == ';') ) + { + comment = 1; + *ptr = 0; + } + else + start = 0; + } + if ( cfg->size && end[-1] ) + PrintStr(L"No newline at end of config file," + " last line will be ignored.\r\n"); +} + +static char *__init get_value(const struct file *cfg, const char *section, + const char *item) +{ + char *ptr = cfg->ptr, *end = ptr + cfg->size; + size_t slen = section ? strlen(section) : 0, ilen = strlen(item); + bool_t match = !slen; + + for ( ; ptr < end; ++ptr ) + { + switch ( *ptr ) + { + case 0: + continue; + case '[': + if ( !slen ) + break; + if ( match ) + return NULL; + match = strncmp(++ptr, section, slen) == 0 && ptr[slen] == ']'; + break; + default: + if ( match && strncmp(ptr, item, ilen) == 0 && ptr[ilen] == '=' ) + return ptr + ilen + 1; + break; + } + ptr += strlen(ptr); + } + return NULL; +} + +static void __init split_value(char *s) +{ + while ( *s && isspace(*s) ) + ++s; + place_string(&mb_modules[mbi.mods_count].string, s); + while ( *s && !isspace(*s) ) + ++s; + *s = 0; +} + +static int __init set_color(u32 mask, int bpp, u8 *pos, u8 *sz) +{ + if ( bpp < 0 ) + return bpp; + if ( !mask ) + return -EINVAL; + for ( *pos = 0; !(mask & 1); ++*pos ) + mask >>= 1; + for ( *sz = 0; mask & 1; ++sz) + mask >>= 1; + if ( mask ) + return -EINVAL; + return max(*pos + *sz, bpp); +} + +#define PE_BASE_RELOC_ABS 0 +#define PE_BASE_RELOC_HIGHLOW 3 +#define PE_BASE_RELOC_DIR64 10 + +extern const struct pe_base_relocs { + u32 rva; + u32 size; + u16 entries[]; +} __base_relocs_start[], __base_relocs_end[]; + +static void __init relocate_image(unsigned long delta) +{ + const struct pe_base_relocs *base_relocs; + + for ( base_relocs = __base_relocs_start; base_relocs < __base_relocs_end; ) + { + unsigned int i, n; + + n = (base_relocs->size - sizeof(*base_relocs)) / + sizeof(*base_relocs->entries); + for ( i = 0; i < n; ++i ) + { + unsigned long addr = xen_phys_start + base_relocs->rva + + (base_relocs->entries[i] & 0xfff); + + switch ( base_relocs->entries[i] >> 12 ) + { + case PE_BASE_RELOC_ABS: + break; + case PE_BASE_RELOC_HIGHLOW: + if ( delta ) + *(u32 *)addr += delta; + break; + case PE_BASE_RELOC_DIR64: + if ( delta ) + *(u64 *)addr += delta; + break; + default: + blexit(L"Unsupported relocation type\r\n"); + } + } + base_relocs = (const void *)(base_relocs->entries + i + (i & 1)); + } +} + +void EFIAPI __init __attribute__((__noreturn__)) +efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) +{ + static EFI_GUID __initdata loaded_image_guid = LOADED_IMAGE_PROTOCOL; + static EFI_GUID __initdata gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + EFI_LOADED_IMAGE *loaded_image; + EFI_STATUS status; + unsigned int i, argc; + CHAR16 **argv, *file_name, *cfg_file_name = NULL; + UINTN cols, rows, depth, size, map_key, info_size, gop_mode = ~0; + EFI_HANDLE *handles = NULL; + EFI_GRAPHICS_OUTPUT_PROTOCOL *gop = NULL; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info; + EFI_FILE_HANDLE dir_handle; + union string section = { NULL }, name; + struct e820entry *e; + u64 efer; + bool_t base_video = 0, trampoline_okay = 0; + + efi_ih = ImageHandle; + efi_bs = SystemTable->BootServices; + efi_rs = SystemTable->RuntimeServices; + efi_ct = SystemTable->ConfigurationTable; + efi_num_ct = SystemTable->NumberOfTableEntries; + efi_version = SystemTable->Hdr.Revision; + efi_fw_vendor = SystemTable->FirmwareVendor; + efi_fw_revision = SystemTable->FirmwareRevision; + + StdOut = SystemTable->ConOut; + StdErr = SystemTable->StdErr ?: StdOut; + + status = efi_bs->HandleProtocol(ImageHandle, &loaded_image_guid, + (void **)&loaded_image); + if ( status != EFI_SUCCESS ) + PrintErrMesg(L"No Loaded Image Protocol", status); + + xen_phys_start = (UINTN)loaded_image->ImageBase; + if ( (xen_phys_start + loaded_image->ImageSize - 1) >> 32 ) + blexit(L"Xen must be loaded below 4Gb.\r\n"); + if ( xen_phys_start & ((1 << L2_PAGETABLE_SHIFT) - 1) ) + blexit(L"Xen must be loaded at a 2Mb boundary.\r\n"); + trampoline_xen_phys_start = xen_phys_start; + + /* Get the file system interface. */ + dir_handle = get_parent_handle(loaded_image, &file_name); + + argc = get_argv(0, NULL, loaded_image->LoadOptions, + loaded_image->LoadOptionsSize); + if ( argc > 0 && + efi_bs->AllocatePool(EfiLoaderData, + (argc + 1) * sizeof(*argv) + + loaded_image->LoadOptionsSize, + (void **)&argv) == EFI_SUCCESS ) + get_argv(argc, argv, loaded_image->LoadOptions, + loaded_image->LoadOptionsSize); + else + argc = 0; + for ( i = 1; i < argc; ++i ) + { + CHAR16 *ptr = argv[i]; + + if ( !ptr ) + break; + if ( *ptr == L'/' || *ptr == L'-' ) + { + if ( wstrcmp(ptr + 1, L"basevideo") == 0 ) + base_video = 1; + else if ( wstrncmp(ptr + 1, L"cfg=", 4) == 0 ) + cfg_file_name = ptr + 5; + else if ( i + 1 < argc && wstrcmp(ptr + 1, L"cfg") == 0 ) + cfg_file_name = argv[++i]; + else if ( wstrcmp(ptr + 1, L"help") == 0 || + (ptr[1] == L'?' && !ptr[2]) ) + { + PrintStr(L"Xen EFI Loader options:\r\n"); + PrintStr(L"-basevideo retain current video mode\r\n"); + PrintStr(L"-cfg=<file> specify configuration file\r\n"); + PrintStr(L"-help, -? display this help\r\n"); + blexit(NULL); + } + else + { + PrintStr(L"WARNING: Unknown command line option '"); + PrintStr(ptr); + PrintStr(L"' ignored\r\n"); + } + } + else + section.w = ptr; + } + + if ( !base_video ) + { + unsigned int best; + + for ( i = 0, size = 0, best = StdOut->Mode->Mode; + i < StdOut->Mode->MaxMode; ++i ) + { + if ( StdOut->QueryMode(StdOut, i, &cols, &rows) == EFI_SUCCESS && + cols * rows > size ) + { + size = cols * rows; + best = i; + } + } + if ( best != StdOut->Mode->Mode ) + StdOut->SetMode(StdOut, best); + } + + PrintStr(L"Xen " __stringify(XEN_VERSION) "." __stringify(XEN_SUBVERSION) + XEN_EXTRAVERSION " (c/s " XEN_CHANGESET ") EFI loader\r\n"); + + relocate_image(0); + + if ( StdOut->QueryMode(StdOut, StdOut->Mode->Mode, + &cols, &rows) == EFI_SUCCESS ) + { + vga_console_info.video_type = XEN_VGATYPE_TEXT_MODE_3; + vga_console_info.u.text_mode_3.columns = cols; + vga_console_info.u.text_mode_3.rows = rows; + vga_console_info.u.text_mode_3.font_height = 16; + } + + size = 0; + status = efi_bs->LocateHandle(ByProtocol, &gop_guid, NULL, &size, NULL); + if ( status == EFI_BUFFER_TOO_SMALL ) + status = efi_bs->AllocatePool(EfiLoaderData, size, (void **)&handles); + if ( !EFI_ERROR(status) ) + status = efi_bs->LocateHandle(ByProtocol, &gop_guid, NULL, &size, + handles); + if ( EFI_ERROR(status) ) + size = 0; + for ( i = 0; i < size / sizeof(*handles); ++i ) + { + status = efi_bs->HandleProtocol(handles[i], &gop_guid, (void **)&gop); + if ( EFI_ERROR(status) ) + continue; + status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &mode_info); + if ( !EFI_ERROR(status) ) + break; + } + if ( handles ) + efi_bs->FreePool(handles); + if ( EFI_ERROR(status) ) + gop = NULL; + + /* Read and parse the config file. */ + if ( !cfg_file_name ) + { + CHAR16 *tail; + + while ( (tail = point_tail(file_name)) != NULL ) + { + wstrcpy(tail, L".cfg"); + if ( read_file(dir_handle, file_name, &cfg) ) + break; + *tail = 0; + } + if ( !tail ) + blexit(L"No configuration file found\r\n"); + PrintStr(L"Using configuration file '"); + PrintStr(file_name); + PrintStr(L"'\r\n"); + } + else if ( !read_file(dir_handle, cfg_file_name, &cfg) ) + blexit(L"Configuration file not found\r\n"); + pre_parse(&cfg); + + if ( section.w ) + w2s(§ion); + else + section.s = get_value(&cfg, "global", "default"); + + name.s = get_value(&cfg, section.s, "kernel"); + if ( !name.s ) + blexit(L"No Dom0 kernel image specified\r\n"); + split_value(name.s); + read_file(dir_handle, s2w(&name), &kernel); + efi_bs->FreePool(name.w); + + name.s = get_value(&cfg, section.s, "ramdisk"); + if ( name.s ) + { + split_value(name.s); + read_file(dir_handle, s2w(&name), &ramdisk); + efi_bs->FreePool(name.w); + } + + name.s = get_value(&cfg, section.s, "xsm"); + if ( name.s ) + { + split_value(name.s); + read_file(dir_handle, s2w(&name), &xsm); + efi_bs->FreePool(name.w); + } + + name.s = get_value(&cfg, section.s, "options"); + if ( name.s ) + place_string(&mbi.cmdline, name.s); + /* Insert image name last, as it gets prefixed to the other options. */ + if ( argc ) + { + name.w = *argv; + w2s(&name); + } + else + name.s = "xen"; + place_string(&mbi.cmdline, name.s); + + cols = rows = depth = 0; + if ( !base_video ) + { + name.cs = get_value(&cfg, section.s, "video"); + if ( !name.cs ) + name.cs = get_value(&cfg, "global", "video"); + if ( name.cs && !strncmp(name.cs, "gfx-", 4) ) + { + cols = simple_strtoul(name.cs + 4, &name.cs, 10); + if ( *name.cs == 'x' ) + rows = simple_strtoul(name.cs + 1, &name.cs, 10); + if ( *name.cs == 'x' ) + depth = simple_strtoul(name.cs + 1, &name.cs, 10); + if ( *name.cs ) + cols = rows = depth = 0; + } + } + + efi_bs->FreePages(cfg.addr, PFN_UP(cfg.size)); + cfg.addr = 0; + + dir_handle->Close(dir_handle); + + if ( gop && !base_video ) + { + for ( i = size = 0; i < gop->Mode->MaxMode; ++i ) + { + unsigned int bpp = 0; + + status = gop->QueryMode(gop, i, &info_size, &mode_info); + if ( EFI_ERROR(status) ) + continue; + switch ( mode_info->PixelFormat ) + { + case PixelBitMask: + bpp = hweight32(mode_info->PixelInformation.RedMask | + mode_info->PixelInformation.GreenMask | + mode_info->PixelInformation.BlueMask); + break; + case PixelRedGreenBlueReserved8BitPerColor: + case PixelBlueGreenRedReserved8BitPerColor: + bpp = 24; + break; + default: + continue; + } + if ( cols == mode_info->HorizontalResolution && + rows == mode_info->VerticalResolution && + (!depth || bpp == depth) ) + { + gop_mode = i; + break; + } + if ( !cols && !rows && + mode_info->HorizontalResolution * + mode_info->VerticalResolution > size ) + { + size = mode_info->HorizontalResolution * + mode_info->VerticalResolution; + gop_mode = i; + } + } + } + + if ( mbi.cmdline ) + mbi.flags |= MBI_CMDLINE; + /* + * These must not be initialized statically, since the value must + * not get relocated when processing base relocations below. + */ + mbi.boot_loader_name = (long)"EFI"; + mbi.mods_addr = (long)mb_modules; + + place_string(&mbi.mem_upper, NULL); + + /* XXX Collect EDD info. */ + /* XXX Collect EDID info. */ + + if ( cpuid_eax(0x80000000) > 0x80000000 ) + { + cpuid_ext_features = cpuid_edx(0x80000001); + boot_cpu_data.x86_capability[1] = cpuid_ext_features; + } + + /* Obtain basic table pointers. */ + for ( i = 0; i < efi_num_ct; ++i ) + { + static EFI_GUID __initdata acpi2_guid = ACPI_20_TABLE_GUID; + static EFI_GUID __initdata acpi_guid = ACPI_TABLE_GUID; + static EFI_GUID __initdata smbios_guid = SMBIOS_TABLE_GUID; + + if ( match_guid(&acpi2_guid, &efi_ct[i].VendorGuid) ) + efi.acpi20 = (long)efi_ct[i].VendorTable; + if ( match_guid(&acpi_guid, &efi_ct[i].VendorGuid) ) + efi.acpi = (long)efi_ct[i].VendorTable; + if ( match_guid(&smbios_guid, &efi_ct[i].VendorGuid) ) + efi.smbios = (long)efi_ct[i].VendorTable; + } + + if (efi.smbios != EFI_INVALID_TABLE_ADDR) + dmi_efi_get_table((void *)(long)efi.smbios); + + /* Allocate space for trampoline (in first Mb). */ + cfg.addr = BOOT_TRAMPOLINE; + cfg.size = trampoline_end - trampoline_start; + status = efi_bs->AllocatePages(AllocateAddress, EfiLoaderData, + PFN_UP(cfg.size), &cfg.addr); + if ( EFI_ERROR(status) ) + { + cfg.addr = 0; + PrintErr(L"Note: Trampoline area is in use\r\n"); + } + + /* Initialise L2 identity-map and xen page table entries (16MB). */ + for ( i = 0; i < 8; ++i ) + { + unsigned int slot = (xen_phys_start >> L2_PAGETABLE_SHIFT) + i; + paddr_t addr = slot << L2_PAGETABLE_SHIFT; + + l2_identmap[i] = l2e_from_paddr(i << L2_PAGETABLE_SHIFT, + PAGE_HYPERVISOR|_PAGE_PSE); + l2_identmap[slot] = l2e_from_paddr(addr, PAGE_HYPERVISOR|_PAGE_PSE); + l2_xenmap[i] = l2e_from_paddr(addr, PAGE_HYPERVISOR|_PAGE_PSE); + slot &= L2_PAGETABLE_ENTRIES - 1; + l2_bootmap[slot] = l2e_from_paddr(addr, __PAGE_HYPERVISOR|_PAGE_PSE); + } + /* Initialise L3 identity-map page directory entries. */ + for ( i = 0; i < ARRAY_SIZE(l2_identmap) / L2_PAGETABLE_ENTRIES; ++i ) + l3_identmap[i] = l3e_from_paddr((UINTN)(l2_identmap + + i * L2_PAGETABLE_ENTRIES), + __PAGE_HYPERVISOR); + /* Initialise L3 xen-map page directory entry. */ + l3_xenmap[l3_table_offset(XEN_VIRT_START)] = + l3e_from_paddr((UINTN)l2_xenmap, __PAGE_HYPERVISOR); + /* Initialise L3 boot-map page directory entries. */ + l3_bootmap[l3_table_offset(xen_phys_start)] = + l3e_from_paddr((UINTN)l2_bootmap, __PAGE_HYPERVISOR); + l3_bootmap[l3_table_offset(xen_phys_start + (8 << L2_PAGETABLE_SHIFT) - 1)] = + l3e_from_paddr((UINTN)l2_bootmap, __PAGE_HYPERVISOR); + /* Hook identity-map, xen-map, and boot-map L3 tables into PML4. */ + idle_pg_table[0] = l4e_from_paddr((UINTN)l3_bootmap, __PAGE_HYPERVISOR); + idle_pg_table[l4_table_offset(DIRECTMAP_VIRT_START)] = + l4e_from_paddr((UINTN)l3_identmap, __PAGE_HYPERVISOR); + idle_pg_table[l4_table_offset(XEN_VIRT_START)] = + l4e_from_paddr((UINTN)l3_xenmap, __PAGE_HYPERVISOR); + /* Initialize 4kB mappings of first 2MB of memory. */ + for ( i = 0; i < L1_PAGETABLE_ENTRIES; ++i ) + { + unsigned int attr = PAGE_HYPERVISOR|MAP_SMALL_PAGES; + + /* VGA hole (0xa0000-0xc0000) should be mapped UC. */ + if ( i >= 0xa0 && i < 0xc0 ) + attr |= _PAGE_PCD; + l1_identmap[i] = l1e_from_pfn(i, attr); + } + l2_identmap[0] = l2e_from_paddr((UINTN)l1_identmap, __PAGE_HYPERVISOR); + + if ( gop ) + { + int bpp = 0; + + /* Set graphics mode. */ + if ( gop_mode < gop->Mode->MaxMode && gop_mode != gop->Mode->Mode ) + gop->SetMode(gop, gop_mode); + + /* Get graphics and frame buffer info. */ + status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &mode_info); + if ( !EFI_ERROR(status) ) + switch ( mode_info->PixelFormat ) + { + case PixelRedGreenBlueReserved8BitPerColor: + vga_console_info.u.vesa_lfb.red_pos = 0; + vga_console_info.u.vesa_lfb.red_size = 8; + vga_console_info.u.vesa_lfb.green_pos = 8; + vga_console_info.u.vesa_lfb.green_size = 8; + vga_console_info.u.vesa_lfb.blue_pos = 16; + vga_console_info.u.vesa_lfb.blue_size = 8; + vga_console_info.u.vesa_lfb.rsvd_pos = 24; + vga_console_info.u.vesa_lfb.rsvd_size = 8; + bpp = 32; + break; + case PixelBlueGreenRedReserved8BitPerColor: + vga_console_info.u.vesa_lfb.red_pos = 16; + vga_console_info.u.vesa_lfb.red_size = 8; + vga_console_info.u.vesa_lfb.green_pos = 8; + vga_console_info.u.vesa_lfb.green_size = 8; + vga_console_info.u.vesa_lfb.blue_pos = 0; + vga_console_info.u.vesa_lfb.blue_size = 8; + vga_console_info.u.vesa_lfb.rsvd_pos = 24; + vga_console_info.u.vesa_lfb.rsvd_size = 8; + bpp = 32; + break; + case PixelBitMask: + bpp = set_color(mode_info->PixelInformation.RedMask, bpp, + &vga_console_info.u.vesa_lfb.red_pos, + &vga_console_info.u.vesa_lfb.red_size); + bpp = set_color(mode_info->PixelInformation.GreenMask, bpp, + &vga_console_info.u.vesa_lfb.green_pos, + &vga_console_info.u.vesa_lfb.green_size); + bpp = set_color(mode_info->PixelInformation.BlueMask, bpp, + &vga_console_info.u.vesa_lfb.blue_pos, + &vga_console_info.u.vesa_lfb.blue_size); + bpp = set_color(mode_info->PixelInformation.ReservedMask, bpp, + &vga_console_info.u.vesa_lfb.rsvd_pos, + &vga_console_info.u.vesa_lfb.rsvd_size); + if ( bpp > 0 ) + break; + /* fall through */ + default: + PrintErr(L"Current graphics mode is unsupported!"); + status = EFI_UNSUPPORTED; + break; + } + if ( !EFI_ERROR(status) ) + { + vga_console_info.video_type = XEN_VGATYPE_EFI_LFB; + vga_console_info.u.vesa_lfb.gbl_caps = 2; /* possibly non-VGA */ + vga_console_info.u.vesa_lfb.width = + mode_info->HorizontalResolution; + vga_console_info.u.vesa_lfb.height = mode_info->VerticalResolution; + vga_console_info.u.vesa_lfb.bits_per_pixel = bpp; + vga_console_info.u.vesa_lfb.bytes_per_line = + (mode_info->PixelsPerScanLine * bpp + 7) >> 3; + vga_console_info.u.vesa_lfb.lfb_base = gop->Mode->FrameBufferBase; + vga_console_info.u.vesa_lfb.lfb_size = + (gop->Mode->FrameBufferSize + 0xffff) >> 16; + } + } + + status = efi_bs->GetMemoryMap(&efi_memmap_size, NULL, &map_key, + &efi_mdesc_size, &mdesc_ver); + mbi.mem_upper -= efi_memmap_size; + mbi.mem_upper &= -__alignof__(EFI_MEMORY_DESCRIPTOR); + if ( mbi.mem_upper < xen_phys_start ) + blexit(L"Out of static memory\r\n"); + efi_memmap = (void *)(long)mbi.mem_upper; + status = efi_bs->GetMemoryMap(&efi_memmap_size, efi_memmap, &map_key, + &efi_mdesc_size, &mdesc_ver); + if ( EFI_ERROR(status) ) + blexit(L"Cannot obtain memory map\r\n"); + + /* Populate E820 table and check trampoline area availability. */ + e = e820map - 1; + for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size ) + { + EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i; + u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT; + u32 type; + + switch ( desc->Type ) + { + default: + type = E820_RESERVED; + break; + case EfiConventionalMemory: + case EfiLoaderCode: + case EfiLoaderData: + case EfiBootServicesCode: + case EfiBootServicesData: + if ( desc->Attribute & EFI_MEMORY_WB ) + type = E820_RAM; + else + case EfiUnusableMemory: + type = E820_UNUSABLE; + break; + case EfiACPIReclaimMemory: + type = E820_ACPI; + break; + case EfiACPIMemoryNVS: + type = E820_NVS; + break; + } + if ( e820nr && type == e->type && + desc->PhysicalStart == e->addr + e->size ) + e->size += len; + else if ( !len || e820nr >= E820MAX ) + continue; + else + { + ++e; + e->addr = desc->PhysicalStart; + e->size = len; + e->type = type; + ++e820nr; + } + if ( type == E820_RAM && e->addr <= BOOT_TRAMPOLINE && + e->addr + e->size >= BOOT_TRAMPOLINE + cfg.size ) + trampoline_okay = 1; + } + + if ( !trampoline_okay ) + blexit(L"Trampoline area unavailable\r\n"); + + status = efi_bs->ExitBootServices(ImageHandle, map_key); + if ( EFI_ERROR(status) ) + PrintErrMesg(L"Cannot exit boot services", status); + + /* Adjust pointers into EFI. */ + efi_ct = (void *)efi_ct + DIRECTMAP_VIRT_START; +#if 0 /* Only needed when using virtual mode (see efi_init_memory()). */ + efi_rs = (void *)efi_rs + DIRECTMAP_VIRT_START; +#endif + efi_memmap = (void *)efi_memmap + DIRECTMAP_VIRT_START; + efi_fw_vendor = (void *)efi_fw_vendor + DIRECTMAP_VIRT_START; + + relocate_image(__XEN_VIRT_START - xen_phys_start); + memcpy((void *)(long)BOOT_TRAMPOLINE, trampoline_start, cfg.size); + + /* Set system registers and transfer control. */ + asm volatile("pushq $0\n\tpopfq"); + rdmsrl(MSR_EFER, efer); + efer |= EFER_SCE; + if ( cpuid_ext_features & (1 << (X86_FEATURE_NX & 0x1f)) ) + efer |= EFER_NX; + wrmsrl(MSR_EFER, efer); + write_cr0(X86_CR0_PE | X86_CR0_MP | X86_CR0_ET | X86_CR0_NE | X86_CR0_WP | + X86_CR0_AM | X86_CR0_PG); + asm volatile ( "mov %[cr4], %%cr4\n\t" + "mov %[cr3], %%cr3\n\t" + "movabs $__start_xen, %[rip]\n\t" + "lidt idt_descr(%%rip)\n\t" + "lgdt gdt_descr(%%rip)\n\t" + "mov stack_start(%%rip), %%rsp\n\t" + "mov %[ds], %%ss\n\t" + "mov %[ds], %%ds\n\t" + "mov %[ds], %%es\n\t" + "mov %[ds], %%fs\n\t" + "mov %[ds], %%gs\n\t" + "movl %[cs], 8(%%rsp)\n\t" + "mov %[rip], (%%rsp)\n\t" + "lretq %[stkoff]-16" + : [rip] "=&r" (efer/* any dead 64-bit variable */) + : [cr3] "r" (idle_pg_table), + [cr4] "r" (mmu_cr4_features), + [cs] "ir" (__HYPERVISOR_CS), + [ds] "r" (__HYPERVISOR_DS), + [stkoff] "i" (STACK_SIZE - sizeof(struct cpu_info)), + "D" (&mbi) + : "memory" ); + for( ; ; ); /* not reached */ +} + +void __init efi_init_memory(void) +{ + unsigned int i; + + printk(XENLOG_INFO "EFI memory map:\n"); + for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size ) + { + EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i; + u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT; + unsigned long smfn, emfn; + unsigned int prot = PAGE_HYPERVISOR; + + printk(XENLOG_INFO " %013" PRIx64 "-%013" PRIx64 + " type=%u attr=%016" PRIx64 "\n", + desc->PhysicalStart, desc->PhysicalStart + len - 1, + desc->Type, desc->Attribute); + + if ( !(desc->Attribute & EFI_MEMORY_RUNTIME) ) + continue; + + smfn = PFN_DOWN(desc->PhysicalStart); + emfn = PFN_UP(desc->PhysicalStart + len); + + desc->VirtualStart = 0xBAAADUL << (EFI_PAGE_SHIFT + BITS_PER_LONG - 32); + + if ( desc->Attribute & EFI_MEMORY_WB ) + /* nothing */; + else if ( desc->Attribute & EFI_MEMORY_WT ) + prot |= _PAGE_PWT | MAP_SMALL_PAGES; + else if ( desc->Attribute & EFI_MEMORY_WC ) + prot |= _PAGE_PAT | MAP_SMALL_PAGES; + else if ( desc->Attribute & (EFI_MEMORY_UC | EFI_MEMORY_UCE) ) + prot |= _PAGE_PWT | _PAGE_PCD | MAP_SMALL_PAGES; + else + { + printk(XENLOG_ERR "Unknown cachability for MFNs %#lx-%#lx\n", + smfn, emfn - 1); + continue; + } + + if ( desc->Attribute & EFI_MEMORY_WP ) + prot &= _PAGE_RW; + if ( desc->Attribute & EFI_MEMORY_XP ) + prot |= _PAGE_NX_BIT; + + if ( pfn_to_pdx(emfn - 1) < (DIRECTMAP_SIZE >> PAGE_SHIFT) && + !(smfn & pfn_hole_mask) && + !((smfn ^ (emfn - 1)) & ~pfn_pdx_bottom_mask) ) + { + if ( map_pages_to_xen((unsigned long)mfn_to_virt(smfn), + smfn, emfn - smfn, prot) == 0 ) + desc->VirtualStart = + (unsigned long)maddr_to_virt(desc->PhysicalStart); + else + printk(XENLOG_ERR "Could not map MFNs %#lx-%#lx\n", + smfn, emfn - 1); + } + else + { + /* XXX allocate e.g. down from FIXADDR_START */ + printk(XENLOG_ERR "No mapping for MFNs %#lx-%#lx\n", + smfn, emfn - 1); + } + } + +#if 0 /* Incompatible with kexec. */ + efi_rs->SetVirtualAddressMap(efi_memmap_size, efi_mdesc_size, + mdesc_ver, efi_memmap); +#endif +} diff --git a/xen/arch/x86/efi/check.c b/xen/arch/x86/efi/check.c new file mode 100644 index 0000000000..7fedd5a610 --- /dev/null +++ b/xen/arch/x86/efi/check.c @@ -0,0 +1,4 @@ +int __attribute__((__ms_abi__)) test(int i) +{ + return i; +} diff --git a/xen/arch/x86/efi/compat.c b/xen/arch/x86/efi/compat.c new file mode 100644 index 0000000000..4277f51f9f --- /dev/null +++ b/xen/arch/x86/efi/compat.c @@ -0,0 +1,16 @@ +#include <xen/guest_access.h> +#include <compat/platform.h> + +#define efi_get_info efi_compat_get_info +#define xenpf_efi_info compat_pf_efi_info + +#define COMPAT +#undef DEFINE_XEN_GUEST_HANDLE +#define DEFINE_XEN_GUEST_HANDLE DEFINE_COMPAT_HANDLE +#undef guest_handle_okay +#define guest_handle_okay compat_handle_okay +#undef guest_handle_cast +#define guest_handle_cast compat_handle_cast +#undef __copy_to_guest_offset +#define __copy_to_guest_offset __copy_to_compat_offset +#include "runtime.c" diff --git a/xen/arch/x86/efi/efi.h b/xen/arch/x86/efi/efi.h new file mode 100644 index 0000000000..e7673ee910 --- /dev/null +++ b/xen/arch/x86/efi/efi.h @@ -0,0 +1,18 @@ +#include <asm/efibind.h> +#include <efi/efidef.h> +#include <efi/efierr.h> +#include <efi/eficon.h> +#include <efi/efidevp.h> +#include <efi/efiapi.h> +#include <xen/efi.h> + +extern unsigned int efi_num_ct; +extern EFI_CONFIGURATION_TABLE *efi_ct; + +extern unsigned int efi_version, efi_fw_revision; +extern const CHAR16 *efi_fw_vendor; + +extern EFI_RUNTIME_SERVICES *efi_rs; + +extern UINTN efi_memmap_size, efi_mdesc_size; +extern void *efi_memmap; diff --git a/xen/arch/x86/efi/mkreloc.c b/xen/arch/x86/efi/mkreloc.c new file mode 100644 index 0000000000..6e52a2e647 --- /dev/null +++ b/xen/arch/x86/efi/mkreloc.c @@ -0,0 +1,377 @@ +#include <fcntl.h> +#include <inttypes.h> +#include <limits.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +struct mz_hdr { + uint16_t signature; +#define MZ_SIGNATURE 0x5a4d + uint16_t last_page_size; + uint16_t page_count; + uint16_t relocation_count; + uint16_t header_paras; + uint16_t min_paras; + uint16_t max_paras; + uint16_t entry_ss; + uint16_t entry_sp; + uint16_t checksum; + uint16_t entry_ip; + uint16_t entry_cs; + uint16_t relocations; + uint16_t overlay; + uint8_t reserved[32]; + uint32_t extended_header_base; +}; + +struct pe_hdr { + uint32_t signature; +#define PE_SIGNATURE 0x00004550 + uint16_t cpu; + uint16_t section_count; + int32_t timestamp; + uint32_t symbols_file_offset; + uint32_t symbol_count; + uint16_t opt_hdr_size; + uint16_t flags; + struct { + uint16_t magic; +#define PE_MAGIC_EXE32 0x010b +#define PE_MAGIC_EXE32PLUS 0x020b + uint8_t linker_major, linker_minor; + uint32_t code_size, data_size, bss_size; + uint32_t entry_rva, code_rva, data_rva; + } opt_hdr; +}; + +#define PE_PAGE_SIZE 0x1000 + +#define PE_BASE_RELOC_ABS 0 +#define PE_BASE_RELOC_HIGHLOW 3 +#define PE_BASE_RELOC_DIR64 10 + +struct coff_section { + char name[8]; + uint32_t size; + uint32_t rva; + uint32_t file_size; + uint32_t file_offset; + uint32_t relocation_file_offset; + uint32_t line_number_file_offset; + uint16_t relocation_count; + uint16_t line_number_count; + uint32_t flags; +#define COFF_SECTION_BSS 0x00000080 +#define COFF_SECTION_DISCARDABLE 0x02000000 +}; + +static void usage(const char *cmd, int rc) +{ + fprintf(rc ? stderr : stdout, + "Usage: %s <image1> <image2>\n", + cmd); + exit(rc); +} + +static unsigned int load(const char *name, int *handle, + struct coff_section **sections, + uint_fast64_t *image_base, + uint32_t *image_size, + unsigned int *width) +{ + int in = open(name, O_RDONLY); + struct mz_hdr mz_hdr; + struct pe_hdr pe_hdr; + uint32_t base; + + if ( in < 0 || + read(in, &mz_hdr, sizeof(mz_hdr)) != sizeof(mz_hdr) ) + { + perror(name); + exit(2); + } + if ( mz_hdr.signature != MZ_SIGNATURE || + mz_hdr.relocations < sizeof(mz_hdr) || + !mz_hdr.extended_header_base ) + { + fprintf(stderr, "%s: Wrong DOS file format\n", name); + exit(2); + } + + if ( lseek(in, mz_hdr.extended_header_base, SEEK_SET) < 0 || + read(in, &pe_hdr, sizeof(pe_hdr)) != sizeof(pe_hdr) || + read(in, &base, sizeof(base)) != sizeof(base) || + /* + * Luckily the image size field lives at the + * same offset for both formats. + */ + lseek(in, 24, SEEK_CUR) < 0 || + read(in, image_size, sizeof(*image_size)) != sizeof(*image_size) ) + { + perror(name); + exit(3); + } + switch ( (pe_hdr.signature == PE_SIGNATURE && + pe_hdr.opt_hdr_size > sizeof(pe_hdr.opt_hdr)) * + pe_hdr.opt_hdr.magic ) + { + case PE_MAGIC_EXE32: + *width = 32; + *image_base = base; + break; + case PE_MAGIC_EXE32PLUS: + *width = 64; + *image_base = ((uint64_t)base << 32) | pe_hdr.opt_hdr.data_rva; + break; + default: + fprintf(stderr, "%s: Wrong PE file format\n", name); + exit(3); + } + + *sections = malloc(pe_hdr.section_count * sizeof(**sections)); + if ( !*sections ) + { + perror(NULL); + exit(4); + } + if ( lseek(in, + mz_hdr.extended_header_base + offsetof(struct pe_hdr, opt_hdr) + + pe_hdr.opt_hdr_size, + SEEK_SET) < 0 || + read(in, *sections, pe_hdr.section_count * sizeof(**sections)) != + pe_hdr.section_count * sizeof(**sections) ) + { + perror(name); + exit(4); + } + + *handle = in; + + return pe_hdr.section_count; +} + +static long page_size; + +static const void *map_section(const struct coff_section *sec, int in, + const char *name) +{ + const char *ptr; + unsigned long offs; + + if ( !page_size ) + page_size = sysconf(_SC_PAGESIZE); + offs = sec->file_offset & (page_size - 1); + + ptr = mmap(0, offs + sec->file_size, PROT_READ, MAP_PRIVATE, in, + sec->file_offset - offs); + if ( ptr == MAP_FAILED ) + { + perror(name); + exit(6); + } + + return ptr + offs; +} + +static void unmap_section(const void *ptr, const struct coff_section *sec) +{ + unsigned long offs = sec->file_offset & (page_size - 1); + + munmap((char *)ptr - offs, offs + sec->file_size); +} + +static void diff_sections(const unsigned char *ptr1, const unsigned char *ptr2, + const struct coff_section *sec, + int_fast64_t diff, unsigned int width, + uint_fast64_t base, uint_fast64_t end) +{ + static uint_fast32_t cur_rva, reloc_size; + unsigned int disp = 0; + uint_fast32_t i; + + if ( !sec ) + { + reloc_size += reloc_size & 2; + if ( reloc_size ) + printf("\t.balign 4\n" + "\t.equ rva_%08" PRIxFAST32 "_relocs, %#08" PRIxFAST32 "\n", + cur_rva, reloc_size); + return; + } + + while ( !(diff & (((int_fast64_t)1 << ((disp + 1) * CHAR_BIT)) - 1)) ) + ++disp; + + for ( i = 0; i < sec->file_size; ++i ) + { + uint_fast32_t rva; + union { + uint32_t u32; + uint64_t u64; + } val1, val2; + int_fast64_t delta; + unsigned int reloc = (width == 4 ? PE_BASE_RELOC_HIGHLOW : + PE_BASE_RELOC_DIR64); + + if ( ptr1[i] == ptr2[i] ) + continue; + + if ( i < disp || i + width - disp > sec->file_size ) + { + fprintf(stderr, + "Bogus difference at %s:%08" PRIxFAST32 "\n", + sec->name, i); + exit(3); + } + + memcpy(&val1, ptr1 + i - disp, width); + memcpy(&val2, ptr2 + i - disp, width); + delta = width == 4 ? val2.u32 - val1.u32 : val2.u64 - val1.u64; + if ( delta != diff ) + { + fprintf(stderr, + "Difference at %s:%08" PRIxFAST32 " is %#" PRIxFAST64 + " (expected %#" PRIxFAST64 ")\n", + sec->name, i, delta, diff); + continue; + } + if ( width == 8 && (val1.u64 < base || val1.u64 > end) ) + reloc = PE_BASE_RELOC_HIGHLOW; + + rva = (sec->rva + i - disp) & ~(PE_PAGE_SIZE - 1); + if ( rva > cur_rva ) + { + reloc_size += reloc_size & 2; + if ( reloc_size ) + printf("\t.equ rva_%08" PRIxFAST32 "_relocs," + " %#08" PRIxFAST32 "\n", + cur_rva, reloc_size); + printf("\t.balign 4\n" + "\t.long %#08" PRIxFAST32 "," + " rva_%08" PRIxFAST32 "_relocs\n", + rva, rva); + cur_rva = rva; + reloc_size = 8; + } + else if ( rva != cur_rva ) + { + fprintf(stderr, + "Cannot handle decreasing RVA (at %s:%08" PRIxFAST32 ")\n", + sec->name, i); + exit(3); + } + + printf("\t.word (%u << 12) | 0x%03" PRIxFAST32 "\n", + reloc, sec->rva + i - disp - rva); + reloc_size += 2; + i += width - disp - 1; + } +} + +int main(int argc, char *argv[]) +{ + int in1, in2; + unsigned int i, nsec, width1, width2; + uint_fast64_t base1, base2; + uint32_t size1, size2; + struct coff_section *sec1, *sec2; + + if ( argc == 1 || + !strcmp(argv[1], "-?") || + !strcmp(argv[1], "-h") || + !strcmp(argv[1], "--help") ) + usage(*argv, argc == 1); + + if ( argc != 3 ) + usage(*argv, 1); + + nsec = load(argv[1], &in1, &sec1, &base1, &size1, &width1); + if ( nsec != load(argv[2], &in2, &sec2, &base2, &size2, &width2) ) + { + fputs("Mismatched section counts\n", stderr); + return 5; + } + if ( width1 != width2 ) + { + fputs("Mismatched image types\n", stderr); + return 5; + } + width1 >>= 3; + if ( base1 == base2 ) + { + fputs("Images must have different base addresses\n", stderr); + return 5; + } + if ( size1 != size2 ) + { + fputs("Images must have identical sizes\n", stderr); + return 5; + } + + puts("\t.section .reloc, \"a\", @progbits\n" + "\t.balign 4\n" + "\t.globl __base_relocs_start, __base_relocs_end\n" + "__base_relocs_start:"); + + for ( i = 0; i < nsec; ++i ) + { + const void *ptr1, *ptr2; + + if ( memcmp(sec1[i].name, sec2[i].name, sizeof(sec1[i].name)) || + sec1[i].rva != sec2[i].rva || + sec1[i].size != sec2[i].size || + sec1[i].file_size != sec2[i].file_size || + sec1[i].flags != sec2[i].flags ) + { + fprintf(stderr, "Mismatched section %u parameters\n", i); + return 5; + } + + if ( !sec1[i].size || + (sec1[i].flags & (COFF_SECTION_DISCARDABLE|COFF_SECTION_BSS)) ) + continue; + + /* + * Don't generate relocations for sections that definitely + * aren't used by the boot loader code. + */ + if ( memcmp(sec1[i].name, ".initcal", sizeof(sec1[i].name)) == 0 || + memcmp(sec1[i].name, ".init.se", sizeof(sec1[i].name)) == 0 || + memcmp(sec1[i].name, ".lockpro", sizeof(sec1[i].name)) == 0 ) + continue; + + if ( !sec1[i].rva ) + { + fprintf(stderr, "Can't handle section %u with zero RVA\n", i); + return 3; + } + + if ( sec1[i].file_size > sec1[i].size ) + { + sec1[i].file_size = sec1[i].size; + sec2[i].file_size = sec2[i].size; + } + ptr1 = map_section(sec1 + i, in1, argv[1]); + ptr2 = map_section(sec2 + i, in2, argv[2]); + + diff_sections(ptr1, ptr2, sec1 + i, base2 - base1, width1, + base1, base1 + size1); + + unmap_section(ptr1, sec1 + i); + unmap_section(ptr2, sec2 + i); + } + + diff_sections(NULL, NULL, NULL, 0, 0, 0, 0); + + puts("__base_relocs_end:"); + + close(in1); + close(in2); + + return 0; +} diff --git a/xen/arch/x86/efi/relocs-dummy.S b/xen/arch/x86/efi/relocs-dummy.S new file mode 100644 index 0000000000..36c30063d2 --- /dev/null +++ b/xen/arch/x86/efi/relocs-dummy.S @@ -0,0 +1,13 @@ +#include <xen/config.h> + + .section .reloc, "a", @progbits + .balign 4 + .globl __base_relocs_start, __base_relocs_end +__base_relocs_start: + .long 0 + .long 8 +__base_relocs_end: + + .globl VIRT_START, ALT_START + .equ VIRT_START, XEN_VIRT_START + .equ ALT_START, XEN_VIRT_END diff --git a/xen/arch/x86/efi/runtime.c b/xen/arch/x86/efi/runtime.c new file mode 100644 index 0000000000..5b98d0c9df --- /dev/null +++ b/xen/arch/x86/efi/runtime.c @@ -0,0 +1,88 @@ +#include "efi.h" +#include <xen/cache.h> +#include <xen/errno.h> +#include <xen/guest_access.h> + +DEFINE_XEN_GUEST_HANDLE(CHAR16); + +#ifndef COMPAT + +# include <public/platform.h> + +const bool_t efi_enabled = 1; + +unsigned int __read_mostly efi_num_ct; +EFI_CONFIGURATION_TABLE *__read_mostly efi_ct; + +unsigned int __read_mostly efi_version; +unsigned int __read_mostly efi_fw_revision; +const CHAR16 *__read_mostly efi_fw_vendor; + +EFI_RUNTIME_SERVICES *__read_mostly efi_rs; + +UINTN __read_mostly efi_memmap_size; +UINTN __read_mostly efi_mdesc_size; +void *__read_mostly efi_memmap; + +struct efi __read_mostly efi = { + .acpi = EFI_INVALID_TABLE_ADDR, + .acpi20 = EFI_INVALID_TABLE_ADDR, + .smbios = EFI_INVALID_TABLE_ADDR, +}; + +#endif + +int efi_get_info(uint32_t idx, union xenpf_efi_info *info) +{ + unsigned int i, n; + + switch ( idx ) + { + case XEN_FW_EFI_VERSION: + info->version = efi_version; + break; + case XEN_FW_EFI_CONFIG_TABLE: + info->cfg.addr = __pa(efi_ct); + info->cfg.nent = efi_num_ct; + break; + case XEN_FW_EFI_VENDOR: + info->vendor.revision = efi_fw_revision; + n = info->vendor.bufsz / sizeof(*efi_fw_vendor); + if ( !guest_handle_okay(guest_handle_cast(info->vendor.name, + CHAR16), n) ) + return -EFAULT; + for ( i = 0; i < n; ++i ) + { + if ( __copy_to_guest_offset(info->vendor.name, i, + efi_fw_vendor + i, 1) ) + return -EFAULT; + if ( !efi_fw_vendor[i] ) + break; + } + break; + case XEN_FW_EFI_MEM_INFO: + for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size ) + { + EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i; + u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT; + + if ( info->mem.addr >= desc->PhysicalStart && + info->mem.addr < desc->PhysicalStart + len ) + { + info->mem.type = desc->Type; + info->mem.attr = desc->Attribute; + if ( info->mem.addr + info->mem.size < info->mem.addr || + info->mem.addr + info->mem.size > + desc->PhysicalStart + len ) + info->mem.size = desc->PhysicalStart + len - + info->mem.addr; + return 0; + } + } + return -ESRCH; + default: + return -EINVAL; + } + + return 0; +} diff --git a/xen/arch/x86/efi/stub.c b/xen/arch/x86/efi/stub.c new file mode 100644 index 0000000000..12289938d1 --- /dev/null +++ b/xen/arch/x86/efi/stub.c @@ -0,0 +1,17 @@ +#include <xen/efi.h> +#include <xen/errno.h> +#include <xen/init.h> + +#ifndef efi_enabled +const bool_t efi_enabled = 0; +#endif + +void __init efi_init_memory(void) { } + +int efi_get_info(uint32_t idx, union xenpf_efi_info *info) +{ + return -ENOSYS; +} + +int efi_compat_get_info(uint32_t idx, union compat_pf_efi_info *) + __attribute__((__alias__("efi_get_info"))); diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 3cc337d8a9..a3ca63f549 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -101,6 +101,7 @@ #include <xen/guest_access.h> #include <xen/pfn.h> #include <xen/xmalloc.h> +#include <xen/efi.h> #include <xen/grant_table.h> #include <asm/paging.h> #include <asm/shadow.h> @@ -382,6 +383,8 @@ void __init arch_init_memory(void) subarch_init_memory(); + efi_init_memory(); + mem_sharing_init(); } diff --git a/xen/arch/x86/platform_hypercall.c b/xen/arch/x86/platform_hypercall.c index 4475a5c1a5..aede176cbd 100644 --- a/xen/arch/x86/platform_hypercall.c +++ b/xen/arch/x86/platform_hypercall.c @@ -19,6 +19,7 @@ #include <xen/iocap.h> #include <xen/guest_access.h> #include <xen/acpi.h> +#include <xen/efi.h> #include <xen/cpu.h> #include <xen/pmstat.h> #include <xen/irq.h> @@ -290,6 +291,14 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xen_platform_op_t) u_xenpf_op) bootsym(boot_edid_info), 128) ) ret = -EFAULT; break; + case XEN_FW_EFI_INFO: + ret = efi_get_info(op->u.firmware_info.index, + &op->u.firmware_info.u.efi_info); + if ( ret == 0 && + copy_field_to_guest(u_xenpf_op, op, + u.firmware_info.u.efi_info) ) + ret = -EFAULT; + break; default: ret = -EINVAL; break; diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index 7a338d6d01..a7d132a250 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -7,6 +7,7 @@ #include <xen/serial.h> #include <xen/softirq.h> #include <xen/acpi.h> +#include <xen/efi.h> #include <xen/console.h> #include <xen/serial.h> #include <xen/trace.h> @@ -444,6 +445,10 @@ static void __init parse_video_info(void) { struct boot_video_info *bvi = &bootsym(boot_vid_info); + /* The EFI loader fills vga_console_info directly. */ + if ( efi_enabled ) + return; + if ( (bvi->orig_video_isVGA == 1) && (bvi->orig_video_mode == 3) ) { vga_console_info.video_type = XEN_VGATYPE_TEXT_MODE_3; @@ -615,6 +620,7 @@ void __init __start_xen(unsigned long mbi_p) vga_console_info.u.text_mode_3.font_height); break; case XEN_VGATYPE_VESA_LFB: + case XEN_VGATYPE_EFI_LFB: printk(" VGA is graphics mode %dx%d, %d bpp\n", vga_console_info.u.vesa_lfb.width, vga_console_info.u.vesa_lfb.height, @@ -660,7 +666,24 @@ void __init __start_xen(unsigned long mbi_p) if ( ((unsigned long)cpu0_stack & (STACK_SIZE-1)) != 0 ) EARLY_FAIL("Misaligned CPU0 stack.\n"); - if ( e820_raw_nr != 0 ) + if ( efi_enabled ) + { + set_pdx_range(xen_phys_start >> PAGE_SHIFT, + (xen_phys_start + BOOTSTRAP_MAP_BASE) >> PAGE_SHIFT); + + /* Clean up boot loader identity mappings. */ + destroy_xen_mappings(xen_phys_start, + xen_phys_start + BOOTSTRAP_MAP_BASE); + +#ifdef CONFIG_X86_64 + /* Make boot page tables match non-EFI boot. */ + l3_bootmap[l3_table_offset(BOOTSTRAP_MAP_BASE)] = + l3e_from_paddr(__pa(l2_bootmap), __PAGE_HYPERVISOR); +#endif + + memmap_type = loader; + } + else if ( e820_raw_nr != 0 ) { memmap_type = "Xen-e820"; } @@ -758,7 +781,7 @@ void __init __start_xen(unsigned long mbi_p) * we can relocate the dom0 kernel and other multiboot modules. Also, on * x86/64, we relocate Xen to higher memory. */ - for ( i = 0; i < mbi->mods_count; i++ ) + for ( i = 0; !efi_enabled && i < mbi->mods_count; i++ ) { if ( mod[i].mod_start & (PAGE_SIZE - 1) ) EARLY_FAIL("Bootloader didn't honor module alignment request.\n"); @@ -946,7 +969,8 @@ void __init __start_xen(unsigned long mbi_p) #else if ( !xen_phys_start ) EARLY_FAIL("Not enough memory to relocate Xen.\n"); - reserve_e820_ram(&boot_e820, __pa(&_start), __pa(&_end)); + reserve_e820_ram(&boot_e820, efi_enabled ? mbi->mem_upper : __pa(&_start), + __pa(&_end)); #endif /* Late kexec reservation (dynamic start address). */ diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c index 933b68f17b..9ff343b97e 100644 --- a/xen/arch/x86/x86_64/mm.c +++ b/xen/arch/x86/x86_64/mm.c @@ -830,7 +830,8 @@ void __init zap_low_mappings(void) /* Replace with mapping of the boot trampoline only. */ map_pages_to_xen(BOOT_TRAMPOLINE, BOOT_TRAMPOLINE >> PAGE_SHIFT, - 0x10, __PAGE_HYPERVISOR); + PFN_UP(trampoline_end - trampoline_start), + __PAGE_HYPERVISOR); } void *compat_arg_xlat_virt_base(void) diff --git a/xen/arch/x86/x86_64/platform_hypercall.c b/xen/arch/x86/x86_64/platform_hypercall.c index 2c9048c237..f20fb99dc6 100644 --- a/xen/arch/x86/x86_64/platform_hypercall.c +++ b/xen/arch/x86/x86_64/platform_hypercall.c @@ -11,6 +11,8 @@ DEFINE_XEN_GUEST_HANDLE(compat_platform_op_t); #define xen_platform_op_t compat_platform_op_t #define do_platform_op(x) compat_platform_op(_##x) +#define efi_get_info efi_compat_get_info + #define xen_processor_px compat_processor_px #define xen_processor_px_t compat_processor_px_t #define xen_processor_performance compat_processor_performance diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S index 16e710c3fd..7679202d50 100644 --- a/xen/arch/x86/xen.lds.S +++ b/xen/arch/x86/xen.lds.S @@ -8,15 +8,34 @@ #undef ENTRY #undef ALIGN +#ifdef EFI + +#define FORMAT "pei-x86-64" +#undef __XEN_VIRT_START +#define __XEN_VIRT_START __image_base__ + +ENTRY(efi_start) + +#else /* !EFI */ + +#ifdef __x86_64__ +#define FORMAT "elf64-x86-64" +#else +#define FORMAT "elf32-i386" +#endif + +ENTRY(start) + +#endif /* EFI */ + +OUTPUT_FORMAT(FORMAT, FORMAT, FORMAT) + #ifdef __x86_64__ -OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") OUTPUT_ARCH(i386:x86-64) #else -OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) #endif -ENTRY(start) PHDRS { text PT_LOAD ; @@ -122,12 +141,29 @@ SECTIONS } :text _end = . ; +#ifdef EFI + . = ALIGN(4); + .reloc : { + *(.reloc) + } :text + /* Trick the linker into setting the image size to exactly 16Mb. */ + . = ALIGN(__section_alignment__); + .pad : { + . = ALIGN(0x1000000); + } :text +#else + efi = .; +#endif + /* Sections to be discarded */ /DISCARD/ : { *(.exit.text) *(.exit.data) *(.exitcall.exit) *(.eh_frame) +#ifdef EFI + *(.comment) +#endif } /* Stabs debugging sections. */ diff --git a/xen/drivers/acpi/osl.c b/xen/drivers/acpi/osl.c index acee045512..dd96a1a20e 100644 --- a/xen/drivers/acpi/osl.c +++ b/xen/drivers/acpi/osl.c @@ -36,9 +36,7 @@ #include <acpi/platform/aclinux.h> #include <xen/spinlock.h> #include <xen/domain_page.h> -#ifdef __ia64__ -#include <linux/efi.h> -#endif +#include <xen/efi.h> #define _COMPONENT ACPI_OS_SERVICES ACPI_MODULE_NAME("osl") @@ -66,7 +64,6 @@ void __init acpi_os_vprintf(const char *fmt, va_list args) acpi_physical_address __init acpi_os_get_root_pointer(void) { -#ifdef __ia64__ if (efi_enabled) { if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) return efi.acpi20; @@ -77,9 +74,7 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) "System description tables not found\n"); return 0; } - } else -#endif - { + } else { acpi_physical_address pa = 0; acpi_find_root_pointer(&pa); diff --git a/xen/drivers/video/vga.c b/xen/drivers/video/vga.c index fea3e23cb4..7696946c73 100644 --- a/xen/drivers/video/vga.c +++ b/xen/drivers/video/vga.c @@ -87,6 +87,7 @@ void __init vga_init(void) vga_puts = vga_text_puts; break; case XEN_VGATYPE_VESA_LFB: + case XEN_VGATYPE_EFI_LFB: vesa_early_init(); break; default: @@ -113,6 +114,7 @@ void __init vga_endboot(void) memset(video, 0, columns * lines * 2); break; case XEN_VGATYPE_VESA_LFB: + case XEN_VGATYPE_EFI_LFB: vesa_endboot(vgacon_keep); break; default: diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h index 68af2a77a0..3327705983 100644 --- a/xen/include/asm-x86/page.h +++ b/xen/include/asm-x86/page.h @@ -301,10 +301,14 @@ extern l2_pgentry_t idle_pg_table_l2[ #elif CONFIG_PAGING_LEVELS == 4 extern l2_pgentry_t *compat_idle_pg_table_l2; extern unsigned int m2p_compat_vstart; +extern l2_pgentry_t l2_xenmap[L2_PAGETABLE_ENTRIES], + l2_bootmap[L2_PAGETABLE_ENTRIES]; +extern l3_pgentry_t l3_xenmap[L3_PAGETABLE_ENTRIES], + l3_identmap[L3_PAGETABLE_ENTRIES], + l3_bootmap[L3_PAGETABLE_ENTRIES]; #endif extern l2_pgentry_t l2_identmap[4*L2_PAGETABLE_ENTRIES]; extern l1_pgentry_t l1_identmap[L1_PAGETABLE_ENTRIES]; -extern l2_pgentry_t l2_xenmap[]; void paging_init(void); void setup_idle_pagetable(void); #endif /* !defined(__ASSEMBLY__) */ diff --git a/xen/include/public/platform.h b/xen/include/public/platform.h index d03ec24e7e..deb2f57bb3 100644 --- a/xen/include/public/platform.h +++ b/xen/include/public/platform.h @@ -118,6 +118,11 @@ DEFINE_XEN_GUEST_HANDLE(xenpf_platform_quirk_t); #define XEN_FW_DISK_INFO 1 /* from int 13 AH=08/41/48 */ #define XEN_FW_DISK_MBR_SIGNATURE 2 /* from MBR offset 0x1b8 */ #define XEN_FW_VBEDDC_INFO 3 /* from int 10 AX=4f15 */ +#define XEN_FW_EFI_INFO 4 /* from EFI */ +#define XEN_FW_EFI_VERSION 0 +#define XEN_FW_EFI_CONFIG_TABLE 1 +#define XEN_FW_EFI_VENDOR 2 +#define XEN_FW_EFI_MEM_INFO 3 struct xenpf_firmware_info { /* IN variables. */ uint32_t type; @@ -148,6 +153,24 @@ struct xenpf_firmware_info { /* must refer to 128-byte buffer */ XEN_GUEST_HANDLE(uint8) edid; } vbeddc_info; /* XEN_FW_VBEDDC_INFO */ + union xenpf_efi_info { + uint32_t version; + struct { + uint64_t addr; /* EFI_CONFIGURATION_TABLE */ + uint32_t nent; + } cfg; + struct { + uint32_t revision; + uint32_t bufsz; /* input, in bytes */ + XEN_GUEST_HANDLE(void) name; /* UCS-2/UTF-16 string */ + } vendor; + struct { + uint64_t addr; + uint64_t size; + uint64_t attr; + uint32_t type; + } mem; + } efi_info; /* XEN_FW_EFI_INFO */ } u; }; typedef struct xenpf_firmware_info xenpf_firmware_info_t; diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h index 93c3fe3837..02b8fe0ec5 100644 --- a/xen/include/public/xen.h +++ b/xen/include/public/xen.h @@ -638,6 +638,7 @@ typedef struct dom0_vga_console_info { uint8_t video_type; /* DOM0_VGA_CONSOLE_??? */ #define XEN_VGATYPE_TEXT_MODE_3 0x03 #define XEN_VGATYPE_VESA_LFB 0x23 +#define XEN_VGATYPE_EFI_LFB 0x70 union { struct { diff --git a/xen/include/xen/compat.h b/xen/include/xen/compat.h index 0b8c2c9416..de87aac336 100644 --- a/xen/include/xen/compat.h +++ b/xen/include/xen/compat.h @@ -34,7 +34,7 @@ /* Cast a compat handle to the specified type of handle. */ #define compat_handle_cast(chnd, type) ({ \ type *_x = (__typeof__(**(chnd)._) *)(full_ptr_t)(chnd).c; \ - (XEN_GUEST_HANDLE(type)) { _x }; \ + (COMPAT_HANDLE(type)) { (full_ptr_t)_x }; \ }) #define guest_from_compat_handle(ghnd, chnd) \ diff --git a/xen/include/xen/dmi.h b/xen/include/xen/dmi.h index 65cf2ad4e3..e9c92b32c3 100644 --- a/xen/include/xen/dmi.h +++ b/xen/include/xen/dmi.h @@ -35,6 +35,7 @@ struct dmi_system_id { extern int dmi_check_system(struct dmi_system_id *list); extern void dmi_scan_machine(void); extern int dmi_get_table(u32 *base, u32 *len); +extern void dmi_efi_get_table(void *); extern void dmi_end_boot(void); #endif /* __DMI_H__ */ diff --git a/xen/include/xen/efi.h b/xen/include/xen/efi.h new file mode 100644 index 0000000000..d32d4f2bc1 --- /dev/null +++ b/xen/include/xen/efi.h @@ -0,0 +1,38 @@ +#ifndef __XEN_EFI_H__ +#define __XEN_EFI_H__ + +#include <xen/types.h> + +#if defined(__ia64__) +# #include <linux/efi.h> +#else + +# if defined(__i386__) +# define efi_enabled 0 +# else +extern const bool_t efi_enabled; +# endif + +#define EFI_INVALID_TABLE_ADDR (~0UL) + +/* Add fields here only if they need to be referenced from non-EFI code. */ +struct efi { + unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ + unsigned long acpi20; /* ACPI table (ACPI 2.0) */ + unsigned long smbios; /* SM BIOS table */ +}; + +extern struct efi efi; + +#endif + +union xenpf_efi_info; +union compat_pf_efi_info; + +void efi_init_memory(void); +#ifndef COMPAT +int efi_get_info(uint32_t idx, union xenpf_efi_info *); +#endif +int efi_compat_get_info(uint32_t idx, union compat_pf_efi_info *); + +#endif /* __XEN_EFI_H__ */ |