Merge branch 'x86-mixed-mode' into efi/next
This commit is contained in:
@@ -105,7 +105,6 @@ vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdx.o $(obj)/tdcall.o $(obj)/td
|
||||
vmlinux-objs-$(CONFIG_UNACCEPTED_MEMORY) += $(obj)/mem.o
|
||||
|
||||
vmlinux-objs-$(CONFIG_EFI) += $(obj)/efi.o
|
||||
vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_mixed.o
|
||||
vmlinux-libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
|
||||
|
||||
$(obj)/vmlinux: $(vmlinux-objs-y) $(vmlinux-libs-y) FORCE
|
||||
|
||||
@@ -1,341 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
|
||||
*
|
||||
* Early support for invoking 32-bit EFI services from a 64-bit kernel.
|
||||
*
|
||||
* Because this thunking occurs before ExitBootServices() we have to
|
||||
* restore the firmware's 32-bit GDT and IDT before we make EFI service
|
||||
* calls.
|
||||
*
|
||||
* On the plus side, we don't have to worry about mangling 64-bit
|
||||
* addresses into 32-bits because we're executing with an identity
|
||||
* mapped pagetable and haven't transitioned to 64-bit virtual addresses
|
||||
* yet.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/page_types.h>
|
||||
#include <asm/processor-flags.h>
|
||||
#include <asm/segment.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
.code64
|
||||
.text
|
||||
/*
|
||||
* When booting in 64-bit mode on 32-bit EFI firmware, startup_64_mixed_mode()
|
||||
* is the first thing that runs after switching to long mode. Depending on
|
||||
* whether the EFI handover protocol or the compat entry point was used to
|
||||
* enter the kernel, it will either branch to the common 64-bit EFI stub
|
||||
* entrypoint efi_stub_entry() directly, or via the 64-bit EFI PE/COFF
|
||||
* entrypoint efi_pe_entry(). In the former case, the bootloader must provide a
|
||||
* struct bootparams pointer as the third argument, so the presence of such a
|
||||
* pointer is used to disambiguate.
|
||||
*
|
||||
* +--------------+
|
||||
* +------------------+ +------------+ +------>| efi_pe_entry |
|
||||
* | efi32_pe_entry |---->| | | +-----------+--+
|
||||
* +------------------+ | | +------+----------------+ |
|
||||
* | startup_32 |---->| startup_64_mixed_mode | |
|
||||
* +------------------+ | | +------+----------------+ |
|
||||
* | efi32_stub_entry |---->| | | |
|
||||
* +------------------+ +------------+ | |
|
||||
* V |
|
||||
* +------------+ +----------------+ |
|
||||
* | startup_64 |<----| efi_stub_entry |<--------+
|
||||
* +------------+ +----------------+
|
||||
*/
|
||||
SYM_FUNC_START(startup_64_mixed_mode)
|
||||
lea efi32_boot_args(%rip), %rdx
|
||||
mov 0(%rdx), %edi
|
||||
mov 4(%rdx), %esi
|
||||
|
||||
/* Switch to the firmware's stack */
|
||||
movl efi32_boot_sp(%rip), %esp
|
||||
andl $~7, %esp
|
||||
|
||||
#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
|
||||
mov 8(%rdx), %edx // saved bootparams pointer
|
||||
test %edx, %edx
|
||||
jnz efi_stub_entry
|
||||
#endif
|
||||
/*
|
||||
* efi_pe_entry uses MS calling convention, which requires 32 bytes of
|
||||
* shadow space on the stack even if all arguments are passed in
|
||||
* registers. We also need an additional 8 bytes for the space that
|
||||
* would be occupied by the return address, and this also results in
|
||||
* the correct stack alignment for entry.
|
||||
*/
|
||||
sub $40, %rsp
|
||||
mov %rdi, %rcx // MS calling convention
|
||||
mov %rsi, %rdx
|
||||
jmp efi_pe_entry
|
||||
SYM_FUNC_END(startup_64_mixed_mode)
|
||||
|
||||
SYM_FUNC_START(__efi64_thunk)
|
||||
push %rbp
|
||||
push %rbx
|
||||
|
||||
movl %ds, %eax
|
||||
push %rax
|
||||
movl %es, %eax
|
||||
push %rax
|
||||
movl %ss, %eax
|
||||
push %rax
|
||||
|
||||
/* Copy args passed on stack */
|
||||
movq 0x30(%rsp), %rbp
|
||||
movq 0x38(%rsp), %rbx
|
||||
movq 0x40(%rsp), %rax
|
||||
|
||||
/*
|
||||
* Convert x86-64 ABI params to i386 ABI
|
||||
*/
|
||||
subq $64, %rsp
|
||||
movl %esi, 0x0(%rsp)
|
||||
movl %edx, 0x4(%rsp)
|
||||
movl %ecx, 0x8(%rsp)
|
||||
movl %r8d, 0xc(%rsp)
|
||||
movl %r9d, 0x10(%rsp)
|
||||
movl %ebp, 0x14(%rsp)
|
||||
movl %ebx, 0x18(%rsp)
|
||||
movl %eax, 0x1c(%rsp)
|
||||
|
||||
leaq 0x20(%rsp), %rbx
|
||||
sgdt (%rbx)
|
||||
sidt 16(%rbx)
|
||||
|
||||
leaq 1f(%rip), %rbp
|
||||
|
||||
/*
|
||||
* Switch to IDT and GDT with 32-bit segments. These are the firmware
|
||||
* GDT and IDT that were installed when the kernel started executing.
|
||||
* The pointers were saved by the efi32_entry() routine below.
|
||||
*
|
||||
* Pass the saved DS selector to the 32-bit code, and use far return to
|
||||
* restore the saved CS selector.
|
||||
*/
|
||||
lidt efi32_boot_idt(%rip)
|
||||
lgdt efi32_boot_gdt(%rip)
|
||||
|
||||
movzwl efi32_boot_ds(%rip), %edx
|
||||
movzwq efi32_boot_cs(%rip), %rax
|
||||
pushq %rax
|
||||
leaq efi_enter32(%rip), %rax
|
||||
pushq %rax
|
||||
lretq
|
||||
|
||||
1: addq $64, %rsp
|
||||
movq %rdi, %rax
|
||||
|
||||
pop %rbx
|
||||
movl %ebx, %ss
|
||||
pop %rbx
|
||||
movl %ebx, %es
|
||||
pop %rbx
|
||||
movl %ebx, %ds
|
||||
/* Clear out 32-bit selector from FS and GS */
|
||||
xorl %ebx, %ebx
|
||||
movl %ebx, %fs
|
||||
movl %ebx, %gs
|
||||
|
||||
pop %rbx
|
||||
pop %rbp
|
||||
RET
|
||||
SYM_FUNC_END(__efi64_thunk)
|
||||
|
||||
.code32
|
||||
#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
|
||||
SYM_FUNC_START(efi32_stub_entry)
|
||||
call 1f
|
||||
1: popl %ecx
|
||||
leal (efi32_boot_args - 1b)(%ecx), %ebx
|
||||
|
||||
/* Clear BSS */
|
||||
xorl %eax, %eax
|
||||
leal (_bss - 1b)(%ecx), %edi
|
||||
leal (_ebss - 1b)(%ecx), %ecx
|
||||
subl %edi, %ecx
|
||||
shrl $2, %ecx
|
||||
cld
|
||||
rep stosl
|
||||
|
||||
add $0x4, %esp /* Discard return address */
|
||||
popl %ecx
|
||||
popl %edx
|
||||
popl %esi
|
||||
movl %esi, 8(%ebx)
|
||||
jmp efi32_entry
|
||||
SYM_FUNC_END(efi32_stub_entry)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* EFI service pointer must be in %edi.
|
||||
*
|
||||
* The stack should represent the 32-bit calling convention.
|
||||
*/
|
||||
SYM_FUNC_START_LOCAL(efi_enter32)
|
||||
/* Load firmware selector into data and stack segment registers */
|
||||
movl %edx, %ds
|
||||
movl %edx, %es
|
||||
movl %edx, %fs
|
||||
movl %edx, %gs
|
||||
movl %edx, %ss
|
||||
|
||||
/* Reload pgtables */
|
||||
movl %cr3, %eax
|
||||
movl %eax, %cr3
|
||||
|
||||
/* Disable paging */
|
||||
movl %cr0, %eax
|
||||
btrl $X86_CR0_PG_BIT, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
/* Disable long mode via EFER */
|
||||
movl $MSR_EFER, %ecx
|
||||
rdmsr
|
||||
btrl $_EFER_LME, %eax
|
||||
wrmsr
|
||||
|
||||
call *%edi
|
||||
|
||||
/* We must preserve return value */
|
||||
movl %eax, %edi
|
||||
|
||||
/*
|
||||
* Some firmware will return with interrupts enabled. Be sure to
|
||||
* disable them before we switch GDTs and IDTs.
|
||||
*/
|
||||
cli
|
||||
|
||||
lidtl 16(%ebx)
|
||||
lgdtl (%ebx)
|
||||
|
||||
movl %cr4, %eax
|
||||
btsl $(X86_CR4_PAE_BIT), %eax
|
||||
movl %eax, %cr4
|
||||
|
||||
movl %cr3, %eax
|
||||
movl %eax, %cr3
|
||||
|
||||
movl $MSR_EFER, %ecx
|
||||
rdmsr
|
||||
btsl $_EFER_LME, %eax
|
||||
wrmsr
|
||||
|
||||
xorl %eax, %eax
|
||||
lldt %ax
|
||||
|
||||
pushl $__KERNEL_CS
|
||||
pushl %ebp
|
||||
|
||||
/* Enable paging */
|
||||
movl %cr0, %eax
|
||||
btsl $X86_CR0_PG_BIT, %eax
|
||||
movl %eax, %cr0
|
||||
lret
|
||||
SYM_FUNC_END(efi_enter32)
|
||||
|
||||
/*
|
||||
* This is the common EFI stub entry point for mixed mode.
|
||||
*
|
||||
* Arguments: %ecx image handle
|
||||
* %edx EFI system table pointer
|
||||
*
|
||||
* Since this is the point of no return for ordinary execution, no registers
|
||||
* are considered live except for the function parameters. [Note that the EFI
|
||||
* stub may still exit and return to the firmware using the Exit() EFI boot
|
||||
* service.]
|
||||
*/
|
||||
SYM_FUNC_START_LOCAL(efi32_entry)
|
||||
call 1f
|
||||
1: pop %ebx
|
||||
|
||||
/* Save firmware GDTR and code/data selectors */
|
||||
sgdtl (efi32_boot_gdt - 1b)(%ebx)
|
||||
movw %cs, (efi32_boot_cs - 1b)(%ebx)
|
||||
movw %ds, (efi32_boot_ds - 1b)(%ebx)
|
||||
|
||||
/* Store firmware IDT descriptor */
|
||||
sidtl (efi32_boot_idt - 1b)(%ebx)
|
||||
|
||||
/* Store firmware stack pointer */
|
||||
movl %esp, (efi32_boot_sp - 1b)(%ebx)
|
||||
|
||||
/* Store boot arguments */
|
||||
leal (efi32_boot_args - 1b)(%ebx), %ebx
|
||||
movl %ecx, 0(%ebx)
|
||||
movl %edx, 4(%ebx)
|
||||
movb $0x0, 12(%ebx) // efi_is64
|
||||
|
||||
/*
|
||||
* Allocate some memory for a temporary struct boot_params, which only
|
||||
* needs the minimal pieces that startup_32() relies on.
|
||||
*/
|
||||
subl $PARAM_SIZE, %esp
|
||||
movl %esp, %esi
|
||||
movl $PAGE_SIZE, BP_kernel_alignment(%esi)
|
||||
movl $_end - 1b, BP_init_size(%esi)
|
||||
subl $startup_32 - 1b, BP_init_size(%esi)
|
||||
|
||||
/* Disable paging */
|
||||
movl %cr0, %eax
|
||||
btrl $X86_CR0_PG_BIT, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
jmp startup_32
|
||||
SYM_FUNC_END(efi32_entry)
|
||||
|
||||
/*
|
||||
* efi_status_t efi32_pe_entry(efi_handle_t image_handle,
|
||||
* efi_system_table_32_t *sys_table)
|
||||
*/
|
||||
SYM_FUNC_START(efi32_pe_entry)
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
pushl %ebx // save callee-save registers
|
||||
pushl %edi
|
||||
|
||||
call verify_cpu // check for long mode support
|
||||
testl %eax, %eax
|
||||
movl $0x80000003, %eax // EFI_UNSUPPORTED
|
||||
jnz 2f
|
||||
|
||||
movl 8(%ebp), %ecx // image_handle
|
||||
movl 12(%ebp), %edx // sys_table
|
||||
jmp efi32_entry // pass %ecx, %edx
|
||||
// no other registers remain live
|
||||
|
||||
2: popl %edi // restore callee-save registers
|
||||
popl %ebx
|
||||
leave
|
||||
RET
|
||||
SYM_FUNC_END(efi32_pe_entry)
|
||||
|
||||
#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
|
||||
.org efi32_stub_entry + 0x200
|
||||
.code64
|
||||
SYM_FUNC_START_NOALIGN(efi64_stub_entry)
|
||||
jmp efi_handover_entry
|
||||
SYM_FUNC_END(efi64_stub_entry)
|
||||
#endif
|
||||
|
||||
.data
|
||||
.balign 8
|
||||
SYM_DATA_START_LOCAL(efi32_boot_gdt)
|
||||
.word 0
|
||||
.quad 0
|
||||
SYM_DATA_END(efi32_boot_gdt)
|
||||
|
||||
SYM_DATA_START_LOCAL(efi32_boot_idt)
|
||||
.word 0
|
||||
.quad 0
|
||||
SYM_DATA_END(efi32_boot_idt)
|
||||
|
||||
SYM_DATA_LOCAL(efi32_boot_cs, .word 0)
|
||||
SYM_DATA_LOCAL(efi32_boot_ds, .word 0)
|
||||
SYM_DATA_LOCAL(efi32_boot_sp, .long 0)
|
||||
SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0)
|
||||
SYM_DATA(efi_is64, .byte 1)
|
||||
@@ -263,13 +263,6 @@ SYM_FUNC_START(startup_32)
|
||||
* used to perform that far jump.
|
||||
*/
|
||||
leal rva(startup_64)(%ebp), %eax
|
||||
#ifdef CONFIG_EFI_MIXED
|
||||
cmpb $1, rva(efi_is64)(%ebp)
|
||||
je 1f
|
||||
leal rva(startup_64_mixed_mode)(%ebp), %eax
|
||||
1:
|
||||
#endif
|
||||
|
||||
pushl $__KERNEL_CS
|
||||
pushl %eax
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO), $(KBUILD_CFLAGS))
|
||||
# `-fdata-sections` flag from KBUILD_CFLAGS_KERNEL
|
||||
KBUILD_CFLAGS_KERNEL := $(filter-out -fdata-sections, $(KBUILD_CFLAGS_KERNEL))
|
||||
|
||||
KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
|
||||
|
||||
lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \
|
||||
file.o mem.o random.o randomalloc.o pci.o \
|
||||
skip_spaces.o lib-cmdline.o lib-ctype.o \
|
||||
@@ -83,6 +85,7 @@ lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \
|
||||
lib-$(CONFIG_ARM) += arm32-stub.o
|
||||
lib-$(CONFIG_ARM64) += kaslr.o arm64.o arm64-stub.o smbios.o
|
||||
lib-$(CONFIG_X86) += x86-stub.o smbios.o
|
||||
lib-$(CONFIG_EFI_MIXED) += x86-mixed.o
|
||||
lib-$(CONFIG_X86_64) += x86-5lvl.o
|
||||
lib-$(CONFIG_RISCV) += kaslr.o riscv.o riscv-stub.o
|
||||
lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
|
||||
*
|
||||
* Early support for invoking 32-bit EFI services from a 64-bit kernel.
|
||||
*
|
||||
* Because this thunking occurs before ExitBootServices() we have to
|
||||
* restore the firmware's 32-bit GDT and IDT before we make EFI service
|
||||
* calls.
|
||||
*
|
||||
* On the plus side, we don't have to worry about mangling 64-bit
|
||||
* addresses into 32-bits because we're executing with an identity
|
||||
* mapped pagetable and haven't transitioned to 64-bit virtual addresses
|
||||
* yet.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/desc_defs.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/page_types.h>
|
||||
#include <asm/pgtable_types.h>
|
||||
#include <asm/processor-flags.h>
|
||||
#include <asm/segment.h>
|
||||
|
||||
.text
|
||||
.code32
|
||||
#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
|
||||
SYM_FUNC_START(efi32_stub_entry)
|
||||
call 1f
|
||||
1: popl %ecx
|
||||
|
||||
/* Clear BSS */
|
||||
xorl %eax, %eax
|
||||
leal (_bss - 1b)(%ecx), %edi
|
||||
leal (_ebss - 1b)(%ecx), %ecx
|
||||
subl %edi, %ecx
|
||||
shrl $2, %ecx
|
||||
cld
|
||||
rep stosl
|
||||
|
||||
add $0x4, %esp /* Discard return address */
|
||||
movl 8(%esp), %ebx /* struct boot_params pointer */
|
||||
jmp efi32_startup
|
||||
SYM_FUNC_END(efi32_stub_entry)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Called using a far call from __efi64_thunk() below, using the x86_64 SysV
|
||||
* ABI (except for R8/R9 which are inaccessible to 32-bit code - EAX/EBX are
|
||||
* used instead). EBP+16 points to the arguments passed via the stack.
|
||||
*
|
||||
* The first argument (EDI) is a pointer to the boot service or protocol, to
|
||||
* which the remaining arguments are passed, each truncated to 32 bits.
|
||||
*/
|
||||
SYM_FUNC_START_LOCAL(efi_enter32)
|
||||
/*
|
||||
* Convert x86-64 SysV ABI params to i386 ABI
|
||||
*/
|
||||
pushl 32(%ebp) /* Up to 3 args passed via the stack */
|
||||
pushl 24(%ebp)
|
||||
pushl 16(%ebp)
|
||||
pushl %ebx /* R9 */
|
||||
pushl %eax /* R8 */
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
pushl %esi
|
||||
|
||||
/* Disable paging */
|
||||
movl %cr0, %eax
|
||||
btrl $X86_CR0_PG_BIT, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
/* Disable long mode via EFER */
|
||||
movl $MSR_EFER, %ecx
|
||||
rdmsr
|
||||
btrl $_EFER_LME, %eax
|
||||
wrmsr
|
||||
|
||||
call *%edi
|
||||
|
||||
/* We must preserve return value */
|
||||
movl %eax, %edi
|
||||
|
||||
call efi32_enable_long_mode
|
||||
|
||||
addl $32, %esp
|
||||
movl %edi, %eax
|
||||
lret
|
||||
SYM_FUNC_END(efi_enter32)
|
||||
|
||||
.code64
|
||||
SYM_FUNC_START(__efi64_thunk)
|
||||
push %rbp
|
||||
movl %esp, %ebp
|
||||
push %rbx
|
||||
|
||||
/* Move args #5 and #6 into 32-bit accessible registers */
|
||||
movl %r8d, %eax
|
||||
movl %r9d, %ebx
|
||||
|
||||
lcalll *efi32_call(%rip)
|
||||
|
||||
pop %rbx
|
||||
pop %rbp
|
||||
RET
|
||||
SYM_FUNC_END(__efi64_thunk)
|
||||
|
||||
.code32
|
||||
SYM_FUNC_START_LOCAL(efi32_enable_long_mode)
|
||||
movl %cr4, %eax
|
||||
btsl $(X86_CR4_PAE_BIT), %eax
|
||||
movl %eax, %cr4
|
||||
|
||||
movl $MSR_EFER, %ecx
|
||||
rdmsr
|
||||
btsl $_EFER_LME, %eax
|
||||
wrmsr
|
||||
|
||||
/* Disable interrupts - the firmware's IDT does not work in long mode */
|
||||
cli
|
||||
|
||||
/* Enable paging */
|
||||
movl %cr0, %eax
|
||||
btsl $X86_CR0_PG_BIT, %eax
|
||||
movl %eax, %cr0
|
||||
ret
|
||||
SYM_FUNC_END(efi32_enable_long_mode)
|
||||
|
||||
/*
|
||||
* This is the common EFI stub entry point for mixed mode. It sets up the GDT
|
||||
* and page tables needed for 64-bit execution, after which it calls the
|
||||
* common 64-bit EFI entrypoint efi_stub_entry().
|
||||
*
|
||||
* Arguments: 0(%esp) image handle
|
||||
* 4(%esp) EFI system table pointer
|
||||
* %ebx struct boot_params pointer (or NULL)
|
||||
*
|
||||
* Since this is the point of no return for ordinary execution, no registers
|
||||
* are considered live except for the function parameters. [Note that the EFI
|
||||
* stub may still exit and return to the firmware using the Exit() EFI boot
|
||||
* service.]
|
||||
*/
|
||||
SYM_FUNC_START_LOCAL(efi32_startup)
|
||||
movl %esp, %ebp
|
||||
|
||||
subl $8, %esp
|
||||
sgdtl (%esp) /* Save GDT descriptor to the stack */
|
||||
movl 2(%esp), %esi /* Existing GDT pointer */
|
||||
movzwl (%esp), %ecx /* Existing GDT limit */
|
||||
inc %ecx /* Existing GDT size */
|
||||
andl $~7, %ecx /* Ensure size is multiple of 8 */
|
||||
|
||||
subl %ecx, %esp /* Allocate new GDT */
|
||||
andl $~15, %esp /* Realign the stack */
|
||||
movl %esp, %edi /* New GDT address */
|
||||
leal 7(%ecx), %eax /* New GDT limit */
|
||||
pushw %cx /* Push 64-bit CS (for LJMP below) */
|
||||
pushl %edi /* Push new GDT address */
|
||||
pushw %ax /* Push new GDT limit */
|
||||
|
||||
/* Copy GDT to the stack and add a 64-bit code segment at the end */
|
||||
movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) & 0xffffffff, (%edi,%ecx)
|
||||
movl $GDT_ENTRY(DESC_CODE64, 0, 0xfffff) >> 32, 4(%edi,%ecx)
|
||||
shrl $2, %ecx
|
||||
cld
|
||||
rep movsl /* Copy the firmware GDT */
|
||||
lgdtl (%esp) /* Switch to the new GDT */
|
||||
|
||||
call 1f
|
||||
1: pop %edi
|
||||
|
||||
/* Record mixed mode entry */
|
||||
movb $0x0, (efi_is64 - 1b)(%edi)
|
||||
|
||||
/* Set up indirect far call to re-enter 32-bit mode */
|
||||
leal (efi32_call - 1b)(%edi), %eax
|
||||
addl %eax, (%eax)
|
||||
movw %cs, 4(%eax)
|
||||
|
||||
/* Disable paging */
|
||||
movl %cr0, %eax
|
||||
btrl $X86_CR0_PG_BIT, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
/* Set up 1:1 mapping */
|
||||
leal (pte - 1b)(%edi), %eax
|
||||
movl $_PAGE_PRESENT | _PAGE_RW | _PAGE_PSE, %ecx
|
||||
leal (_PAGE_PRESENT | _PAGE_RW)(%eax), %edx
|
||||
2: movl %ecx, (%eax)
|
||||
addl $8, %eax
|
||||
addl $PMD_SIZE, %ecx
|
||||
jnc 2b
|
||||
|
||||
movl $PAGE_SIZE, %ecx
|
||||
.irpc l, 0123
|
||||
movl %edx, \l * 8(%eax)
|
||||
addl %ecx, %edx
|
||||
.endr
|
||||
addl %ecx, %eax
|
||||
movl %edx, (%eax)
|
||||
movl %eax, %cr3
|
||||
|
||||
call efi32_enable_long_mode
|
||||
|
||||
/* Set up far jump to 64-bit mode (CS is already on the stack) */
|
||||
leal (efi_stub_entry - 1b)(%edi), %eax
|
||||
movl %eax, 2(%esp)
|
||||
|
||||
movl 0(%ebp), %edi
|
||||
movl 4(%ebp), %esi
|
||||
movl %ebx, %edx
|
||||
ljmpl *2(%esp)
|
||||
SYM_FUNC_END(efi32_startup)
|
||||
|
||||
/*
|
||||
* efi_status_t efi32_pe_entry(efi_handle_t image_handle,
|
||||
* efi_system_table_32_t *sys_table)
|
||||
*/
|
||||
SYM_FUNC_START(efi32_pe_entry)
|
||||
pushl %ebx // save callee-save registers
|
||||
|
||||
/* Check whether the CPU supports long mode */
|
||||
movl $0x80000001, %eax // assume extended info support
|
||||
cpuid
|
||||
btl $29, %edx // check long mode bit
|
||||
jnc 1f
|
||||
leal 8(%esp), %esp // preserve stack alignment
|
||||
xor %ebx, %ebx // no struct boot_params pointer
|
||||
jmp efi32_startup // only ESP and EBX remain live
|
||||
1: movl $0x80000003, %eax // EFI_UNSUPPORTED
|
||||
popl %ebx
|
||||
RET
|
||||
SYM_FUNC_END(efi32_pe_entry)
|
||||
|
||||
#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
|
||||
.org efi32_stub_entry + 0x200
|
||||
.code64
|
||||
SYM_FUNC_START_NOALIGN(efi64_stub_entry)
|
||||
jmp efi_handover_entry
|
||||
SYM_FUNC_END(efi64_stub_entry)
|
||||
#endif
|
||||
|
||||
.data
|
||||
.balign 8
|
||||
SYM_DATA_START_LOCAL(efi32_call)
|
||||
.long efi_enter32 - .
|
||||
.word 0x0
|
||||
SYM_DATA_END(efi32_call)
|
||||
SYM_DATA(efi_is64, .byte 1)
|
||||
|
||||
.bss
|
||||
.balign PAGE_SIZE
|
||||
SYM_DATA_LOCAL(pte, .fill 6 * PAGE_SIZE, 1, 0)
|
||||
@@ -397,17 +397,13 @@ static void __noreturn efi_exit(efi_handle_t handle, efi_status_t status)
|
||||
asm("hlt");
|
||||
}
|
||||
|
||||
void __noreturn efi_stub_entry(efi_handle_t handle,
|
||||
efi_system_table_t *sys_table_arg,
|
||||
struct boot_params *boot_params);
|
||||
|
||||
/*
|
||||
* Because the x86 boot code expects to be passed a boot_params we
|
||||
* need to create one ourselves (usually the bootloader would create
|
||||
* one for us).
|
||||
*/
|
||||
efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
|
||||
efi_system_table_t *sys_table_arg)
|
||||
static efi_status_t efi_allocate_bootparams(efi_handle_t handle,
|
||||
struct boot_params **bp)
|
||||
{
|
||||
efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
|
||||
struct boot_params *boot_params;
|
||||
@@ -416,21 +412,15 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
|
||||
unsigned long alloc;
|
||||
char *cmdline_ptr;
|
||||
|
||||
efi_system_table = sys_table_arg;
|
||||
|
||||
/* Check if we were booted by the EFI firmware */
|
||||
if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
|
||||
efi_exit(handle, EFI_INVALID_PARAMETER);
|
||||
|
||||
status = efi_bs_call(handle_protocol, handle, &proto, (void **)&image);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_err("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
|
||||
efi_exit(handle, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = efi_allocate_pages(PARAM_SIZE, &alloc, ULONG_MAX);
|
||||
if (status != EFI_SUCCESS)
|
||||
efi_exit(handle, status);
|
||||
return status;
|
||||
|
||||
boot_params = memset((void *)alloc, 0x0, PARAM_SIZE);
|
||||
hdr = &boot_params->hdr;
|
||||
@@ -446,14 +436,14 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
|
||||
cmdline_ptr = efi_convert_cmdline(image);
|
||||
if (!cmdline_ptr) {
|
||||
efi_free(PARAM_SIZE, alloc);
|
||||
efi_exit(handle, EFI_OUT_OF_RESOURCES);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
efi_set_u64_split((unsigned long)cmdline_ptr, &hdr->cmd_line_ptr,
|
||||
&boot_params->ext_cmd_line_ptr);
|
||||
|
||||
efi_stub_entry(handle, sys_table_arg, boot_params);
|
||||
/* not reached */
|
||||
*bp = boot_params;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static void add_e820ext(struct boot_params *params,
|
||||
@@ -740,13 +730,16 @@ static efi_status_t parse_options(const char *cmdline)
|
||||
return efi_parse_options(cmdline);
|
||||
}
|
||||
|
||||
static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry)
|
||||
static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry,
|
||||
struct boot_params *boot_params)
|
||||
{
|
||||
unsigned long virt_addr = LOAD_PHYSICAL_ADDR;
|
||||
unsigned long addr, alloc_size, entry;
|
||||
efi_status_t status;
|
||||
u32 seed[2] = {};
|
||||
|
||||
boot_params_ptr = boot_params;
|
||||
|
||||
/* determine the required size of the allocation */
|
||||
alloc_size = ALIGN(max_t(unsigned long, output_len, kernel_total_size),
|
||||
MIN_KERNEL_ALIGN);
|
||||
@@ -777,7 +770,7 @@ static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry)
|
||||
seed[0] = 0;
|
||||
}
|
||||
|
||||
boot_params_ptr->hdr.loadflags |= KASLR_FLAG;
|
||||
boot_params->hdr.loadflags |= KASLR_FLAG;
|
||||
}
|
||||
|
||||
status = efi_random_alloc(alloc_size, CONFIG_PHYSICAL_ALIGN, &addr,
|
||||
@@ -815,20 +808,27 @@ static void __noreturn enter_kernel(unsigned long kernel_addr,
|
||||
void __noreturn efi_stub_entry(efi_handle_t handle,
|
||||
efi_system_table_t *sys_table_arg,
|
||||
struct boot_params *boot_params)
|
||||
|
||||
{
|
||||
efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
|
||||
struct setup_header *hdr = &boot_params->hdr;
|
||||
const struct linux_efi_initrd *initrd = NULL;
|
||||
unsigned long kernel_entry;
|
||||
struct setup_header *hdr;
|
||||
efi_status_t status;
|
||||
|
||||
boot_params_ptr = boot_params;
|
||||
|
||||
efi_system_table = sys_table_arg;
|
||||
/* Check if we were booted by the EFI firmware */
|
||||
if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
|
||||
efi_exit(handle, EFI_INVALID_PARAMETER);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_EFI_HANDOVER_PROTOCOL) || !boot_params) {
|
||||
status = efi_allocate_bootparams(handle, &boot_params);
|
||||
if (status != EFI_SUCCESS)
|
||||
efi_exit(handle, status);
|
||||
}
|
||||
|
||||
hdr = &boot_params->hdr;
|
||||
|
||||
if (have_unsupported_snp_features())
|
||||
efi_exit(handle, EFI_UNSUPPORTED);
|
||||
|
||||
@@ -870,7 +870,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
|
||||
if (efi_mem_encrypt > 0)
|
||||
hdr->xloadflags |= XLF_MEM_ENCRYPTION;
|
||||
|
||||
status = efi_decompress_kernel(&kernel_entry);
|
||||
status = efi_decompress_kernel(&kernel_entry, boot_params);
|
||||
if (status != EFI_SUCCESS) {
|
||||
efi_err("Failed to decompress kernel\n");
|
||||
goto fail;
|
||||
@@ -940,6 +940,12 @@ fail:
|
||||
efi_exit(handle, status);
|
||||
}
|
||||
|
||||
efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
|
||||
efi_system_table_t *sys_table_arg)
|
||||
{
|
||||
efi_stub_entry(handle, sys_table_arg, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
|
||||
void efi_handover_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg,
|
||||
struct boot_params *boot_params)
|
||||
|
||||
Reference in New Issue
Block a user