[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v9 07/13] x86: add multiboot2 protocol support for EFI platforms



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@xxxxxxxxxx>
> ---
> 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
>


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.