forked from Minki/linux
96738c69a7
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>
99 lines
1.9 KiB
ArmAsm
99 lines
1.9 KiB
ArmAsm
/*
|
|
* Function calling ABI conversion from Linux to EFI for x86_64
|
|
*
|
|
* Copyright (C) 2007 Intel Corp
|
|
* Bibo Mao <bibo.mao@intel.com>
|
|
* Huang Ying <ying.huang@intel.com>
|
|
*/
|
|
|
|
#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; \
|
|
subq $0x70, %rsp; \
|
|
and $~0xf, %rsp; \
|
|
mov %rax, (%rsp); \
|
|
mov %cr0, %rax; \
|
|
clts; \
|
|
mov %rax, 0x8(%rsp); \
|
|
movaps %xmm0, 0x60(%rsp); \
|
|
movaps %xmm1, 0x50(%rsp); \
|
|
movaps %xmm2, 0x40(%rsp); \
|
|
movaps %xmm3, 0x30(%rsp); \
|
|
movaps %xmm4, 0x20(%rsp); \
|
|
movaps %xmm5, 0x10(%rsp)
|
|
|
|
#define RESTORE_XMM \
|
|
movaps 0x60(%rsp), %xmm0; \
|
|
movaps 0x50(%rsp), %xmm1; \
|
|
movaps 0x40(%rsp), %xmm2; \
|
|
movaps 0x30(%rsp), %xmm3; \
|
|
movaps 0x20(%rsp), %xmm4; \
|
|
movaps 0x10(%rsp), %xmm5; \
|
|
mov 0x8(%rsp), %rsi; \
|
|
mov %rsi, %cr0; \
|
|
mov (%rsp), %rsp
|
|
|
|
/* stolen from gcc */
|
|
.macro FLUSH_TLB_ALL
|
|
movq %r15, efi_scratch(%rip)
|
|
movq %r14, efi_scratch+8(%rip)
|
|
movq %cr4, %r15
|
|
movq %r15, %r14
|
|
andb $0x7f, %r14b
|
|
movq %r14, %cr4
|
|
movq %r15, %cr4
|
|
movq efi_scratch+8(%rip), %r14
|
|
movq efi_scratch(%rip), %r15
|
|
.endm
|
|
|
|
.macro SWITCH_PGT
|
|
cmpb $0, efi_scratch+24(%rip)
|
|
je 1f
|
|
movq %r15, efi_scratch(%rip) # r15
|
|
# save previous CR3
|
|
movq %cr3, %r15
|
|
movq %r15, efi_scratch+8(%rip) # prev_cr3
|
|
movq efi_scratch+16(%rip), %r15 # EFI pgt
|
|
movq %r15, %cr3
|
|
1:
|
|
.endm
|
|
|
|
.macro RESTORE_PGT
|
|
cmpb $0, efi_scratch+24(%rip)
|
|
je 2f
|
|
movq efi_scratch+8(%rip), %r15
|
|
movq %r15, %cr3
|
|
movq efi_scratch(%rip), %r15
|
|
FLUSH_TLB_ALL
|
|
2:
|
|
.endm
|
|
|
|
ENTRY(efi_call)
|
|
SAVE_XMM
|
|
mov (%rsp), %rax
|
|
mov 8(%rax), %rax
|
|
subq $48, %rsp
|
|
mov %r9, 32(%rsp)
|
|
mov %rax, 40(%rsp)
|
|
mov %r8, %r9
|
|
mov %rcx, %r8
|
|
mov %rsi, %rcx
|
|
SWITCH_PGT
|
|
call *%rdi
|
|
RESTORE_PGT
|
|
addq $48, %rsp
|
|
RESTORE_XMM
|
|
ret
|
|
ENDPROC(efi_call)
|
|
|
|
.data
|
|
ENTRY(efi_scratch)
|
|
.fill 3,8,0
|
|
.byte 0
|
|
.quad 0
|