[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v3 13/16 - RFC] x86: add multiboot2 protocol support for EFI platforms
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@xxxxxxxxxx> --- 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). v3 - not fixed yet: - xen/arch/x86/efi/stub.c:efi_multiboot2() should print error message and halt system. 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 | 177 +++++++++++++++++++++++++++++++++++-- xen/arch/x86/efi/efi-boot.h | 43 +++++++++ xen/arch/x86/efi/stub.c | 5 ++ xen/arch/x86/setup.c | 10 ++- 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, 240 insertions(+), 14 deletions(-) diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S index e46d691..efb0614 100644 --- a/xen/arch/x86/boot/head.S +++ b/xen/arch/x86/boot/head.S @@ -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) multiboot2_header_end: @@ -100,19 +107,29 @@ multiboot2_header_end: 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 .Lbad_cpu_msg: .asciz "ERR: Not a 64-bit CPU!" .Lbad_ldr_msg: .asciz "ERR: Not a Multiboot bootloader!" +.Lbad_ldr_mb2: .asciz "ERR: On EFI platform use latest Multiboot2 compatible bootloader!" .section .init.text, "ax", @progbits bad_cpu: mov $(sym_phys(.Lbad_cpu_msg)),%esi # Error message - jmp print_err + mov $0xB8000,%edi # VGA framebuffer + jmp 1f not_multiboot: mov $(sym_phys(.Lbad_ldr_msg)),%esi # Error message -print_err: - mov $0xB8000,%edi # VGA framebuffer + mov $0xB8000,%edi # VGA framebuffer + jmp 1f +mb2_too_old: + mov $(sym_phys(.Lbad_ldr_mb2)),%esi # Error message + xor %edi,%edi # No VGA framebuffer 1: mov (%esi),%bl test %bl,%bl # Terminate on '\0' sentinel je .Lhalt @@ -123,6 +140,8 @@ print_err: mov $0x3f8+0,%dx # UART Transmit Holding Register mov %bl,%al out %al,%dx # Send a character over the serial line + test %edi,%edi # Is VGA framebuffer available? + jz 1b movsb # Write a character to the VGA framebuffer mov $7,%al stosb # Write an attribute to the VGA framebuffer @@ -130,6 +149,130 @@ print_err: .Lhalt: hlt jmp .Lhalt + .code64 + +__efi64_start: + cld + + /* Check for Multiboot2 bootloader. */ + cmp $MULTIBOOT2_BOOTLOADER_MAGIC,%eax + je efi_multiboot2_proto + + /* Jump to not_multiboot after switching CPU to x86_32 mode. */ + lea not_multiboot(%rip),%rdi + jmp x86_32_switch + +efi_multiboot2_proto: + /* + * Multiboot2 information address is 32-bit, + * so, zero higher half of %rbx. + */ + mov %ebx,%ebx + + /* Skip Multiboot2 information fixed part. */ + lea (MB2_fixed_sizeof+MULTIBOOT2_TAG_ALIGN-1)(%rbx),%rcx + and $~(MULTIBOOT2_TAG_ALIGN-1),%rcx + +0: + /* Get EFI SystemTable address from Multiboot2 information. */ + cmpl $MULTIBOOT2_TAG_TYPE_EFI64,MB2_tag_type(%rcx) + jne 1f + + mov MB2_efi64_st(%rcx),%rsi + + /* Do not clear BSS twice and do not go into real mode. */ + movb $1,skip_realmode(%rip) + jmp 3f + +1: + /* Get EFI ImageHandle address from Multiboot2 information. */ + cmpl $MULTIBOOT2_TAG_TYPE_EFI64_IH,MB2_tag_type(%rcx) + jne 2f + + mov MB2_efi64_ih(%rcx),%rdi + jmp 3f + +2: + /* Is it the end of Multiboot2 information? */ + cmpl $MULTIBOOT2_TAG_TYPE_END,MB2_tag_type(%rcx) + je run_bs + +3: + /* Go to next Multiboot2 information tag. */ + add MB2_tag_size(%rcx),%ecx + add $(MULTIBOOT2_TAG_ALIGN-1),%rcx + and $~(MULTIBOOT2_TAG_ALIGN-1),%rcx + jmp 0b + +run_bs: + 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),%rdi + lea __bss_end(%rip),%rcx + sub %rdi,%rcx + shr $3,%rcx + 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,%rax + mov %rax,%rcx + + pop %rax + + /* Jump to trampoline_setup after switching CPU to x86_32 mode. */ + lea trampoline_setup(%rip),%rdi + +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 +300,7 @@ __start: /* Not available? BDA value will be fine. */ cmovnz MB_mem_lower(%ebx),%edx - jmp trampoline_setup + jmp trampoline_bios_setup multiboot2_proto: /* Skip Multiboot2 information fixed part. */ @@ -170,12 +313,19 @@ multiboot2_proto: jne 1f mov MB2_mem_lower(%ecx),%edx - jmp trampoline_setup + jmp trampoline_bios_setup 1: + /* EFI mode is not supported via legacy BIOS path. */ + cmpl $MULTIBOOT2_TAG_TYPE_EFI32,MB2_tag_type(%ecx) + je mb2_too_old + + cmpl $MULTIBOOT2_TAG_TYPE_EFI64,MB2_tag_type(%ecx) + je mb2_too_old + /* 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 @@ -183,7 +333,7 @@ multiboot2_proto: 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) */ @@ -199,12 +349,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! */ @@ -221,6 +372,13 @@ trampoline_setup: add $12,%esp /* Remove reloc() args from stack. */ mov %eax,sym_phys(multiboot_ptr) + /* + * Do not zero BSS on EFI platform here. + * It was initialized earlier. + */ + cmpb $1,sym_phys(skip_realmode) + je 1f + /* Initialize BSS (no nasty surprises!). */ mov $sym_phys(__bss_start),%edi mov $sym_phys(__bss_end),%ecx @@ -229,6 +387,7 @@ trampoline_setup: xor %eax,%eax rep stosl +1: /* Interrogate CPU extended features via CPUID. */ mov $0x80000000,%eax cpuid diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h index 84afffa..b311b7c 100644 --- a/xen/arch/x86/efi/efi-boot.h +++ b/xen/arch/x86/efi/efi-boot.h @@ -228,6 +228,9 @@ static void *__init efi_arch_allocate_mmap_buffer(UINTN map_size) static void __init efi_arch_pre_exit_boot(void) { + if ( !efi_enabled(EFI_LOADER) ) + return; + if ( !trampoline_phys ) { if ( !cfg.addr ) @@ -665,6 +668,46 @@ 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_PLATFORM, &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() is not needed here. boot_cpu_data + * is set later in xen/arch/x86/boot/head.S. + */ + + 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 diff --git a/xen/arch/x86/efi/stub.c b/xen/arch/x86/efi/stub.c index c5ae369..d30fe89 100644 --- a/xen/arch/x86/efi/stub.c +++ b/xen/arch/x86/efi/stub.c @@ -13,6 +13,11 @@ struct efi __read_mostly efi = { .smbios3 = EFI_INVALID_TABLE_ADDR }; +void __init efi_multiboot2(void) +{ + /* TODO: Fail if entered! */ +} + void __init efi_init_memory(void) { } void efi_update_l4_pgtable(unsigned int l4idx, l4_pgentry_t l4e) { } diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index 4eb8572..21bbe6a 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -715,7 +715,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_PLATFORM) ) + if ( efi_enabled(EFI_LOADER) ) { set_pdx_range(xen_phys_start >> PAGE_SHIFT, (xen_phys_start + BOOTSTRAP_MAP_BASE) >> PAGE_SHIFT); @@ -728,7 +728,11 @@ 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_PLATFORM) ) + { + memmap_type = "EFI"; } else if ( e820_raw_nr != 0 ) { @@ -826,7 +830,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_PLATFORM) && 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."); diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c index b926082..b7aed49 100644 --- a/xen/arch/x86/x86_64/asm-offsets.c +++ b/xen/arch/x86/x86_64/asm-offsets.c @@ -173,4 +173,6 @@ 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); } diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S index 6376bfa..fa1da37 100644 --- a/xen/arch/x86/xen.lds.S +++ b/xen/arch/x86/xen.lds.S @@ -192,7 +192,7 @@ SECTIONS } :text /* Align BSS to speedup its initialization. */ - . = ALIGN(4); + . = ALIGN(8); .bss : { /* BSS */ . = ALIGN(STACK_SIZE); __bss_start = .; @@ -207,7 +207,7 @@ SECTIONS *(.bss.percpu.read_mostly) . = ALIGN(SMP_CACHE_BYTES); __per_cpu_data_end = .; - . = ALIGN(4); + . = ALIGN(8); __bss_end = .; } :text _end = . ; diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c index d10c0ab..129512f 100644 --- a/xen/common/efi/boot.c +++ b/xen/common/efi/boot.c @@ -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; @@ -936,6 +947,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) #ifndef CONFIG_ARM /* Disabled until runtime services implemented. */ set_bit(EFI_PLATFORM, &efi.flags); + set_bit(EFI_LOADER, &efi.flags); #endif efi_init(ImageHandle, SystemTable); diff --git a/xen/include/xen/efi.h b/xen/include/xen/efi.h index 659c7c4..7b0a7ca 100644 --- a/xen/include/xen/efi.h +++ b/xen/include/xen/efi.h @@ -9,6 +9,7 @@ #define EFI_INVALID_TABLE_ADDR (~0UL) #define EFI_PLATFORM 0 +#define EFI_LOADER 1 /* Add fields here only if they need to be referenced from non-EFI code. */ struct efi { -- 1.7.10.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |