diff mbox

[v5,13/16] x86: add multiboot2 protocol support for EFI platforms

Message ID 1471646606-28519-14-git-send-email-daniel.kiper@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel Kiper Aug. 19, 2016, 10:43 p.m. UTC
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/x86_64/asm-offsets.c |    2 +
 xen/arch/x86/xen.lds.S            |    4 +-
 xen/common/efi/boot.c             |   11 ++
 6 files changed, 312 insertions(+), 21 deletions(-)

Comments

Jan Beulich Aug. 25, 2016, 12:59 p.m. UTC | #1
>>> On 20.08.16 at 00:43, <daniel.kiper@oracle.com> wrote:
> @@ -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

This now ends up being misaligned. Not a big deal, but anyway.

>          .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

All the labels you add should imo be .L ones. And the two numeric
labels, considering their use across other label boundaries, would
perhaps better become .L ones too.

> +__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

Does the boot loader, btw, make any guarantees towards placing
the image below 4G?

> +.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

As indicated before - please use %rbx here; there's no need for
having a pointless address size prefix.

> +        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

I think this comment need extending: Why again is not skipping
real mode fine if there's no such tag, no matter that we got called
in 64-bit mode here? Actually, looking further down, wasn't it that
you simply abuse that label (you jump to an error label below when
it is still zero)? That needs to be said here if so, for someone
reading the code in order to understand that there's no actual use
of real mode anywhere on this path.

> +        /*
> +         * 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

In assembly code please use .startof.(.bss) and .sizeof.(.bss), as
being propose for other uses by
https://lists.xenproject.org/archives/html/xen-devel/2016-08/msg02718.html

> +        /*
> +         * IN: %rdi - EFI ImageHandle, %rsi - EFI SystemTable.
> +         * OUT: %rax - Highest available memory address below 1 MiB.

Please be more precise, as "available" leaves open how much space
is actually available there, and hence what this can be used for. Also
according to ...

> +         * 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

... the output really is in %eax.

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

Please avoid pointless code movement like this.

> +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;

Where is it being made certain that there are 64k of space available
right below this address, as is being assumed at trampoline_setup?

> --- a/xen/arch/x86/efi/stub.c
> +++ b/xen/arch/x86/efi/stub.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");

There are explanations missing here: First, a warning should be added
alongside the EFI header inclusions, making clear that no services
whatsoever can be called. And then the asm() here needs to explain
that it open codes an MS-ABI function call. Which then makes me
wonder (even if it doesn't matter much) - are the clobbers actually
correct? I think you also need to clobber rsi and rdi. Otoh there's no
need for an explicit "cc" clobber on x86.

Jan
Daniel Kiper Aug. 30, 2016, 7:32 p.m. UTC | #2
On Thu, Aug 25, 2016 at 06:59:54AM -0600, Jan Beulich wrote:
> >>> On 20.08.16 at 00:43, <daniel.kiper@oracle.com> wrote:
> > @@ -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
>
> This now ends up being misaligned. Not a big deal, but anyway.
>
> >          .section .init.text, "ax", @progbits

I hope that ".align 4" is sufficient here.

> >  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
>
> All the labels you add should imo be .L ones. And the two numeric
> labels, considering their use across other label boundaries, would
> perhaps better become .L ones too.

OK.

> > +__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
>
> Does the boot loader, btw, make any guarantees towards placing
> the image below 4G?

Yep.

[...]

> > +        /*
> > +         * IN: %rdi - EFI ImageHandle, %rsi - EFI SystemTable.
> > +         * OUT: %rax - Highest available memory address below 1 MiB.
>
> Please be more precise, as "available" leaves open how much space
> is actually available there, and hence what this can be used for. Also
> according to ...
>
> > +         * 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
>
> ... the output really is in %eax.

efi_multiboot2() is called according to System V AMD64 ABI. So, according to it
return value is stored in %rax. Hence, from spec point of view comment is correct.
Use of %eax is a bit different thing (our need/choice) and if you wish it can be
documented accordingly.

> > @@ -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
>
> Please avoid pointless code movement like this.

I think that shr should be close to sub because both operations are related
to some extent. However, you are right that maybe this cleanup should be in
separate patch. Does it make sense for you?

> > +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;
>
> Where is it being made certain that there are 64k of space available
> right below this address, as is being assumed at trampoline_setup?

You are right. This is a bug. However, the problem is more generic
and should be fixed for all boot cases (BIOS, EFI loader and EFI with
GRUB2). Additionally, in case of BIOS and EFI with GRUB2 we should
check that it is possible to put all data from grub loader in low
memory. So, it looks that there is a place for at least two (three?)
additional patches. Do you want them at the beginning of or the end
of this patch series.

> > --- a/xen/arch/x86/efi/stub.c
> > +++ b/xen/arch/x86/efi/stub.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");
>
> There are explanations missing here: First, a warning should be added
> alongside the EFI header inclusions, making clear that no services
> whatsoever can be called. And then the asm() here needs to explain

I am not convinced but if you wish...

> that it open codes an MS-ABI function call. Which then makes me

OK.

> wonder (even if it doesn't matter much) - are the clobbers actually
> correct? I think you also need to clobber rsi and rdi. Otoh there's no

Nope, MS-ABI says that %rsi and %rdi must be saved and restored by
function which uses them. So, we do not need list both registers
in clobbers here.

> need for an explicit "cc" clobber on x86.

Why?

Daniel
Jan Beulich Aug. 31, 2016, 12:49 p.m. UTC | #3
>>> On 30.08.16 at 21:32, <daniel.kiper@oracle.com> wrote:
> On Thu, Aug 25, 2016 at 06:59:54AM -0600, Jan Beulich wrote:
>> >>> On 20.08.16 at 00:43, <daniel.kiper@oracle.com> wrote:
>> > @@ -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
>>
>> Please avoid pointless code movement like this.
> 
> I think that shr should be close to sub because both operations are related
> to some extent. However, you are right that maybe this cleanup should be in
> separate patch. Does it make sense for you?

Separating dependent instructions by other unrelated ones can
actually improve performance. I agree that performance isn't of
much relevance here, but I think I still wouldn't agree to a change
like this even if submitted separately.

>> > +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;
>>
>> Where is it being made certain that there are 64k of space available
>> right below this address, as is being assumed at trampoline_setup?
> 
> You are right. This is a bug. However, the problem is more generic
> and should be fixed for all boot cases (BIOS, EFI loader and EFI with
> GRUB2). Additionally, in case of BIOS and EFI with GRUB2 we should
> check that it is possible to put all data from grub loader in low
> memory. So, it looks that there is a place for at least two (three?)
> additional patches. Do you want them at the beginning of or the end
> of this patch series.

If you think there are bugs in pre-existing code, then for having the
option of backporting putting such patches at the beginning would
be desirable. That said, I'm not convinced there are problems really
in need of fixing here (yet the grub environment is different and
hence will need taking care of even if we leave the other code paths
as they are now).

>> > --- a/xen/arch/x86/efi/stub.c
>> > +++ b/xen/arch/x86/efi/stub.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");
>>
>> There are explanations missing here: First, a warning should be added
>> alongside the EFI header inclusions, making clear that no services
>> whatsoever can be called. And then the asm() here needs to explain
> 
> I am not convinced but if you wish...

Not convinced of what?

>> that it open codes an MS-ABI function call. Which then makes me
> 
> OK.
> 
>> wonder (even if it doesn't matter much) - are the clobbers actually
>> correct? I think you also need to clobber rsi and rdi. Otoh there's no
> 
> Nope, MS-ABI says that %rsi and %rdi must be saved and restored by
> function which uses them. So, we do not need list both registers
> in clobbers here.

Oh, right, I manage to mix up calling conventions.

>> need for an explicit "cc" clobber on x86.
> 
> Why?

Because such a clobber gets added to every asm() by the compiler,
unless it uses the (new in gcc 6) flag output. I've actually suggested
to upstream a patch making it possible to avoid that automatic
addition, but there hadn't been a whole lot of useful feedback.

Jan
Daniel Kiper Aug. 31, 2016, 5:07 p.m. UTC | #4
On Wed, Aug 31, 2016 at 06:49:51AM -0600, Jan Beulich wrote:
> >>> On 30.08.16 at 21:32, <daniel.kiper@oracle.com> wrote:
> > On Thu, Aug 25, 2016 at 06:59:54AM -0600, Jan Beulich wrote:
> >> >>> On 20.08.16 at 00:43, <daniel.kiper@oracle.com> wrote:

[...]

> >> > +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;
> >>
> >> Where is it being made certain that there are 64k of space available
> >> right below this address, as is being assumed at trampoline_setup?
> >
> > You are right. This is a bug. However, the problem is more generic
> > and should be fixed for all boot cases (BIOS, EFI loader and EFI with
> > GRUB2). Additionally, in case of BIOS and EFI with GRUB2 we should
> > check that it is possible to put all data from grub loader in low
> > memory. So, it looks that there is a place for at least two (three?)
> > additional patches. Do you want them at the beginning of or the end
> > of this patch series.
>
> If you think there are bugs in pre-existing code, then for having the
> option of backporting putting such patches at the beginning would
> be desirable. That said, I'm not convinced there are problems really
> in need of fixing here (yet the grub environment is different and
> hence will need taking care of even if we leave the other code paths
> as they are now).

I think that we should no blindly assume that a given amount of memory
is available. However, we do that in a few places including this one which
you pointed out. So, it should be fixed. Though, the question is: Is it
possible? Right now I am not sure that it is true in all cases mentioned
above. There is a chance that in case pointed out by you it is. Anyway,
I will check it.

> >> > --- a/xen/arch/x86/efi/stub.c
> >> > +++ b/xen/arch/x86/efi/stub.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");
> >>
> >> There are explanations missing here: First, a warning should be added
> >> alongside the EFI header inclusions, making clear that no services
> >> whatsoever can be called. And then the asm() here needs to explain
> >
> > I am not convinced but if you wish...
>
> Not convinced of what?

About "... a warning should be added alongside the EFI header inclusions,
making clear that no services whatsoever can be called". AIUI, "warning" ==
"comment" here. However, I think that everybody who reads this file is
aware that "no services whatsoever can be called". So, I am not sure
where is the point.

[...]

> >> need for an explicit "cc" clobber on x86.
> >
> > Why?
>
> Because such a clobber gets added to every asm() by the compiler,
> unless it uses the (new in gcc 6) flag output. I've actually suggested
> to upstream a patch making it possible to avoid that automatic
> addition, but there hadn't been a whole lot of useful feedback.

So, when somebody uses this new flag then "cc" will not be add here.
This is not big deal but I think that extra "cc" clobbers does not
hurt too.

Daniel
Jan Beulich Sept. 1, 2016, 7:40 a.m. UTC | #5
>>> On 31.08.16 at 19:07, <daniel.kiper@oracle.com> wrote:
> On Wed, Aug 31, 2016 at 06:49:51AM -0600, Jan Beulich wrote:
>> >>> On 30.08.16 at 21:32, <daniel.kiper@oracle.com> wrote:
>> > On Thu, Aug 25, 2016 at 06:59:54AM -0600, Jan Beulich wrote:
>> >> >>> On 20.08.16 at 00:43, <daniel.kiper@oracle.com> wrote:
>> >> > --- a/xen/arch/x86/efi/stub.c
>> >> > +++ b/xen/arch/x86/efi/stub.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");
>> >>
>> >> There are explanations missing here: First, a warning should be added
>> >> alongside the EFI header inclusions, making clear that no services
>> >> whatsoever can be called. And then the asm() here needs to explain
>> >
>> > I am not convinced but if you wish...
>>
>> Not convinced of what?
> 
> About "... a warning should be added alongside the EFI header inclusions,
> making clear that no services whatsoever can be called". AIUI, "warning" ==
> "comment" here. However, I think that everybody who reads this file is
> aware that "no services whatsoever can be called". So, I am not sure
> where is the point.

Odd - you do an EFI call here (in the asm()) and talk about reader's
awareness?

>> >> need for an explicit "cc" clobber on x86.
>> >
>> > Why?
>>
>> Because such a clobber gets added to every asm() by the compiler,
>> unless it uses the (new in gcc 6) flag output. I've actually suggested
>> to upstream a patch making it possible to avoid that automatic
>> addition, but there hadn't been a whole lot of useful feedback.
> 
> So, when somebody uses this new flag then "cc" will not be add here.
> This is not big deal but I think that extra "cc" clobbers does not
> hurt too.

It surely doesn't hurt, but it makes code bigger and hence results in
it taking longer to be read and parsed (for all of these - even if just
slightly). I'm sorry, but I'm opposed to adding unnecessary stuff.

Jan
Daniel Kiper Sept. 1, 2016, 8:37 p.m. UTC | #6
On Thu, Sep 01, 2016 at 01:40:11AM -0600, Jan Beulich wrote:
> >>> On 31.08.16 at 19:07, <daniel.kiper@oracle.com> wrote:
> > On Wed, Aug 31, 2016 at 06:49:51AM -0600, Jan Beulich wrote:
> >> >>> On 30.08.16 at 21:32, <daniel.kiper@oracle.com> wrote:
> >> > On Thu, Aug 25, 2016 at 06:59:54AM -0600, Jan Beulich wrote:
> >> >> >>> On 20.08.16 at 00:43, <daniel.kiper@oracle.com> wrote:
> >> >> > --- a/xen/arch/x86/efi/stub.c
> >> >> > +++ b/xen/arch/x86/efi/stub.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");
> >> >>
> >> >> There are explanations missing here: First, a warning should be added
> >> >> alongside the EFI header inclusions, making clear that no services
> >> >> whatsoever can be called. And then the asm() here needs to explain
> >> >
> >> > I am not convinced but if you wish...
> >>
> >> Not convinced of what?
> >
> > About "... a warning should be added alongside the EFI header inclusions,
> > making clear that no services whatsoever can be called". AIUI, "warning" ==
> > "comment" here. However, I think that everybody who reads this file is
> > aware that "no services whatsoever can be called". So, I am not sure
> > where is the point.
>
> Odd - you do an EFI call here (in the asm()) and talk about reader's
> awareness?

I do this in quite strange way just to display clear error from file called
stub.c which contains just mostly function stubs. So, I have a feeling that
sane reader will be conscious here and will not expect code which does sensible
stuff. However, if you still think that these are insufficient then I can add
warninng/comment which assures potential reader that this is a stub file and
most functions does nothing except efi_multiboot2().

> >> >> need for an explicit "cc" clobber on x86.
> >> >
> >> > Why?
> >>
> >> Because such a clobber gets added to every asm() by the compiler,
> >> unless it uses the (new in gcc 6) flag output. I've actually suggested
> >> to upstream a patch making it possible to avoid that automatic
> >> addition, but there hadn't been a whole lot of useful feedback.
> >
> > So, when somebody uses this new flag then "cc" will not be add here.
> > This is not big deal but I think that extra "cc" clobbers does not
> > hurt too.
>
> It surely doesn't hurt, but it makes code bigger and hence results in
> it taking longer to be read and parsed (for all of these - even if just
> slightly). I'm sorry, but I'm opposed to adding unnecessary stuff.

As you wish...

Daniel
diff mbox

Patch

diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index 5e61854..aca5370 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,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
diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h
index 3f87b7c..2622cf7 100644
--- a/xen/arch/x86/efi/efi-boot.h
+++ b/xen/arch/x86/efi/efi-boot.h
@@ -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
diff --git a/xen/arch/x86/efi/stub.c b/xen/arch/x86/efi/stub.c
index f7bc042..d886de6 100644
--- a/xen/arch/x86/efi/stub.c
+++ b/xen/arch/x86/efi/stub.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();
+}
 
 bool_t efi_enabled(int feature)
 {
diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c
index 0fc1c63..9695ea6 100644
--- a/xen/arch/x86/x86_64/asm-offsets.c
+++ b/xen/arch/x86/x86_64/asm-offsets.c
@@ -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);
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 40eb4c5..a6246c0 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -323,5 +323,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 48ef8ad..8d81069 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;