Message ID | 1475185362-14198-8-git-send-email-daniel.kiper@oracle.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 29/09/16 22:42, Daniel Kiper wrote: > 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> > --- > v9 - suggestions/fixes: > - use .L labels instead of numeric ones in multiboot2 data scanning loops > (suggested by Jan Beulich). > > v8 - suggestions/fixes: > - use __bss_start(%rip)/__bss_end(%rip) instead of > of .startof.(.bss)(%rip)/$.sizeof.(.bss) because > latter is not tested extensively in different > built environments yet > (suggested by Andrew Cooper), > - fix multiboot2 data scanning loop in x86_32 code > (suggested by Jan Beulich), > - add check for extra mem for mbi data if Xen is loaded > via multiboot2 protocol on EFI platform > (suggested by Jan Beulich), > - improve comments > (suggested by Jan Beulich). > > v7 - suggestions/fixes: > - do not allocate twice memory for trampoline if we were > loaded via multiboot2 protocol on EFI platform, > - wrap long line > (suggested by Jan Beulich), > - improve comments > (suggested by Jan Beulich). > > 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 | 260 ++++++++++++++++++++++++++++++++++--- > xen/arch/x86/efi/efi-boot.h | 54 +++++++- > xen/arch/x86/efi/stub.c | 38 ++++++ > 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, 346 insertions(+), 23 deletions(-) > > diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S > index d423fd8..0155b32 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) > .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 Why is this turned into a variable? > > .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,182 @@ 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 > + > +.Lefi_mb2_tsize: > + /* 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 .Lefi_mb2_st > + > + /* > + * 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) Always use add/sub 1 in preference to inc and dec. They are the same length to encode in 64bit, and avoids a pipeline stall from a merge of the eflags register. > + jmp .Lefi_mb2_next_tag > + > +.Lefi_mb2_st: > + /* Get EFI SystemTable address from Multiboot2 information. */ > + cmpl $MULTIBOOT2_TAG_TYPE_EFI64,MB2_tag_type(%rcx) > + cmove MB2_efi64_st(%rcx),%rsi > + je .Lefi_mb2_next_tag > + > + /* Get EFI ImageHandle address from Multiboot2 information. */ > + cmpl $MULTIBOOT2_TAG_TYPE_EFI64_IH,MB2_tag_type(%rcx) > + cmove MB2_efi64_ih(%rcx),%rdi > + je .Lefi_mb2_next_tag > + > + /* Is it the end of Multiboot2 information? */ > + cmpl $MULTIBOOT2_TAG_TYPE_END,MB2_tag_type(%rcx) > + je run_bs > + > +.Lefi_mb2_next_tag: > + /* 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 .Lefi_mb2_tsize > + > +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 What is %rax being preserved for? > + 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 > + > + /* > + * 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 above this address is reserved for trampoline; > + * memory below this address is used as a storage for > + * mbi struct created in reloc(). > + * > + * 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) This would be cleaner and shorter as push $BOOT_CS32 push $cs32_switch lretq > + > + .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 +362,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,24 +374,33 @@ __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 .Lmb2_next_tag > + > + /* 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 > > +.Lmb2_next_tag: > /* Go to next Multiboot2 information tag. */ > add MB2_tag_size(%ecx),%ecx > add $(MULTIBOOT2_TAG_ALIGN-1),%ecx > and $~(MULTIBOOT2_TAG_ALIGN-1),%ecx > jmp .Lmb2_tsize > > -trampoline_setup: > +trampoline_bios_setup: > /* Set up trampoline segment 64k below EBDA */ > movzwl 0x40e,%ecx /* EBDA segment */ > cmp $0xa000,%ecx /* sanity check (high) */ > @@ -202,16 +416,19 @@ 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 */ > + /* Reserve 64kb for the trampoline. */ > sub $0x1000,%ecx > > /* From arch/x86/smpboot.c: start_eip had better be page-aligned! */ > xor %cl, %cl > + > +trampoline_setup: > + /* Save trampoline address for later use. */ > shl $4, %ecx > mov %ecx,sym_phys(trampoline_phys) > > @@ -223,7 +440,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 This should be moved earlier in the BIOS case, rather than inserting jump. It can go ahead of even the multiboot check, immediately after loading the gdt. > + > + /* Initialize BSS (no nasty surprises!). */ > mov $sym_phys(__bss_start),%edi > mov $sym_phys(__bss_end),%ecx > sub %edi,%ecx > @@ -231,6 +455,7 @@ trampoline_setup: > shr $2,%ecx > rep stosl > > +1: > /* Interrogate CPU extended features via CPUID. */ > mov $0x80000000,%eax > cpuid > @@ -282,8 +507,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) Please don't reuse skip_realmode for something unrelated. For the sake of one extra byte of data, its not worth the confusion when reading the code. ~Andrew > + jnz 1f > + > call cmdline_parse_early > > +1: > /* Switch to low-memory stack. */ > mov sym_phys(trampoline_phys),%edi > lea 0x10000(%edi),%esp >
>>> On 23.11.16 at 19:52, <andrew.cooper3@citrix.com> wrote: > On 29/09/16 22:42, Daniel Kiper wrote: >> @@ -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 > > Why is this turned into a variable? This gets zapped to zero near __efi64_start. >> +.Lefi_mb2_tsize: >> + /* 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 .Lefi_mb2_st >> + >> + /* >> + * 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) > > Always use add/sub 1 in preference to inc and dec. They are the same > length to encode in 64bit, and avoids a pipeline stall from a merge of > the eflags register. What you say regarding length not true - add/sub need to encode the immediate somewhere (even if the operand was a register, inc/dec would still be smaller than add/sub, just not by as much as in 32-bit code). And the pipeline stall, afaik, affects only rather old processors. >> +x86_32_switch: >> + cli >> + >> + /* Initialize GDTR. */ >> + lgdt gdt_boot_descr(%rip) >> + >> + /* Reload code selector. */ >> + ljmpl *cs32_switch_addr(%rip) > > This would be cleaner and shorter as > > push $BOOT_CS32 > push $cs32_switch I'm not sure: For one this would need to be $sym_phys(cs32_switch). And with that I wouldn't be certain the relocation gets expressed correctly. But yes, if it works, I too would prefer this. Jan
On Wed, Nov 23, 2016 at 06:52:27PM +0000, Andrew Cooper wrote: > On 29/09/16 22:42, Daniel Kiper wrote: [...] > > +vga_text_buffer: > > + .long 0xb8000 > > Why is this turned into a variable? We must disable VGA accesses during runtime on platforms which does not have one like e.g. EFI. That is why I have added variable here. It seems to me that it is the simplest method to do that. > > .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,182 @@ 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 > > + > > +.Lefi_mb2_tsize: > > + /* 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 .Lefi_mb2_st > > + > > + /* > > + * 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) > > Always use add/sub 1 in preference to inc and dec. They are the same > length to encode in 64bit, and avoids a pipeline stall from a merge of > the eflags register. Nice but does it pays here? I am not convinced. > > + jmp .Lefi_mb2_next_tag > > + > > +.Lefi_mb2_st: > > + /* Get EFI SystemTable address from Multiboot2 information. */ > > + cmpl $MULTIBOOT2_TAG_TYPE_EFI64,MB2_tag_type(%rcx) > > + cmove MB2_efi64_st(%rcx),%rsi > > + je .Lefi_mb2_next_tag > > + > > + /* Get EFI ImageHandle address from Multiboot2 information. */ > > + cmpl $MULTIBOOT2_TAG_TYPE_EFI64_IH,MB2_tag_type(%rcx) > > + cmove MB2_efi64_ih(%rcx),%rdi > > + je .Lefi_mb2_next_tag > > + > > + /* Is it the end of Multiboot2 information? */ > > + cmpl $MULTIBOOT2_TAG_TYPE_END,MB2_tag_type(%rcx) > > + je run_bs > > + > > +.Lefi_mb2_next_tag: > > + /* 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 .Lefi_mb2_tsize > > + > > +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 > > What is %rax being preserved for? %rax contains the Multiboot2 identifier and we need it later in reloc(). > > + 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 > > + > > + /* > > + * 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 above this address is reserved for trampoline; > > + * memory below this address is used as a storage for > > + * mbi struct created in reloc(). > > + * > > + * 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) > > This would be cleaner and shorter as > > push $BOOT_CS32 > push $cs32_switch > lretq If you wish I can try it. > > + > > + .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 +362,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,24 +374,33 @@ __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 .Lmb2_next_tag > > + > > + /* 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 > > > > +.Lmb2_next_tag: > > /* Go to next Multiboot2 information tag. */ > > add MB2_tag_size(%ecx),%ecx > > add $(MULTIBOOT2_TAG_ALIGN-1),%ecx > > and $~(MULTIBOOT2_TAG_ALIGN-1),%ecx > > jmp .Lmb2_tsize > > > > -trampoline_setup: > > +trampoline_bios_setup: > > /* Set up trampoline segment 64k below EBDA */ > > movzwl 0x40e,%ecx /* EBDA segment */ > > cmp $0xa000,%ecx /* sanity check (high) */ > > @@ -202,16 +416,19 @@ 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 */ > > + /* Reserve 64kb for the trampoline. */ > > sub $0x1000,%ecx > > > > /* From arch/x86/smpboot.c: start_eip had better be page-aligned! */ > > xor %cl, %cl > > + > > +trampoline_setup: > > + /* Save trampoline address for later use. */ > > shl $4, %ecx > > mov %ecx,sym_phys(trampoline_phys) > > > > @@ -223,7 +440,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 > > This should be moved earlier in the BIOS case, rather than inserting > jump. It can go ahead of even the multiboot check, immediately after > loading the gdt. Then later in patch #11 (x86: make Xen early boot code relocatable) we must put this code back here because otherwise it will not be relocatable. So, IMO it should stay here as is or somewhere around. > > + > > + /* Initialize BSS (no nasty surprises!). */ > > mov $sym_phys(__bss_start),%edi > > mov $sym_phys(__bss_end),%ecx > > sub %edi,%ecx > > @@ -231,6 +455,7 @@ trampoline_setup: > > shr $2,%ecx > > rep stosl > > > > +1: > > /* Interrogate CPU extended features via CPUID. */ > > mov $0x80000000,%eax > > cpuid > > @@ -282,8 +507,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) > > Please don't reuse skip_realmode for something unrelated. For the sake > of one extra byte of data, its not worth the confusion when reading the > code. If you wish no problem. Daniel
On Thu, Nov 24, 2016 at 04:08:12AM -0700, Jan Beulich wrote: > >>> On 23.11.16 at 19:52, <andrew.cooper3@citrix.com> wrote: > > On 29/09/16 22:42, Daniel Kiper wrote: [...] > >> +.Lefi_mb2_tsize: > >> + /* 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 .Lefi_mb2_st > >> + > >> + /* > >> + * 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) > > > > Always use add/sub 1 in preference to inc and dec. They are the same > > length to encode in 64bit, and avoids a pipeline stall from a merge of > > the eflags register. > > What you say regarding length not true - add/sub need to encode > the immediate somewhere (even if the operand was a register, > inc/dec would still be smaller than add/sub, just not by as much as > in 32-bit code). And the pipeline stall, afaik, affects only rather old > processors. Intel 64 and IA-32 Architectures Optimization Reference Manual, section 3.5.1.1, Use of the INC and DEC Instructions says nothing about exceptions. So, it looks that this applies to all x86 CPUs. However, as I said earlier, I am not convinced that we should consider such nuances here. incb reads better at least for me. Daniel
>>> On 24.11.16 at 22:44, <daniel.kiper@oracle.com> wrote: > On Thu, Nov 24, 2016 at 04:08:12AM -0700, Jan Beulich wrote: >> >>> On 23.11.16 at 19:52, <andrew.cooper3@citrix.com> wrote: >> > On 29/09/16 22:42, Daniel Kiper wrote: > > [...] > >> >> +.Lefi_mb2_tsize: >> >> + /* 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 .Lefi_mb2_st >> >> + >> >> + /* >> >> + * 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) >> > >> > Always use add/sub 1 in preference to inc and dec. They are the same >> > length to encode in 64bit, and avoids a pipeline stall from a merge of >> > the eflags register. >> >> What you say regarding length not true - add/sub need to encode >> the immediate somewhere (even if the operand was a register, >> inc/dec would still be smaller than add/sub, just not by as much as >> in 32-bit code). And the pipeline stall, afaik, affects only rather old >> processors. > > Intel 64 and IA-32 Architectures Optimization Reference Manual, section > 3.5.1.1, Use of the INC and DEC Instructions says nothing about exceptions. Which by itself is suspicious, as the dependency issue had been introduced only in (iirc) Pentium4. And anyway, this is a general recommendation of theirs, i.e. something to consider when intending to run well everywhere. Such general recommendation by their very nature don't consider model specific differences. > So, it looks that this applies to all x86 CPUs. However, as I said earlier, > I am not convinced that we should consider such nuances here. incb reads > better at least for me. I agree, and I hope we can get Andrew to agree too. Jan
On Fri, Nov 25, 2016 at 12:50:55AM -0700, Jan Beulich wrote: > >>> On 24.11.16 at 22:44, <daniel.kiper@oracle.com> wrote: > > On Thu, Nov 24, 2016 at 04:08:12AM -0700, Jan Beulich wrote: > >> >>> On 23.11.16 at 19:52, <andrew.cooper3@citrix.com> wrote: > >> > On 29/09/16 22:42, Daniel Kiper wrote: > > > > [...] > > > >> >> +.Lefi_mb2_tsize: > >> >> + /* 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 .Lefi_mb2_st > >> >> + > >> >> + /* > >> >> + * 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) > >> > > >> > Always use add/sub 1 in preference to inc and dec. They are the same > >> > length to encode in 64bit, and avoids a pipeline stall from a merge of > >> > the eflags register. > >> > >> What you say regarding length not true - add/sub need to encode > >> the immediate somewhere (even if the operand was a register, > >> inc/dec would still be smaller than add/sub, just not by as much as > >> in 32-bit code). And the pipeline stall, afaik, affects only rather old > >> processors. > > > > Intel 64 and IA-32 Architectures Optimization Reference Manual, section > > 3.5.1.1, Use of the INC and DEC Instructions says nothing about exceptions. > > Which by itself is suspicious, as the dependency issue had been > introduced only in (iirc) Pentium4. And anyway, this is a general Hmmm... Interesting... It looks that INC/DEC behavior has not changed since the beginning (why it would change? It would not make sense). Though my oldest book about 80286, 80386 and i486 CPUS (printed in 1991 and it is based on eighties Intel documentation) says nothing about such issues. So, I have a feeling that this issue appeared (or it existed earlier but it was not documented minor problem) when x86 implementation changed from 100% in silicon to mostly (???) microcode. IIRC, it happened around Pentium but I am not sure. Daniel
>>> On 30.11.16 at 14:45, <daniel.kiper@oracle.com> wrote: > On Fri, Nov 25, 2016 at 12:50:55AM -0700, Jan Beulich wrote: >> >>> On 24.11.16 at 22:44, <daniel.kiper@oracle.com> wrote: >> > On Thu, Nov 24, 2016 at 04:08:12AM -0700, Jan Beulich wrote: >> >> >>> On 23.11.16 at 19:52, <andrew.cooper3@citrix.com> wrote: >> >> > Always use add/sub 1 in preference to inc and dec. They are the same >> >> > length to encode in 64bit, and avoids a pipeline stall from a merge of >> >> > the eflags register. >> >> >> >> What you say regarding length not true - add/sub need to encode >> >> the immediate somewhere (even if the operand was a register, >> >> inc/dec would still be smaller than add/sub, just not by as much as >> >> in 32-bit code). And the pipeline stall, afaik, affects only rather old >> >> processors. >> > >> > Intel 64 and IA-32 Architectures Optimization Reference Manual, section >> > 3.5.1.1, Use of the INC and DEC Instructions says nothing about exceptions. >> >> Which by itself is suspicious, as the dependency issue had been >> introduced only in (iirc) Pentium4. And anyway, this is a general > > Hmmm... Interesting... It looks that INC/DEC behavior has not changed since > the beginning (why it would change? It would not make sense). Please properly disambiguate "behavior": Architectural behavior of course can't change. Performance, otoh, has changed many times. And the resource dependency issue did appear only once the pipelining of instructions was sophisticated enough, but not good enough yet to track (as a dependency) EFLAGS.CF separately from the other arithmetic result flags. Jan
On Wed, Nov 30, 2016 at 06:59:57AM -0700, Jan Beulich wrote: > >>> On 30.11.16 at 14:45, <daniel.kiper@oracle.com> wrote: > > On Fri, Nov 25, 2016 at 12:50:55AM -0700, Jan Beulich wrote: > >> >>> On 24.11.16 at 22:44, <daniel.kiper@oracle.com> wrote: > >> > On Thu, Nov 24, 2016 at 04:08:12AM -0700, Jan Beulich wrote: > >> >> >>> On 23.11.16 at 19:52, <andrew.cooper3@citrix.com> wrote: > >> >> > Always use add/sub 1 in preference to inc and dec. They are the same > >> >> > length to encode in 64bit, and avoids a pipeline stall from a merge of > >> >> > the eflags register. > >> >> > >> >> What you say regarding length not true - add/sub need to encode > >> >> the immediate somewhere (even if the operand was a register, > >> >> inc/dec would still be smaller than add/sub, just not by as much as > >> >> in 32-bit code). And the pipeline stall, afaik, affects only rather old > >> >> processors. > >> > > >> > Intel 64 and IA-32 Architectures Optimization Reference Manual, section > >> > 3.5.1.1, Use of the INC and DEC Instructions says nothing about exceptions. > >> > >> Which by itself is suspicious, as the dependency issue had been > >> introduced only in (iirc) Pentium4. And anyway, this is a general > > > > Hmmm... Interesting... It looks that INC/DEC behavior has not changed since > > the beginning (why it would change? It would not make sense). > > Please properly disambiguate "behavior": Architectural behavior of > course can't change. Performance, otoh, has changed many times. Right! > And the resource dependency issue did appear only once the > pipelining of instructions was sophisticated enough, but not good > enough yet to track (as a dependency) EFLAGS.CF separately from > the other arithmetic result flags. OK, make sense right now. Thanks for explanation. Daniel
diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S index d423fd8..0155b32 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) .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,182 @@ 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 + +.Lefi_mb2_tsize: + /* 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 .Lefi_mb2_st + + /* + * 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 .Lefi_mb2_next_tag + +.Lefi_mb2_st: + /* Get EFI SystemTable address from Multiboot2 information. */ + cmpl $MULTIBOOT2_TAG_TYPE_EFI64,MB2_tag_type(%rcx) + cmove MB2_efi64_st(%rcx),%rsi + je .Lefi_mb2_next_tag + + /* Get EFI ImageHandle address from Multiboot2 information. */ + cmpl $MULTIBOOT2_TAG_TYPE_EFI64_IH,MB2_tag_type(%rcx) + cmove MB2_efi64_ih(%rcx),%rdi + je .Lefi_mb2_next_tag + + /* Is it the end of Multiboot2 information? */ + cmpl $MULTIBOOT2_TAG_TYPE_END,MB2_tag_type(%rcx) + je run_bs + +.Lefi_mb2_next_tag: + /* 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 .Lefi_mb2_tsize + +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 __bss_start(%rip),%edi + lea __bss_end(%rip),%ecx + sub %edi,%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 above this address is reserved for trampoline; + * memory below this address is used as a storage for + * mbi struct created in reloc(). + * + * 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 +362,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,24 +374,33 @@ __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 .Lmb2_next_tag + + /* 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 +.Lmb2_next_tag: /* Go to next Multiboot2 information tag. */ add MB2_tag_size(%ecx),%ecx add $(MULTIBOOT2_TAG_ALIGN-1),%ecx and $~(MULTIBOOT2_TAG_ALIGN-1),%ecx jmp .Lmb2_tsize -trampoline_setup: +trampoline_bios_setup: /* Set up trampoline segment 64k below EBDA */ movzwl 0x40e,%ecx /* EBDA segment */ cmp $0xa000,%ecx /* sanity check (high) */ @@ -202,16 +416,19 @@ 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 */ + /* Reserve 64kb for the trampoline. */ sub $0x1000,%ecx /* From arch/x86/smpboot.c: start_eip had better be page-aligned! */ xor %cl, %cl + +trampoline_setup: + /* Save trampoline address for later use. */ shl $4, %ecx mov %ecx,sym_phys(trampoline_phys) @@ -223,7 +440,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 +455,7 @@ trampoline_setup: shr $2,%ecx rep stosl +1: /* Interrogate CPU extended features via CPUID. */ mov $0x80000000,%eax cpuid @@ -282,8 +507,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 diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h index 62c010e..dc857d8 100644 --- a/xen/arch/x86/efi/efi-boot.h +++ b/xen/arch/x86/efi/efi-boot.h @@ -146,6 +146,8 @@ static void __init efi_arch_process_memory_map(EFI_SYSTEM_TABLE *SystemTable, { struct e820entry *e; unsigned int i; + /* Check for extra mem for mbi data if Xen is loaded via multiboot2 protocol. */ + UINTN extra_mem = efi_enabled(EFI_LOADER) ? 0 : (64 << 10); /* Populate E820 table and check trampoline area availability. */ e = e820map - 1; @@ -168,7 +170,8 @@ static void __init efi_arch_process_memory_map(EFI_SYSTEM_TABLE *SystemTable, /* fall through */ case EfiConventionalMemory: if ( !trampoline_phys && desc->PhysicalStart + len <= 0x100000 && - len >= cfg.size && desc->PhysicalStart + len > cfg.addr ) + len >= cfg.size + extra_mem && + desc->PhysicalStart + len > cfg.addr ) cfg.addr = (desc->PhysicalStart + len - cfg.size) & PAGE_MASK; /* fall through */ case EfiLoaderCode: @@ -210,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) @@ -653,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 diff --git a/xen/arch/x86/efi/stub.c b/xen/arch/x86/efi/stub.c index 4158124..6ea6aa1 100644 --- a/xen/arch/x86/efi/stub.c +++ b/xen/arch/x86/efi/stub.c @@ -3,6 +3,44 @@ #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 may + * not be directly supported by C compiler. + */ + 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) { diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c index b437a8f..2a22659 100644 --- a/xen/arch/x86/x86_64/asm-offsets.c +++ b/xen/arch/x86/x86_64/asm-offsets.c @@ -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); diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S index b0b1c9b..e0e2529 100644 --- a/xen/arch/x86/xen.lds.S +++ b/xen/arch/x86/xen.lds.S @@ -331,5 +331,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") diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c index 46559f0..6e2cc23 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;
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> --- v9 - suggestions/fixes: - use .L labels instead of numeric ones in multiboot2 data scanning loops (suggested by Jan Beulich). v8 - suggestions/fixes: - use __bss_start(%rip)/__bss_end(%rip) instead of of .startof.(.bss)(%rip)/$.sizeof.(.bss) because latter is not tested extensively in different built environments yet (suggested by Andrew Cooper), - fix multiboot2 data scanning loop in x86_32 code (suggested by Jan Beulich), - add check for extra mem for mbi data if Xen is loaded via multiboot2 protocol on EFI platform (suggested by Jan Beulich), - improve comments (suggested by Jan Beulich). v7 - suggestions/fixes: - do not allocate twice memory for trampoline if we were loaded via multiboot2 protocol on EFI platform, - wrap long line (suggested by Jan Beulich), - improve comments (suggested by Jan Beulich). 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 | 260 ++++++++++++++++++++++++++++++++++--- xen/arch/x86/efi/efi-boot.h | 54 +++++++- xen/arch/x86/efi/stub.c | 38 ++++++ 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, 346 insertions(+), 23 deletions(-)