0, /* Number of the lines - no preference. */ \
0 /* Number of bits per pixel - no preference. */
+ /* Request that ExitBootServices() not be called. */
+ mb2ht_init MB2_HT(EFI_BS), MB2_HT(OPTIONAL)
+
+ /* EFI64 Multiboot2 entry point. */
+ mb2ht_init MB2_HT(ENTRY_ADDRESS_EFI64), MB2_HT(OPTIONAL), \
+ sym_phys(__efi64_mb2_start)
+
/* Multiboot2 header end tag. */
mb2ht_init MB2_HT(END), MB2_HT(REQUIRED)
.Lmultiboot2_header_end:
gdt_boot_descr:
.word 6*8-1
.long sym_phys(trampoline_gdt)
+ .long 0 /* Needed for 64-bit lgdt */
.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.data, "aw", @progbits
+ .align 4
+
+vga_text_buffer:
+ .long 0xb8000
+
+efi_platform:
+ .byte 0
.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:
+ /*
+ * Here we are on EFI platform. vga_text_buffer was zapped earlier
+ * because there is pretty good chance that VGA is unavailable.
+ */
+ mov $(sym_phys(.Lbad_ldr_nst)),%esi # Error message
+ jmp .Lget_vtb
+.Lmb2_no_ih:
+ /* Ditto. */
+ mov $(sym_phys(.Lbad_ldr_nih)),%esi # Error message
+ jmp .Lget_vtb
+.Lmb2_no_bs:
+ /*
+ * Ditto. Additionally, here there is a chance that Xen was started
+ * via start label. Then reliable vga_text_buffer zap is impossible
+ * in Multiboot2 scanning loop and we have to zero %edi below.
+ */
+ mov $(sym_phys(.Lbad_ldr_nbs)),%esi # Error message
+ xor %edi,%edi # No VGA text buffer
+ jmp .Lsend_chr
+.Lmb2_efi_ia_32:
+ /*
+ * Here we are on EFI IA-32 platform. Then reliable vga_text_buffer zap is
+ * impossible in Multiboot2 scanning loop and we have to zero %edi below.
+ */
+ 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
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_mb2_start:
+ /*
+ * Multiboot2 spec says that here CPU is in 64-bit mode. However,
+ * there is also guarantee that all code and data is always put
+ * by the bootloader below 4 GiB. Hence, we can safely truncate
+ * addresses to 32-bits in most cases below.
+ */
+
+ 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),%r15
+ 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 .Lrun_bs
+
+ /* Are EFI boot services available? */
+ cmpl $MULTIBOOT2_TAG_TYPE_EFI_BS,MB2_tag_type(%rcx)
+ jne .Lefi_mb2_st
+
+ /* We are on EFI platform and EFI boot services are available. */
+ incb efi_platform(%rip)
+
+ /*
+ * 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 .Lrun_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
+
+.Lrun_bs:
+ /* Are EFI boot services available? */
+ cmpb $0,efi_platform(%rip)
+
+ /* Jump to .Lmb2_no_bs after switching CPU to x86_32 mode. */
+ lea .Lmb2_no_bs(%rip),%r15
+ jz x86_32_switch
+
+ /* Is EFI SystemTable address provided by boot loader? */
+ test %rsi,%rsi
+
+ /* Jump to .Lmb2_no_st after switching CPU to x86_32 mode. */
+ lea .Lmb2_no_st(%rip),%r15
+ jz x86_32_switch
+
+ /* Is EFI ImageHandle address provided by boot loader? */
+ test %rdi,%rdi
+
+ /* Jump to .Lmb2_no_ih after switching CPU to x86_32 mode. */
+ lea .Lmb2_no_ih(%rip),%r15
+ jz x86_32_switch
+
+ /*
+ * Align the stack as UEFI spec requires. Keep it aligned
+ * before efi_multiboot2() call by pushing/popping even
+ * numbers of items on it.
+ */
+ and $~15,%rsp
+
+ /* Save Multiboot2 magic on the stack. */
+ push %rax
+
+ /* Save EFI ImageHandle on the stack. */
+ 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
+
+ /* Keep the stack aligned. Do not pop a single item off it. */
+ mov (%rsp),%rdi
+
+ /*
+ * efi_multiboot2() is called according to System V AMD64 ABI:
+ * - IN: %rdi - EFI ImageHandle, %rsi - EFI SystemTable.
+ */
+ call efi_multiboot2
+
+ /* Just pop an item from the stack. */
+ pop %rax
+
+ /* Restore Multiboot2 magic. */
+ pop %rax
+
+ /* Jump to trampoline_setup after switching CPU to x86_32 mode. */
+ lea trampoline_setup(%rip),%r15
+
+x86_32_switch:
+ mov %r15,%rdi
+
+ cli
+
+ /* Initialize GDTR. */
+ lgdt gdt_boot_descr(%rip)
+
+ /* Reload code selector. */
+ pushq $BOOT_CS32
+ lea cs32_switch(%rip),%edx
+ push %rdx
+ 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
/* 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. */
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) */
cmp %ecx,%edx /* compare with BDA value */
cmovb %edx,%ecx /* and use the smaller */
-2: /* Reserve 64kb for the trampoline */
- sub $0x1000,%ecx
+2:
+ /* Reserve memory for the trampoline and the low-memory stack. */
+ sub $((TRAMPOLINE_SPACE+TRAMPOLINE_STACK_SPACE)>>4),%ecx
/* From arch/x86/smpboot.c: start_eip had better be page-aligned! */
xor %cl, %cl
shl $4, %ecx
mov %ecx,sym_phys(trampoline_phys)
+trampoline_setup:
+ mov sym_phys(trampoline_phys),%ecx
+
+ /* Get bottom-most low-memory stack address. */
+ add $TRAMPOLINE_SPACE,%ecx
+
/* Save the Multiboot info struct (after relocation) for later use. */
mov $sym_phys(cpu0_stack)+1024,%esp
- push %ecx /* Boot trampoline address. */
+ push %ecx /* Bottom-most low-memory stack address. */
push %ebx /* Multiboot information address. */
push %eax /* Multiboot magic. */
call reloc
mov %eax,sym_phys(multiboot_ptr)
- /* Initialize BSS (no nasty surprises!) */
+ /*
+ * Now trampoline_phys points to the following structure (lowest address
+ * is at the bottom):
+ *
+ * +------------------------+
+ * | TRAMPOLINE_STACK_SPACE |
+ * +------------------------+
+ * | mbi data |
+ * +- - - - - - - - - - - - +
+ * | TRAMPOLINE_SPACE |
+ * +------------------------+
+ *
+ * mbi data grows downwards from the highest address of TRAMPOLINE_SPACE
+ * region to the end of the trampoline. The rest of TRAMPOLINE_SPACE is
+ * reserved for trampoline code and data.
+ */
+
+ /*
+ * Do not zero BSS on EFI platform here.
+ * It was initialized earlier.
+ */
+ cmpb $0,sym_phys(efi_platform)
+ jnz 1f
+
+ /* Initialize BSS (no nasty surprises!). */
mov $sym_phys(__bss_start),%edi
mov $sym_phys(__bss_end),%ecx
sub %edi,%ecx
shr $2,%ecx
rep stosl
+1:
/* Interrogate CPU extended features via CPUID. */
mov $0x80000000,%eax
cpuid
cmp $sym_phys(__trampoline_seg_stop),%edi
jb 1b
+ /* Do not parse command line on EFI platform here. */
+ cmpb $0,sym_phys(efi_platform)
+ jnz 1f
+
/* Bail if there is no command line to parse. */
mov sym_phys(multiboot_ptr),%ebx
testl $MBI_CMDLINE,MB_flags(%ebx)
call cmdline_parse_early
1:
- /* Switch to low-memory stack. */
+ /* Switch to low-memory stack which lives at the end of trampoline region. */
mov sym_phys(trampoline_phys),%edi
- lea 0x10000(%edi),%esp
+ lea TRAMPOLINE_SPACE+TRAMPOLINE_STACK_SPACE(%edi),%esp
lea trampoline_boot_cpu_entry-trampoline_start(%edi),%eax
pushl $BOOT_CS32
push %eax