@@ -89,6 +89,13 @@ multiboot2_header_start:
0, /* Number of the lines - no preference. */ \
0 /* Number of bits per pixel - no preference. */
+ /* Inhibit bootloader from calling ExitBootServices(). */
+ mb2ht_init MB2_HT(EFI_BS), MB2_HT(OPTIONAL)
+
+ /* EFI64 entry point. */
+ mb2ht_init MB2_HT(ENTRY_ADDRESS_EFI64), MB2_HT(OPTIONAL), \
+ sym_phys(__efi64_start)
+
/* Multiboot2 header end tag. */
mb2ht_init MB2_HT(END), MB2_HT(REQUIRED)
.Lmultiboot2_header_end:
@@ -100,20 +107,49 @@ multiboot2_header_start:
gdt_boot_descr:
.word 6*8-1
.long sym_phys(trampoline_gdt)
+ .long 0 /* Needed for 64-bit lgdt */
+
+cs32_switch_addr:
+ .long sym_phys(cs32_switch)
+ .word BOOT_CS32
+
+ .align 4
+vga_text_buffer:
+ .long 0xb8000
.Lbad_cpu_msg: .asciz "ERR: Not a 64-bit CPU!"
.Lbad_ldr_msg: .asciz "ERR: Not a Multiboot bootloader!"
+.Lbad_ldr_nbs: .asciz "ERR: Bootloader shutdown EFI x64 boot services!"
+.Lbad_ldr_nst: .asciz "ERR: EFI SystemTable is not provided by bootloader!"
+.Lbad_ldr_nih: .asciz "ERR: EFI ImageHandle is not provided by bootloader!"
+.Lbad_efi_msg: .asciz "ERR: EFI IA-32 platforms are not supported!"
.section .init.text, "ax", @progbits
bad_cpu:
mov $(sym_phys(.Lbad_cpu_msg)),%esi # Error message
- jmp print_err
+ jmp .Lget_vtb
not_multiboot:
mov $(sym_phys(.Lbad_ldr_msg)),%esi # Error message
-print_err:
- mov $0xB8000,%edi # VGA framebuffer
-1: mov (%esi),%bl
+ jmp .Lget_vtb
+.Lmb2_no_st:
+ mov $(sym_phys(.Lbad_ldr_nst)),%esi # Error message
+ jmp .Lget_vtb
+.Lmb2_no_ih:
+ mov $(sym_phys(.Lbad_ldr_nih)),%esi # Error message
+ jmp .Lget_vtb
+.Lmb2_no_bs:
+ mov $(sym_phys(.Lbad_ldr_nbs)),%esi # Error message
+ xor %edi,%edi # No VGA text buffer
+ jmp .Lsend_chr
+.Lmb2_efi_ia_32:
+ mov $(sym_phys(.Lbad_efi_msg)),%esi # Error message
+ xor %edi,%edi # No VGA text buffer
+ jmp .Lsend_chr
+.Lget_vtb:
+ mov sym_phys(vga_text_buffer),%edi
+.Lsend_chr:
+ mov (%esi),%bl
test %bl,%bl # Terminate on '\0' sentinel
je .Lhalt
mov $0x3f8+5,%dx # UART Line Status Register
@@ -123,13 +159,180 @@ print_err:
mov $0x3f8+0,%dx # UART Transmit Holding Register
mov %bl,%al
out %al,%dx # Send a character over the serial line
- movsb # Write a character to the VGA framebuffer
+ test %edi,%edi # Is the VGA text buffer available?
+ jz .Lsend_chr
+ movsb # Write a character to the VGA text buffer
mov $7,%al
- stosb # Write an attribute to the VGA framebuffer
- jmp 1b
+ stosb # Write an attribute to the VGA text buffer
+ jmp .Lsend_chr
.Lhalt: hlt
jmp .Lhalt
+ .code64
+
+__efi64_start:
+ cld
+
+ /* VGA is not available on EFI platforms. */
+ movl $0,vga_text_buffer(%rip)
+
+ /* Check for Multiboot2 bootloader. */
+ cmp $MULTIBOOT2_BOOTLOADER_MAGIC,%eax
+ je .Lefi_multiboot2_proto
+
+ /* Jump to not_multiboot after switching CPU to x86_32 mode. */
+ lea not_multiboot(%rip),%edi
+ jmp x86_32_switch
+
+.Lefi_multiboot2_proto:
+ /* Zero EFI SystemTable and EFI ImageHandle addresses. */
+ xor %esi,%esi
+ xor %edi,%edi
+
+ /* Skip Multiboot2 information fixed part. */
+ lea (MB2_fixed_sizeof+MULTIBOOT2_TAG_ALIGN-1)(%rbx),%ecx
+ and $~(MULTIBOOT2_TAG_ALIGN-1),%ecx
+
+0:
+ /* Check Multiboot2 information total size. */
+ mov %ecx,%r8d
+ sub %ebx,%r8d
+ cmp %r8d,MB2_fixed_total_size(%rbx)
+ jbe run_bs
+
+ /* Are EFI boot services available? */
+ cmpl $MULTIBOOT2_TAG_TYPE_EFI_BS,MB2_tag_type(%rcx)
+ jne 1f
+
+ /*
+ * Yes, store that info in skip_realmode variable. I agree that
+ * its name is a bit unfortunate in this context, however, by the
+ * way we disable real mode and other legacy stuff which should
+ * not be run on EFI platforms.
+ */
+ incb skip_realmode(%rip)
+ jmp 9f
+
+1:
+ /* Get EFI SystemTable address from Multiboot2 information. */
+ cmpl $MULTIBOOT2_TAG_TYPE_EFI64,MB2_tag_type(%rcx)
+ cmove MB2_efi64_st(%rcx),%rsi
+ je 9f
+
+ /* Get EFI ImageHandle address from Multiboot2 information. */
+ cmpl $MULTIBOOT2_TAG_TYPE_EFI64_IH,MB2_tag_type(%rcx)
+ cmove MB2_efi64_ih(%rcx),%rdi
+ je 9f
+
+ /* Is it the end of Multiboot2 information? */
+ cmpl $MULTIBOOT2_TAG_TYPE_END,MB2_tag_type(%rcx)
+ je run_bs
+
+9:
+ /* Go to next Multiboot2 information tag. */
+ add MB2_tag_size(%rcx),%ecx
+ add $(MULTIBOOT2_TAG_ALIGN-1),%ecx
+ and $~(MULTIBOOT2_TAG_ALIGN-1),%ecx
+ jmp 0b
+
+run_bs:
+ /* Are EFI boot services available? */
+ cmpb $0,skip_realmode(%rip)
+ jnz 0f
+
+ /* Jump to .Lmb2_no_bs after switching CPU to x86_32 mode. */
+ lea .Lmb2_no_bs(%rip),%edi
+ jmp x86_32_switch
+
+0:
+ /* Is EFI SystemTable address provided by boot loader? */
+ test %rsi,%rsi
+ jnz 1f
+
+ /* Jump to .Lmb2_no_st after switching CPU to x86_32 mode. */
+ lea .Lmb2_no_st(%rip),%edi
+ jmp x86_32_switch
+
+1:
+ /* Is EFI ImageHandle address provided by boot loader? */
+ test %rdi,%rdi
+ jnz 2f
+
+ /* Jump to .Lmb2_no_ih after switching CPU to x86_32 mode. */
+ lea .Lmb2_no_ih(%rip),%edi
+ jmp x86_32_switch
+
+2:
+ push %rax
+ push %rdi
+
+ /*
+ * Initialize BSS (no nasty surprises!).
+ * It must be done earlier than in BIOS case
+ * because efi_multiboot2() touches it.
+ */
+ lea .startof.(.bss)(%rip),%edi
+ mov $.sizeof.(.bss),%ecx
+ shr $3,%ecx
+ xor %eax,%eax
+ rep stosq
+
+ pop %rdi
+
+ /*
+ * efi_multiboot2() is called according to System V AMD64 ABI:
+ * - IN: %rdi - EFI ImageHandle, %rsi - EFI SystemTable,
+ * - OUT: %rax - highest usable memory address below 1 MiB;
+ * memory there is used later for stack and
+ * as a storage for boot data.
+ *
+ * MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO tag is not provided
+ * on EFI platforms. Hence, it could not be used like
+ * on legacy BIOS platforms.
+ */
+ call efi_multiboot2
+
+ /* Convert memory address to bytes/16 and store it in safe place. */
+ shr $4,%eax
+ mov %eax,%ecx
+
+ pop %rax
+
+ /* Jump to trampoline_setup after switching CPU to x86_32 mode. */
+ lea trampoline_setup(%rip),%edi
+
+x86_32_switch:
+ cli
+
+ /* Initialize GDTR. */
+ lgdt gdt_boot_descr(%rip)
+
+ /* Reload code selector. */
+ ljmpl *cs32_switch_addr(%rip)
+
+ .code32
+
+cs32_switch:
+ /* Initialize basic data segments. */
+ mov $BOOT_DS,%edx
+ mov %edx,%ds
+ mov %edx,%es
+ mov %edx,%ss
+ /* %esp is initialized later. */
+
+ /* Load null descriptor to unused segment registers. */
+ xor %edx,%edx
+ mov %edx,%fs
+ mov %edx,%gs
+
+ /* Disable paging. */
+ mov %cr0,%edx
+ and $(~X86_CR0_PG),%edx
+ mov %edx,%cr0
+
+ /* Jump to earlier loaded address. */
+ jmp *%edi
+
__start:
cld
cli
@@ -157,7 +360,7 @@ __start:
/* Not available? BDA value will be fine. */
cmovnz MB_mem_lower(%ebx),%edx
- jmp trampoline_setup
+ jmp trampoline_bios_setup
.Lmultiboot2_proto:
/* Skip Multiboot2 information fixed part. */
@@ -169,16 +372,24 @@ __start:
mov %ecx,%edi
sub %ebx,%edi
cmp %edi,MB2_fixed_total_size(%ebx)
- jbe trampoline_setup
+ jbe trampoline_bios_setup
/* Get mem_lower from Multiboot2 information. */
cmpl $MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO,MB2_tag_type(%ecx)
cmove MB2_mem_lower(%ecx),%edx
- je trampoline_setup
+ je trampoline_bios_setup
+
+ /* EFI IA-32 platforms are not supported. */
+ cmpl $MULTIBOOT2_TAG_TYPE_EFI32,MB2_tag_type(%ecx)
+ je .Lmb2_efi_ia_32
+
+ /* Bootloader shutdown EFI x64 boot services. */
+ cmpl $MULTIBOOT2_TAG_TYPE_EFI64,MB2_tag_type(%ecx)
+ je .Lmb2_no_bs
/* Is it the end of Multiboot2 information? */
cmpl $MULTIBOOT2_TAG_TYPE_END,MB2_tag_type(%ecx)
- je trampoline_setup
+ je trampoline_bios_setup
/* Go to next Multiboot2 information tag. */
add MB2_tag_size(%ecx),%ecx
@@ -186,7 +397,7 @@ __start:
and $~(MULTIBOOT2_TAG_ALIGN-1),%ecx
jmp 0b
-trampoline_setup:
+trampoline_bios_setup:
/* Set up trampoline segment 64k below EBDA */
movzwl 0x40e,%ecx /* EBDA segment */
cmp $0xa000,%ecx /* sanity check (high) */
@@ -202,12 +413,13 @@ trampoline_setup:
* multiboot structure (if available) and use the smallest.
*/
cmp $0x100,%edx /* is the multiboot value too small? */
- jb 2f /* if so, do not use it */
+ jb trampoline_setup /* if so, do not use it */
shl $10-4,%edx
cmp %ecx,%edx /* compare with BDA value */
cmovb %edx,%ecx /* and use the smaller */
-2: /* Reserve 64kb for the trampoline */
+trampoline_setup:
+ /* Reserve 64kb for the trampoline. */
sub $0x1000,%ecx
/* From arch/x86/smpboot.c: start_eip had better be page-aligned! */
@@ -223,7 +435,14 @@ trampoline_setup:
call reloc
mov %eax,sym_phys(multiboot_ptr)
- /* Initialize BSS (no nasty surprises!) */
+ /*
+ * Do not zero BSS on EFI platform here.
+ * It was initialized earlier.
+ */
+ cmpb $0,sym_phys(skip_realmode)
+ jnz 1f
+
+ /* Initialize BSS (no nasty surprises!). */
mov $sym_phys(__bss_start),%edi
mov $sym_phys(__bss_end),%ecx
sub %edi,%ecx
@@ -231,6 +450,7 @@ trampoline_setup:
shr $2,%ecx
rep stosl
+1:
/* Interrogate CPU extended features via CPUID. */
mov $0x80000000,%eax
cpuid
@@ -282,8 +502,13 @@ trampoline_setup:
cmp $sym_phys(__trampoline_seg_stop),%edi
jb 1b
+ /* Do not parse command line on EFI platform here. */
+ cmpb $0,sym_phys(skip_realmode)
+ jnz 1f
+
call cmdline_parse_early
+1:
/* Switch to low-memory stack. */
mov sym_phys(trampoline_phys),%edi
lea 0x10000(%edi),%esp
@@ -213,12 +213,14 @@ static void *__init efi_arch_allocate_mmap_buffer(UINTN map_size)
static void __init efi_arch_pre_exit_boot(void)
{
- if ( !trampoline_phys )
- {
- if ( !cfg.addr )
- blexit(L"No memory for trampoline");
+ if ( trampoline_phys )
+ return;
+
+ if ( !cfg.addr )
+ blexit(L"No memory for trampoline");
+
+ if ( efi_enabled(EFI_LOADER) )
relocate_trampoline(cfg.addr);
- }
}
static void __init noreturn efi_arch_post_exit_boot(void)
@@ -656,6 +658,43 @@ static bool_t __init efi_arch_use_config_file(EFI_SYSTEM_TABLE *SystemTable)
static void efi_arch_flush_dcache_area(const void *vaddr, UINTN size) { }
+paddr_t __init efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
+{
+ EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
+ UINTN cols, gop_mode = ~0, rows;
+
+ __set_bit(EFI_BOOT, &efi_flags);
+ __set_bit(EFI_RS, &efi_flags);
+
+ efi_init(ImageHandle, SystemTable);
+
+ efi_console_set_mode();
+
+ if ( StdOut->QueryMode(StdOut, StdOut->Mode->Mode,
+ &cols, &rows) == EFI_SUCCESS )
+ efi_arch_console_init(cols, rows);
+
+ gop = efi_get_gop();
+
+ if ( gop )
+ gop_mode = efi_find_gop_mode(gop, 0, 0, 0);
+
+ efi_arch_edd();
+ efi_arch_cpu();
+
+ efi_tables();
+ setup_efi_pci();
+ efi_variables();
+
+ if ( gop )
+ efi_set_gop_mode(gop, gop_mode);
+
+ efi_exit_boot(ImageHandle, SystemTable);
+
+ /* Return highest usable memory address below 1 MiB. */
+ return cfg.addr;
+}
+
/*
* Local variables:
* mode: C
@@ -3,6 +3,43 @@
#include <xen/init.h>
#include <xen/lib.h>
#include <asm/page.h>
+#include <asm/efibind.h>
+#include <efi/efidef.h>
+#include <efi/eficapsule.h>
+#include <efi/eficon.h>
+#include <efi/efidevp.h>
+#include <efi/efiapi.h>
+
+/*
+ * Here we are in EFI stub. EFI calls are not supported due to lack
+ * of relevant functionality in compiler and/or linker.
+ *
+ * efi_multiboot2() is an exception. Please look below for more details.
+ */
+
+paddr_t __init noreturn efi_multiboot2(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
+{
+ CHAR16 *err = L"Xen does not have EFI code build in!!!\r\nSystem halted!!!\r\n";
+ SIMPLE_TEXT_OUTPUT_INTERFACE *StdErr;
+
+ StdErr = SystemTable->StdErr ? SystemTable->StdErr : SystemTable->ConOut;
+
+ /*
+ * Print error message and halt the system.
+ *
+ * We have to open code MS x64 calling convention
+ * in assembly because here this convention is not
+ * directly supported by C compiler and/or linker.
+ */
+ asm volatile(
+ " call %2 \n"
+ "0: hlt \n"
+ " jmp 0b \n"
+ : "+c" (StdErr), "+d" (err) : "g" (StdErr->OutputString)
+ : "rax", "r8", "r9", "r10", "r11", "memory");
+
+ unreachable();
+}
bool efi_enabled(unsigned int feature)
{
@@ -175,6 +175,8 @@ void __dummy__(void)
OFFSET(MB2_tag_type, multiboot2_tag_t, type);
OFFSET(MB2_tag_size, multiboot2_tag_t, size);
OFFSET(MB2_mem_lower, multiboot2_tag_basic_meminfo_t, mem_lower);
+ OFFSET(MB2_efi64_st, multiboot2_tag_efi64_t, pointer);
+ OFFSET(MB2_efi64_ih, multiboot2_tag_efi64_ih_t, pointer);
BLANK();
OFFSET(DOMAIN_vm_assist, struct domain, vm_assist);
@@ -332,5 +332,5 @@ ASSERT(IS_ALIGNED(__init_end, PAGE_SIZE), "__init_end misaligned")
ASSERT(IS_ALIGNED(trampoline_start, 4), "trampoline_start misaligned")
ASSERT(IS_ALIGNED(trampoline_end, 4), "trampoline_end misaligned")
-ASSERT(IS_ALIGNED(__bss_start, 4), "__bss_start misaligned")
-ASSERT(IS_ALIGNED(__bss_end, 4), "__bss_end misaligned")
+ASSERT(IS_ALIGNED(__bss_start, 8), "__bss_start misaligned")
+ASSERT(IS_ALIGNED(__bss_end, 8), "__bss_end misaligned")
@@ -81,6 +81,17 @@ static bool_t match_guid(const EFI_GUID *guid1, const EFI_GUID *guid2);
static void *ebmalloc(size_t size);
+static void efi_init(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable);
+static void efi_console_set_mode(void);
+static EFI_GRAPHICS_OUTPUT_PROTOCOL *efi_get_gop(void);
+static UINTN efi_find_gop_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
+ UINTN cols, UINTN rows, UINTN depth);
+static void efi_tables(void);
+static void setup_efi_pci(void);
+static void efi_variables(void);
+static void efi_set_gop_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, UINTN gop_mode);
+static void efi_exit_boot(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable);
+
static const EFI_BOOT_SERVICES *__initdata efi_bs;
static UINT32 __initdata efi_bs_revision;
static EFI_HANDLE __initdata efi_ih;
This way Xen can be loaded on EFI platforms using GRUB2 and other boot loaders which support multiboot2 protocol. Signed-off-by: Daniel Kiper <daniel.kiper@oracle.com> --- v6 - suggestions/fixes: - improve label names in assembly error printing code (suggested by Jan Beulich), - improve comments (suggested by Jan Beulich), - various minor cleanups and fixes (suggested by Jan Beulich). v4 - suggestions/fixes: - remove redundant BSS alignment, - update BSS alignment check, - use __set_bit() instead of set_bit() if possible (suggested by Jan Beulich), - call efi_arch_cpu() from efi_multiboot2() even if the same work is done later in other place right now (suggested by Jan Beulich), - xen/arch/x86/efi/stub.c:efi_multiboot2() fail properly on EFI platforms, - do not read data beyond the end of multiboot2 information in xen/arch/x86/boot/head.S (suggested by Jan Beulich), - use 32-bit registers in x86_64 code if possible (suggested by Jan Beulich), - multiboot2 information address is 64-bit in x86_64 code, so, treat it is as is (suggested by Jan Beulich), - use cmovcc if possible, - leave only one space between rep and stosq (suggested by Jan Beulich), - improve error handling, - improve early error messages, (suggested by Jan Beulich), - improve early error messages printing code, - improve label names (suggested by Jan Beulich), - improve comments (suggested by Jan Beulich), - various minor cleanups. v3 - suggestions/fixes: - take into account alignment when skipping multiboot2 fixed part (suggested by Konrad Rzeszutek Wilk), - improve segment registers initialization (suggested by Jan Beulich), - improve comments (suggested by Jan Beulich and Konrad Rzeszutek Wilk), - improve commit message (suggested by Jan Beulich). v2 - suggestions/fixes: - generate multiboot2 header using macros (suggested by Jan Beulich), - switch CPU to x86_32 mode before jumping to 32-bit code (suggested by Andrew Cooper), - reduce code changes to increase patch readability (suggested by Jan Beulich), - improve comments (suggested by Jan Beulich), - ignore MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO tag on EFI platform and find on my own multiboot2.mem_lower value, - stop execution if EFI platform is detected in legacy BIOS path. --- xen/arch/x86/boot/head.S | 255 ++++++++++++++++++++++++++++++++++--- xen/arch/x86/efi/efi-boot.h | 49 ++++++- xen/arch/x86/efi/stub.c | 37 ++++++ xen/arch/x86/x86_64/asm-offsets.c | 2 + xen/arch/x86/xen.lds.S | 4 +- xen/common/efi/boot.c | 11 ++ 6 files changed, 336 insertions(+), 22 deletions(-)