Skip to content

Commit

Permalink
x86/efi: Add early thunk code to go from 64-bit to 32-bit
Browse files Browse the repository at this point in the history
Implement the transition code to go from IA32e mode to protected mode in
the EFI boot stub. This is required to use 32-bit EFI services from a
64-bit kernel.

Since EFI boot stub is executed in an identity-mapped region, there's
not much we need to do before invoking the 32-bit EFI boot services.
However, we do reload the firmware's global descriptor table
(efi32_boot_gdt) in case things like timer events are still running in
the firmware.

Signed-off-by: Matt Fleming <matt.fleming@intel.com>
  • Loading branch information
Matt Fleming committed Mar 4, 2014
1 parent 54b52d8 commit 0154416
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 0 deletions.
29 changes: 29 additions & 0 deletions arch/x86/boot/compressed/efi_stub_64.S
Original file line number Diff line number Diff line change
@@ -1 +1,30 @@
#include <asm/segment.h>
#include <asm/msr.h>
#include <asm/processor-flags.h>

#include "../../platform/efi/efi_stub_64.S"

#ifdef CONFIG_EFI_MIXED
.code64
.text
ENTRY(efi64_thunk)
push %rbp
push %rbx

subq $16, %rsp
leaq efi_exit32(%rip), %rax
movl %eax, 8(%rsp)
leaq efi_gdt64(%rip), %rax
movl %eax, 4(%rsp)
movl %eax, 2(%rax) /* Fixup the gdt base address */
leaq efi32_boot_gdt(%rip), %rax
movl %eax, (%rsp)

call __efi64_thunk

addq $16, %rsp
pop %rbx
pop %rbp
ret
ENDPROC(efi64_thunk)
#endif /* CONFIG_EFI_MIXED */
150 changes: 150 additions & 0 deletions arch/x86/platform/efi/efi_stub_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
*/

#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/msr.h>
#include <asm/processor-flags.h>
#include <asm/page_types.h>

#define SAVE_XMM \
mov %rsp, %rax; \
Expand Down Expand Up @@ -164,6 +168,152 @@ ENTRY(efi_call6)
ret
ENDPROC(efi_call6)

#ifdef CONFIG_EFI_MIXED

/*
* We run this function from the 1:1 mapping.
*
* This function must be invoked with a 1:1 mapped stack.
*/
ENTRY(__efi64_thunk)
subq $32, %rsp
movl %esi, 0x0(%rsp)
movl %edx, 0x4(%rsp)
movl %ecx, 0x8(%rsp)
movq %r8, %rsi
movl %esi, 0xc(%rsp)
movq %r9, %rsi
movl %esi, 0x10(%rsp)

sgdt save_gdt(%rip)

leaq 1f(%rip), %rbx
movq %rbx, func_rt_ptr(%rip)

/* Switch to gdt with 32-bit segments */
movl 40(%rsp), %eax
lgdt (%rax)

leaq efi_enter32(%rip), %rax
pushq $__KERNEL_CS
pushq %rax
lretq

1: addq $32, %rsp

lgdt save_gdt(%rip)

/*
* Convert 32-bit status code into 64-bit.
*/
test %rax, %rax
jz 1f
movl %eax, %ecx
andl $0x0fffffff, %ecx
andl $0xf0000000, %eax
shl $32, %rax
or %rcx, %rax
1:
ret
ENDPROC(__efi64_thunk)

ENTRY(efi_exit32)
xorq %rax, %rax
movl %eax, %ds
movl %eax, %es
movl %eax, %ss

movq func_rt_ptr(%rip), %rax
push %rax
mov %rdi, %rax
ret
ENDPROC(efi_exit32)

.code32
/*
* EFI service pointer must be in %edi.
*
* The stack should represent the 32-bit calling convention.
*/
ENTRY(efi_enter32)
movl $__KERNEL_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %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

movl 44(%esp), %eax
movl %eax, 2(%eax)
lgdtl (%eax)

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

movl 48(%esp), %eax
pushl $__KERNEL_CS
pushl %eax

/* Enable paging */
movl %cr0, %eax
btsl $X86_CR0_PG_BIT, %eax
movl %eax, %cr0
lret
ENDPROC(efi_enter32)

.data
.balign 8
.global efi32_boot_gdt
efi32_boot_gdt: .word 0
.quad 0

save_gdt: .word 0
.quad 0
func_rt_ptr: .quad 0

.global efi_gdt64
efi_gdt64:
.word efi_gdt64_end - efi_gdt64
.long 0 /* Filled out by user */
.word 0
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x00af9a000000ffff /* __KERNEL_CS */
.quad 0x00cf92000000ffff /* __KERNEL_DS */
.quad 0x0080890000000000 /* TS descriptor */
.quad 0x0000000000000000 /* TS continued */
efi_gdt64_end:
#endif /* CONFIG_EFI_MIXED */

.data
ENTRY(efi_scratch)
.fill 3,8,0
Expand Down

0 comments on commit 0154416

Please sign in to comment.