@@ -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,19 +107,45 @@ 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
+
+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 0f
not_multiboot:
mov $(sym_phys(.Lbad_ldr_msg)),%esi # Error message
-print_err:
- mov $0xB8000,%edi # VGA framebuffer
+ jmp 0f
+mb2_no_st:
+ mov $(sym_phys(.Lbad_ldr_nst)),%esi # Error message
+ jmp 0f
+mb2_no_ih:
+ mov $(sym_phys(.Lbad_ldr_nih)),%esi # Error message
+ jmp 0f
+mb2_no_bs:
+ mov $(sym_phys(.Lbad_ldr_nbs)),%esi # Error message
+ xor %edi,%edi # No VGA text buffer
+ jmp 1f
+mb2_efi_ia_32:
+ mov $(sym_phys(.Lbad_efi_msg)),%esi # Error message
+ xor %edi,%edi # No VGA text buffer
+ jmp 1f
+0: mov sym_phys(vga_text_buffer),%edi
1: mov (%esi),%bl
test %bl,%bl # Terminate on '\0' sentinel
je .Lhalt
@@ -123,13 +156,173 @@ 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 1b
+ movsb # Write a character to the VGA text buffer
mov $7,%al
- stosb # Write an attribute to the VGA framebuffer
+ stosb # Write an attribute to the VGA text buffer
jmp 1b
.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)(%ebx),%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, skip real mode and do not do other unneeded things. */
+ 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 mb2_no_bs after switching CPU to x86_32 mode. */
+ lea mb2_no_bs(%rip),%edi
+ jmp x86_32_switch
+
+0:
+ /* Is EFI SystemTable address provided by boot loader? */
+ test %rsi,%rsi
+ jnz 1f
+
+ /* Jump to mb2_no_st after switching CPU to x86_32 mode. */
+ lea mb2_no_st(%rip),%edi
+ jmp x86_32_switch
+
+1:
+ /* Is EFI ImageHandle address provided by boot loader? */
+ test %rdi,%rdi
+ jnz 2f
+
+ /* Jump to mb2_no_ih after switching CPU to x86_32 mode. */
+ lea mb2_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 __bss_start(%rip),%edi
+ lea __bss_end(%rip),%ecx
+ sub %edi,%ecx
+ shr $3,%ecx
+ xor %eax,%eax
+ rep stosq
+
+ pop %rdi
+
+ /*
+ * IN: %rdi - EFI ImageHandle, %rsi - EFI SystemTable.
+ * OUT: %rax - Highest available memory address below 1 MiB.
+ *
+ * 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
+
+ /* Initialise GDT. */
+ lgdt gdt_boot_descr(%rip)
+
+ /* Reload code selector. */
+ ljmpl *cs32_switch_addr(%rip)
+
+ .code32
+
+cs32_switch:
+ /* Initialise basic data segments. */
+ mov $BOOT_DS,%edx
+ mov %edx,%ds
+ mov %edx,%es
+ mov %edx,%ss
+ /* %esp is initialised 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 +350,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 +362,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 mb2_efi_ia_32
+
+ /* Bootloader shutdown EFI x64 boot services. */
+ cmpl $MULTIBOOT2_TAG_TYPE_EFI64,MB2_tag_type(%ecx)
+ je mb2_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 +387,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 +403,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,14 +425,22 @@ 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
- xor %eax,%eax
shr $2,%ecx
+ xor %eax,%eax
rep stosl
+1:
/* Interrogate CPU extended features via CPUID. */
mov $0x80000000,%eax
cpuid
@@ -282,8 +492,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
@@ -248,12 +248,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)
@@ -687,6 +689,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 available memory address below 1 MiB. */
+ return cfg.addr;
+}
+
/*
* Local variables:
* mode: C
@@ -3,6 +3,30 @@
#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>
+
+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. */
+ asm volatile(
+ " call %2 \n"
+ "0: hlt \n"
+ " jmp 0b \n"
+ : "+c" (StdErr), "+d" (err) : "g" (StdErr->OutputString)
+ : "rax", "r8", "r9", "r10", "r11", "cc", "memory");
+
+ unreachable();
+}
void __init free_ebmalloc_unused_mem(void)
{
@@ -727,7 +727,7 @@ void __init noreturn __start_xen(unsigned long mbi_p)
if ( !(mbi->flags & MBI_MODULES) || (mbi->mods_count == 0) )
panic("dom0 kernel not specified. Check bootloader configuration.");
- if ( efi_enabled(EFI_BOOT) )
+ if ( efi_enabled(EFI_LOADER) )
{
set_pdx_range(xen_phys_start >> PAGE_SHIFT,
(xen_phys_start + BOOTSTRAP_MAP_BASE) >> PAGE_SHIFT);
@@ -740,8 +740,10 @@ void __init noreturn __start_xen(unsigned long mbi_p)
l3_bootmap[l3_table_offset(BOOTSTRAP_MAP_BASE)] =
l3e_from_paddr(__pa(l2_bootmap), __PAGE_HYPERVISOR);
- memmap_type = loader;
+ memmap_type = "EFI";
}
+ else if ( efi_enabled(EFI_BOOT) )
+ memmap_type = "EFI";
else if ( e820_raw_nr != 0 )
{
memmap_type = "Xen-e820";
@@ -838,7 +840,7 @@ void __init noreturn __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; !efi_enabled(EFI_BOOT) && i < mbi->mods_count; i++ )
+ for ( i = 0; !efi_enabled(EFI_LOADER) && i < mbi->mods_count; i++ )
{
if ( mod[i].mod_start & (PAGE_SIZE - 1) )
panic("Bootloader didn't honor module alignment request.");
@@ -176,6 +176,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);
@@ -324,5 +324,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")
@@ -79,6 +79,17 @@ static size_t wstrlen(const CHAR16 * s);
static int set_color(u32 mask, int bpp, u8 *pos, u8 *sz);
static bool_t match_guid(const EFI_GUID *guid1, const EFI_GUID *guid2);
+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;
@@ -935,6 +946,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
bool_t use_cfg_file;
__set_bit(EFI_BOOT, &efi.flags);
+ __set_bit(EFI_LOADER, &efi.flags);
#ifndef CONFIG_ARM /* Disabled until runtime services implemented. */
__set_bit(EFI_RS, &efi.flags);
@@ -12,6 +12,7 @@
struct efi {
unsigned long flags; /* Bit fields representing available EFI features/properties */
#define EFI_BOOT 0 /* Were we booted from EFI? */
+#define EFI_LOADER 1 /* Were we booted directly from EFI loader? */
#define EFI_RS 2 /* Can we use runtime services? */
unsigned long mps; /* MPS table */
unsigned long acpi; /* ACPI table (IA64 ext 0.71) */
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> --- 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 | 243 ++++++++++++++++++++++++++++++++++--- xen/arch/x86/efi/efi-boot.h | 49 +++++++- xen/arch/x86/efi/stub.c | 24 ++++ xen/arch/x86/setup.c | 8 +- xen/arch/x86/x86_64/asm-offsets.c | 2 + xen/arch/x86/xen.lds.S | 4 +- xen/common/efi/boot.c | 12 ++ xen/include/xen/efi.h | 1 + 8 files changed, 319 insertions(+), 24 deletions(-)