Besides introducing the relevant code paralleling parts of what is under xen/arch/x86/boot/, this adjusts the build logic so that with a single compilation two images (gzip-compressed ELF and EFI application) can get created. The EFI part of this depends on a new enough compiler (supposedly gcc 4.4.x and above, but so far only tested to work with 4.5.x) and a properly configured linker (must support the i386pep emulation). If either functionality is found to not be available, the EFI part of the build will simply be skipped. The patch adds all code to allow Xen and the (accordingly enabled) Dom0 kernel to boot, but doesn't allow Dom0 to make use of EFI runtime calls (this will be the subject of the next patch). Parts of the code were lifted from an earlier never published OS project of ours - whether respective license information needs to be added to the respective source file is unclear to me (I was told internally that adding a GPLv2 license header can be done if needed by the community). Open issues (not preventing this from being committed imo): The trampoline allocation and initialization isn't really nice. This is due to the trampoline needing to be placed at a fixed address, and hence making the trampoline relocatable would seem desirable here (as well as for BIOS-based booting, where the trampoline location needed to be adjusted a number of time already in the past, due to it colliding with firmware data). By excluding mem.S, edd.S, and video.S from copied trampoline (i.e. moving up wakeup.S? and making sure none of the symbols are used from EFI code), the effective trampoline size could at least be reduced. Should the mappings of [__XEN_VIRT_START, mbi.mem_upper) and [_end, __XEN_VIRT_START+BOOTSTRAP_MAP_BASE) be destroyed, despite non-EFI code also keeping them? Signed-off-by: Jan Beulich --- a/xen/Makefile +++ b/xen/Makefile @@ -12,6 +12,8 @@ export XEN_DOMAIN ?= $(shell ([ -x /bin/ 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: --- 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) \ --- 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 --- 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 --- 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 --- a/xen/arch/x86/dmi_scan.c +++ b/xen/arch/x86/dmi_scan.c @@ -9,6 +9,7 @@ #include #include #include +#include #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 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 (*dec 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 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"); --- /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 --- /dev/null +++ b/xen/arch/x86/efi/boot.c @@ -0,0 +1,1221 @@ +#include "efi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if EFI_PAGE_SIZE != PAGE_SIZE +# error Cannot use xen/pfn.h here! +#endif +#include +#include +#include +#include +#include +#include + +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= 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 +} --- /dev/null +++ b/xen/arch/x86/efi/check.c @@ -0,0 +1,4 @@ +int __attribute__((__ms_abi__)) test(int i) +{ + return i; +} --- /dev/null +++ b/xen/arch/x86/efi/compat.c @@ -0,0 +1,16 @@ +#include +#include + +#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" --- /dev/null +++ b/xen/arch/x86/efi/efi.h @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include +#include +#include + +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; --- /dev/null +++ b/xen/arch/x86/efi/mkreloc.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 \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; +} --- /dev/null +++ b/xen/arch/x86/efi/relocs-dummy.S @@ -0,0 +1,13 @@ +#include + + .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 --- /dev/null +++ b/xen/arch/x86/efi/runtime.c @@ -0,0 +1,88 @@ +#include "efi.h" +#include +#include +#include + +DEFINE_XEN_GUEST_HANDLE(CHAR16); + +#ifndef COMPAT + +# include + +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; +} --- /dev/null +++ b/xen/arch/x86/efi/stub.c @@ -0,0 +1,17 @@ +#include +#include +#include + +#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"))); --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -101,6 +101,7 @@ #include #include #include +#include #include #include #include @@ -382,6 +383,8 @@ void __init arch_init_memory(void) subarch_init_memory(); + efi_init_memory(); + mem_sharing_init(); } --- a/xen/arch/x86/platform_hypercall.c +++ b/xen/arch/x86/platform_hypercall.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -289,6 +290,14 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xe 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; --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -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 mb 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 mb 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 mb * 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 mb #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). */ --- 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) --- 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_ #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 --- 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. */ --- a/xen/drivers/acpi/osl.c +++ b/xen/drivers/acpi/osl.c @@ -36,9 +36,7 @@ #include #include #include -#ifdef __ia64__ -#include -#endif +#include #define _COMPONENT ACPI_OS_SERVICES ACPI_MODULE_NAME("osl") @@ -66,7 +64,6 @@ void __init acpi_os_vprintf(const char * 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 "System description tables not found\n"); return 0; } - } else -#endif - { + } else { acpi_physical_address pa = 0; acpi_find_root_pointer(&pa); --- 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: --- 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__) */ --- a/xen/include/public/platform.h +++ b/xen/include/public/platform.h @@ -118,6 +118,11 @@ DEFINE_XEN_GUEST_HANDLE(xenpf_platform_q #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; --- 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 { --- 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) \ --- 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__ */ --- /dev/null +++ b/xen/include/xen/efi.h @@ -0,0 +1,38 @@ +#ifndef __XEN_EFI_H__ +#define __XEN_EFI_H__ + +#include + +#if defined(__ia64__) +# #include +#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__ */