2014-01-11 02:48:30 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2014 Intel Corporation; author Matt Fleming
|
x86/efi: Avoid triple faults during EFI mixed mode calls
Andy pointed out that if an NMI or MCE is received while we're in the
middle of an EFI mixed mode call a triple fault will occur. This can
happen, for example, when issuing an EFI mixed mode call while running
perf.
The reason for the triple fault is that we execute the mixed mode call
in 32-bit mode with paging disabled but with 64-bit kernel IDT handlers
installed throughout the call.
At Andy's suggestion, stop playing the games we currently do at runtime,
such as disabling paging and installing a 32-bit GDT for __KERNEL_CS. We
can simply switch to the __KERNEL32_CS descriptor before invoking
firmware services, and run in compatibility mode. This way, if an
NMI/MCE does occur the kernel IDT handler will execute correctly, since
it'll jump to __KERNEL_CS automatically.
However, this change is only possible post-ExitBootServices(). Before
then the firmware "owns" the machine and expects for its 32-bit IDT
handlers to be left intact to service interrupts, etc.
So, we now need to distinguish between early boot and runtime
invocations of EFI services. During early boot, we need to restore the
GDT that the firmware expects to be present. We can only jump to the
__KERNEL32_CS code segment for mixed mode calls after ExitBootServices()
has been invoked.
A liberal sprinkling of comments in the thunking code should make the
differences in early and late environments more apparent.
Reported-by: Andy Lutomirski <luto@amacapital.net>
Tested-by: Borislav Petkov <bp@suse.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
2015-01-13 23:25:00 +08:00
|
|
|
*
|
|
|
|
* Support for invoking 32-bit EFI runtime services from a 64-bit
|
|
|
|
* kernel.
|
|
|
|
*
|
|
|
|
* The below thunking functions are only used after ExitBootServices()
|
|
|
|
* has been called. This simplifies things considerably as compared with
|
|
|
|
* the early EFI thunking because we can leave all the kernel state
|
|
|
|
* intact (GDT, IDT, etc) and simply invoke the the 32-bit EFI runtime
|
|
|
|
* services from __KERNEL32_CS. This means we can continue to service
|
|
|
|
* interrupts across an EFI mixed mode call.
|
|
|
|
*
|
|
|
|
* We do however, need to handle the fact that we're running in a full
|
|
|
|
* 64-bit virtual address space. Things like the stack and instruction
|
|
|
|
* addresses need to be accessible by the 32-bit firmware, so we rely on
|
|
|
|
* using the identity mappings in the EFI page table to access the stack
|
|
|
|
* and kernel text (see efi_setup_page_tables()).
|
2014-01-11 02:48:30 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/linkage.h>
|
|
|
|
#include <asm/page_types.h>
|
x86/efi: Avoid triple faults during EFI mixed mode calls
Andy pointed out that if an NMI or MCE is received while we're in the
middle of an EFI mixed mode call a triple fault will occur. This can
happen, for example, when issuing an EFI mixed mode call while running
perf.
The reason for the triple fault is that we execute the mixed mode call
in 32-bit mode with paging disabled but with 64-bit kernel IDT handlers
installed throughout the call.
At Andy's suggestion, stop playing the games we currently do at runtime,
such as disabling paging and installing a 32-bit GDT for __KERNEL_CS. We
can simply switch to the __KERNEL32_CS descriptor before invoking
firmware services, and run in compatibility mode. This way, if an
NMI/MCE does occur the kernel IDT handler will execute correctly, since
it'll jump to __KERNEL_CS automatically.
However, this change is only possible post-ExitBootServices(). Before
then the firmware "owns" the machine and expects for its 32-bit IDT
handlers to be left intact to service interrupts, etc.
So, we now need to distinguish between early boot and runtime
invocations of EFI services. During early boot, we need to restore the
GDT that the firmware expects to be present. We can only jump to the
__KERNEL32_CS code segment for mixed mode calls after ExitBootServices()
has been invoked.
A liberal sprinkling of comments in the thunking code should make the
differences in early and late environments more apparent.
Reported-by: Andy Lutomirski <luto@amacapital.net>
Tested-by: Borislav Petkov <bp@suse.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
2015-01-13 23:25:00 +08:00
|
|
|
#include <asm/segment.h>
|
2014-01-11 02:48:30 +08:00
|
|
|
|
|
|
|
.text
|
|
|
|
.code64
|
|
|
|
ENTRY(efi64_thunk)
|
|
|
|
push %rbp
|
|
|
|
push %rbx
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Switch to 1:1 mapped 32-bit stack pointer.
|
|
|
|
*/
|
|
|
|
movq %rsp, efi_saved_sp(%rip)
|
|
|
|
movq efi_scratch+25(%rip), %rsp
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the physical address of the kernel text.
|
|
|
|
*/
|
|
|
|
movq $__START_KERNEL_map, %rax
|
|
|
|
subq phys_base(%rip), %rax
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Push some physical addresses onto the stack. This is easier
|
|
|
|
* to do now in a code64 section while the assembler can address
|
|
|
|
* 64-bit values. Note that all the addresses on the stack are
|
|
|
|
* 32-bit.
|
|
|
|
*/
|
|
|
|
subq $16, %rsp
|
|
|
|
leaq efi_exit32(%rip), %rbx
|
|
|
|
subq %rax, %rbx
|
|
|
|
movl %ebx, 8(%rsp)
|
|
|
|
|
|
|
|
leaq __efi64_thunk(%rip), %rbx
|
|
|
|
subq %rax, %rbx
|
|
|
|
call *%rbx
|
|
|
|
|
|
|
|
movq efi_saved_sp(%rip), %rsp
|
|
|
|
pop %rbx
|
|
|
|
pop %rbp
|
|
|
|
retq
|
|
|
|
ENDPROC(efi64_thunk)
|
|
|
|
|
x86/efi: Avoid triple faults during EFI mixed mode calls
Andy pointed out that if an NMI or MCE is received while we're in the
middle of an EFI mixed mode call a triple fault will occur. This can
happen, for example, when issuing an EFI mixed mode call while running
perf.
The reason for the triple fault is that we execute the mixed mode call
in 32-bit mode with paging disabled but with 64-bit kernel IDT handlers
installed throughout the call.
At Andy's suggestion, stop playing the games we currently do at runtime,
such as disabling paging and installing a 32-bit GDT for __KERNEL_CS. We
can simply switch to the __KERNEL32_CS descriptor before invoking
firmware services, and run in compatibility mode. This way, if an
NMI/MCE does occur the kernel IDT handler will execute correctly, since
it'll jump to __KERNEL_CS automatically.
However, this change is only possible post-ExitBootServices(). Before
then the firmware "owns" the machine and expects for its 32-bit IDT
handlers to be left intact to service interrupts, etc.
So, we now need to distinguish between early boot and runtime
invocations of EFI services. During early boot, we need to restore the
GDT that the firmware expects to be present. We can only jump to the
__KERNEL32_CS code segment for mixed mode calls after ExitBootServices()
has been invoked.
A liberal sprinkling of comments in the thunking code should make the
differences in early and late environments more apparent.
Reported-by: Andy Lutomirski <luto@amacapital.net>
Tested-by: Borislav Petkov <bp@suse.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
2015-01-13 23:25:00 +08:00
|
|
|
/*
|
|
|
|
* We run this function from the 1:1 mapping.
|
|
|
|
*
|
|
|
|
* This function must be invoked with a 1:1 mapped stack.
|
|
|
|
*/
|
|
|
|
ENTRY(__efi64_thunk)
|
|
|
|
movl %ds, %eax
|
|
|
|
push %rax
|
|
|
|
movl %es, %eax
|
|
|
|
push %rax
|
|
|
|
movl %ss, %eax
|
|
|
|
push %rax
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
leaq 1f(%rip), %rbx
|
|
|
|
movq %rbx, func_rt_ptr(%rip)
|
|
|
|
|
|
|
|
/* Switch to 32-bit descriptor */
|
|
|
|
pushq $__KERNEL32_CS
|
|
|
|
leaq efi_enter32(%rip), %rax
|
|
|
|
pushq %rax
|
|
|
|
lretq
|
|
|
|
|
|
|
|
1: addq $32, %rsp
|
|
|
|
|
|
|
|
pop %rbx
|
|
|
|
movl %ebx, %ss
|
|
|
|
pop %rbx
|
|
|
|
movl %ebx, %es
|
|
|
|
pop %rbx
|
|
|
|
movl %ebx, %ds
|
2014-01-11 02:48:30 +08:00
|
|
|
|
x86/efi: Avoid triple faults during EFI mixed mode calls
Andy pointed out that if an NMI or MCE is received while we're in the
middle of an EFI mixed mode call a triple fault will occur. This can
happen, for example, when issuing an EFI mixed mode call while running
perf.
The reason for the triple fault is that we execute the mixed mode call
in 32-bit mode with paging disabled but with 64-bit kernel IDT handlers
installed throughout the call.
At Andy's suggestion, stop playing the games we currently do at runtime,
such as disabling paging and installing a 32-bit GDT for __KERNEL_CS. We
can simply switch to the __KERNEL32_CS descriptor before invoking
firmware services, and run in compatibility mode. This way, if an
NMI/MCE does occur the kernel IDT handler will execute correctly, since
it'll jump to __KERNEL_CS automatically.
However, this change is only possible post-ExitBootServices(). Before
then the firmware "owns" the machine and expects for its 32-bit IDT
handlers to be left intact to service interrupts, etc.
So, we now need to distinguish between early boot and runtime
invocations of EFI services. During early boot, we need to restore the
GDT that the firmware expects to be present. We can only jump to the
__KERNEL32_CS code segment for mixed mode calls after ExitBootServices()
has been invoked.
A liberal sprinkling of comments in the thunking code should make the
differences in early and late environments more apparent.
Reported-by: Andy Lutomirski <luto@amacapital.net>
Tested-by: Borislav Petkov <bp@suse.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
2015-01-13 23:25:00 +08:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
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
|
|
|
|
|
|
|
|
call *%edi
|
|
|
|
|
|
|
|
/* We must preserve return value */
|
|
|
|
movl %eax, %edi
|
|
|
|
|
|
|
|
movl 72(%esp), %eax
|
|
|
|
pushl $__KERNEL_CS
|
|
|
|
pushl %eax
|
|
|
|
|
|
|
|
lret
|
|
|
|
ENDPROC(efi_enter32)
|
|
|
|
|
|
|
|
.data
|
|
|
|
.balign 8
|
|
|
|
func_rt_ptr: .quad 0
|
2014-01-11 02:48:30 +08:00
|
|
|
efi_saved_sp: .quad 0
|