x86/entry: Add kernel IBRS implementation
commit 2dbb887e875b1de3ca8f40ddf26bcfe55798c609 upstream. Implement Kernel IBRS - currently the only known option to mitigate RSB underflow speculation issues on Skylake hardware. Note: since IBRS_ENTER requires fuller context established than UNTRAIN_RET, it must be placed after it. However, since UNTRAIN_RET itself implies a RET, it must come after IBRS_ENTER. This means IBRS_ENTER needs to also move UNTRAIN_RET. Note 2: KERNEL_IBRS is sub-optimal for XenPV. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Borislav Petkov <bp@suse.de> Reviewed-by: Josh Poimboeuf <jpoimboe@kernel.org> Signed-off-by: Borislav Petkov <bp@suse.de> [cascardo: conflict at arch/x86/entry/entry_64.S, skip_r11rcx] [cascardo: conflict at arch/x86/entry/entry_64_compat.S] [cascardo: conflict fixups, no ANNOTATE_NOENDBR] [cascardo: entry fixups because of missing UNTRAIN_RET] [cascardo: conflicts on fsgsbase] Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
fd32a31553
commit
a3111faed5
@ -6,6 +6,8 @@
|
|||||||
#include <asm/percpu.h>
|
#include <asm/percpu.h>
|
||||||
#include <asm/asm-offsets.h>
|
#include <asm/asm-offsets.h>
|
||||||
#include <asm/processor-flags.h>
|
#include <asm/processor-flags.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
#include <asm/nospec-branch.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
@ -308,6 +310,62 @@ For 32-bit we have the following conventions - kernel is built with
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IBRS kernel mitigation for Spectre_v2.
|
||||||
|
*
|
||||||
|
* Assumes full context is established (PUSH_REGS, CR3 and GS) and it clobbers
|
||||||
|
* the regs it uses (AX, CX, DX). Must be called before the first RET
|
||||||
|
* instruction (NOTE! UNTRAIN_RET includes a RET instruction)
|
||||||
|
*
|
||||||
|
* The optional argument is used to save/restore the current value,
|
||||||
|
* which is used on the paranoid paths.
|
||||||
|
*
|
||||||
|
* Assumes x86_spec_ctrl_{base,current} to have SPEC_CTRL_IBRS set.
|
||||||
|
*/
|
||||||
|
.macro IBRS_ENTER save_reg
|
||||||
|
ALTERNATIVE "jmp .Lend_\@", "", X86_FEATURE_KERNEL_IBRS
|
||||||
|
movl $MSR_IA32_SPEC_CTRL, %ecx
|
||||||
|
|
||||||
|
.ifnb \save_reg
|
||||||
|
rdmsr
|
||||||
|
shl $32, %rdx
|
||||||
|
or %rdx, %rax
|
||||||
|
mov %rax, \save_reg
|
||||||
|
test $SPEC_CTRL_IBRS, %eax
|
||||||
|
jz .Ldo_wrmsr_\@
|
||||||
|
lfence
|
||||||
|
jmp .Lend_\@
|
||||||
|
.Ldo_wrmsr_\@:
|
||||||
|
.endif
|
||||||
|
|
||||||
|
movq PER_CPU_VAR(x86_spec_ctrl_current), %rdx
|
||||||
|
movl %edx, %eax
|
||||||
|
shr $32, %rdx
|
||||||
|
wrmsr
|
||||||
|
.Lend_\@:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Similar to IBRS_ENTER, requires KERNEL GS,CR3 and clobbers (AX, CX, DX)
|
||||||
|
* regs. Must be called after the last RET.
|
||||||
|
*/
|
||||||
|
.macro IBRS_EXIT save_reg
|
||||||
|
ALTERNATIVE "jmp .Lend_\@", "", X86_FEATURE_KERNEL_IBRS
|
||||||
|
movl $MSR_IA32_SPEC_CTRL, %ecx
|
||||||
|
|
||||||
|
.ifnb \save_reg
|
||||||
|
mov \save_reg, %rdx
|
||||||
|
.else
|
||||||
|
movq PER_CPU_VAR(x86_spec_ctrl_current), %rdx
|
||||||
|
andl $(~SPEC_CTRL_IBRS), %edx
|
||||||
|
.endif
|
||||||
|
|
||||||
|
movl %edx, %eax
|
||||||
|
shr $32, %rdx
|
||||||
|
wrmsr
|
||||||
|
.Lend_\@:
|
||||||
|
.endm
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mitigate Spectre v1 for conditional swapgs code paths.
|
* Mitigate Spectre v1 for conditional swapgs code paths.
|
||||||
*
|
*
|
||||||
|
@ -172,6 +172,10 @@ GLOBAL(entry_SYSCALL_64_after_hwframe)
|
|||||||
/* IRQs are off. */
|
/* IRQs are off. */
|
||||||
movq %rax, %rdi
|
movq %rax, %rdi
|
||||||
movq %rsp, %rsi
|
movq %rsp, %rsi
|
||||||
|
|
||||||
|
/* clobbers %rax, make sure it is after saving the syscall nr */
|
||||||
|
IBRS_ENTER
|
||||||
|
|
||||||
call do_syscall_64 /* returns with IRQs disabled */
|
call do_syscall_64 /* returns with IRQs disabled */
|
||||||
|
|
||||||
TRACE_IRQS_IRETQ /* we're about to change IF */
|
TRACE_IRQS_IRETQ /* we're about to change IF */
|
||||||
@ -248,6 +252,7 @@ GLOBAL(entry_SYSCALL_64_after_hwframe)
|
|||||||
* perf profiles. Nothing jumps here.
|
* perf profiles. Nothing jumps here.
|
||||||
*/
|
*/
|
||||||
syscall_return_via_sysret:
|
syscall_return_via_sysret:
|
||||||
|
IBRS_EXIT
|
||||||
POP_REGS pop_rdi=0
|
POP_REGS pop_rdi=0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -621,6 +626,7 @@ GLOBAL(retint_user)
|
|||||||
TRACE_IRQS_IRETQ
|
TRACE_IRQS_IRETQ
|
||||||
|
|
||||||
GLOBAL(swapgs_restore_regs_and_return_to_usermode)
|
GLOBAL(swapgs_restore_regs_and_return_to_usermode)
|
||||||
|
IBRS_EXIT
|
||||||
#ifdef CONFIG_DEBUG_ENTRY
|
#ifdef CONFIG_DEBUG_ENTRY
|
||||||
/* Assert that pt_regs indicates user mode. */
|
/* Assert that pt_regs indicates user mode. */
|
||||||
testb $3, CS(%rsp)
|
testb $3, CS(%rsp)
|
||||||
@ -1247,7 +1253,13 @@ ENTRY(paranoid_entry)
|
|||||||
*/
|
*/
|
||||||
FENCE_SWAPGS_KERNEL_ENTRY
|
FENCE_SWAPGS_KERNEL_ENTRY
|
||||||
|
|
||||||
ret
|
/*
|
||||||
|
* Once we have CR3 and %GS setup save and set SPEC_CTRL. Just like
|
||||||
|
* CR3 above, keep the old value in a callee saved register.
|
||||||
|
*/
|
||||||
|
IBRS_ENTER save_reg=%r15
|
||||||
|
|
||||||
|
RET
|
||||||
END(paranoid_entry)
|
END(paranoid_entry)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1275,12 +1287,20 @@ ENTRY(paranoid_exit)
|
|||||||
jmp .Lparanoid_exit_restore
|
jmp .Lparanoid_exit_restore
|
||||||
.Lparanoid_exit_no_swapgs:
|
.Lparanoid_exit_no_swapgs:
|
||||||
TRACE_IRQS_IRETQ_DEBUG
|
TRACE_IRQS_IRETQ_DEBUG
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must restore IBRS state before both CR3 and %GS since we need access
|
||||||
|
* to the per-CPU x86_spec_ctrl_shadow variable.
|
||||||
|
*/
|
||||||
|
IBRS_EXIT save_reg=%r15
|
||||||
|
|
||||||
/* Always restore stashed CR3 value (see paranoid_entry) */
|
/* Always restore stashed CR3 value (see paranoid_entry) */
|
||||||
RESTORE_CR3 scratch_reg=%rbx save_reg=%r14
|
RESTORE_CR3 scratch_reg=%rbx save_reg=%r14
|
||||||
.Lparanoid_exit_restore:
|
.Lparanoid_exit_restore:
|
||||||
jmp restore_regs_and_return_to_kernel
|
jmp restore_regs_and_return_to_kernel
|
||||||
END(paranoid_exit)
|
END(paranoid_exit)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save all registers in pt_regs, and switch GS if needed.
|
* Save all registers in pt_regs, and switch GS if needed.
|
||||||
*/
|
*/
|
||||||
@ -1300,6 +1320,7 @@ ENTRY(error_entry)
|
|||||||
FENCE_SWAPGS_USER_ENTRY
|
FENCE_SWAPGS_USER_ENTRY
|
||||||
/* We have user CR3. Change to kernel CR3. */
|
/* We have user CR3. Change to kernel CR3. */
|
||||||
SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
|
SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
|
||||||
|
IBRS_ENTER
|
||||||
|
|
||||||
.Lerror_entry_from_usermode_after_swapgs:
|
.Lerror_entry_from_usermode_after_swapgs:
|
||||||
/* Put us onto the real thread stack. */
|
/* Put us onto the real thread stack. */
|
||||||
@ -1355,6 +1376,7 @@ ENTRY(error_entry)
|
|||||||
SWAPGS
|
SWAPGS
|
||||||
FENCE_SWAPGS_USER_ENTRY
|
FENCE_SWAPGS_USER_ENTRY
|
||||||
SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
|
SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
|
||||||
|
IBRS_ENTER
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pretend that the exception came from user mode: set up pt_regs
|
* Pretend that the exception came from user mode: set up pt_regs
|
||||||
@ -1460,6 +1482,8 @@ ENTRY(nmi)
|
|||||||
PUSH_AND_CLEAR_REGS rdx=(%rdx)
|
PUSH_AND_CLEAR_REGS rdx=(%rdx)
|
||||||
ENCODE_FRAME_POINTER
|
ENCODE_FRAME_POINTER
|
||||||
|
|
||||||
|
IBRS_ENTER
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At this point we no longer need to worry about stack damage
|
* At this point we no longer need to worry about stack damage
|
||||||
* due to nesting -- we're on the normal thread stack and we're
|
* due to nesting -- we're on the normal thread stack and we're
|
||||||
@ -1683,6 +1707,9 @@ end_repeat_nmi:
|
|||||||
movq $-1, %rsi
|
movq $-1, %rsi
|
||||||
call do_nmi
|
call do_nmi
|
||||||
|
|
||||||
|
/* Always restore stashed SPEC_CTRL value (see paranoid_entry) */
|
||||||
|
IBRS_EXIT save_reg=%r15
|
||||||
|
|
||||||
/* Always restore stashed CR3 value (see paranoid_entry) */
|
/* Always restore stashed CR3 value (see paranoid_entry) */
|
||||||
RESTORE_CR3 scratch_reg=%r15 save_reg=%r14
|
RESTORE_CR3 scratch_reg=%r15 save_reg=%r14
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
*
|
*
|
||||||
* Copyright 2000-2002 Andi Kleen, SuSE Labs.
|
* Copyright 2000-2002 Andi Kleen, SuSE Labs.
|
||||||
*/
|
*/
|
||||||
#include "calling.h"
|
|
||||||
#include <asm/asm-offsets.h>
|
#include <asm/asm-offsets.h>
|
||||||
#include <asm/current.h>
|
#include <asm/current.h>
|
||||||
#include <asm/errno.h>
|
#include <asm/errno.h>
|
||||||
@ -17,6 +16,8 @@
|
|||||||
#include <linux/linkage.h>
|
#include <linux/linkage.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
|
||||||
|
#include "calling.h"
|
||||||
|
|
||||||
.section .entry.text, "ax"
|
.section .entry.text, "ax"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -106,6 +107,8 @@ ENTRY(entry_SYSENTER_compat)
|
|||||||
xorl %r15d, %r15d /* nospec r15 */
|
xorl %r15d, %r15d /* nospec r15 */
|
||||||
cld
|
cld
|
||||||
|
|
||||||
|
IBRS_ENTER
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SYSENTER doesn't filter flags, so we need to clear NT and AC
|
* SYSENTER doesn't filter flags, so we need to clear NT and AC
|
||||||
* ourselves. To save a few cycles, we can check whether
|
* ourselves. To save a few cycles, we can check whether
|
||||||
@ -253,6 +256,8 @@ GLOBAL(entry_SYSCALL_compat_after_hwframe)
|
|||||||
*/
|
*/
|
||||||
TRACE_IRQS_OFF
|
TRACE_IRQS_OFF
|
||||||
|
|
||||||
|
IBRS_ENTER
|
||||||
|
|
||||||
movq %rsp, %rdi
|
movq %rsp, %rdi
|
||||||
call do_fast_syscall_32
|
call do_fast_syscall_32
|
||||||
/* XEN PV guests always use IRET path */
|
/* XEN PV guests always use IRET path */
|
||||||
@ -267,6 +272,9 @@ sysret32_from_system_call:
|
|||||||
*/
|
*/
|
||||||
STACKLEAK_ERASE
|
STACKLEAK_ERASE
|
||||||
TRACE_IRQS_ON /* User mode traces as IRQs on. */
|
TRACE_IRQS_ON /* User mode traces as IRQs on. */
|
||||||
|
|
||||||
|
IBRS_EXIT
|
||||||
|
|
||||||
movq RBX(%rsp), %rbx /* pt_regs->rbx */
|
movq RBX(%rsp), %rbx /* pt_regs->rbx */
|
||||||
movq RBP(%rsp), %rbp /* pt_regs->rbp */
|
movq RBP(%rsp), %rbp /* pt_regs->rbp */
|
||||||
movq EFLAGS(%rsp), %r11 /* pt_regs->flags (in r11) */
|
movq EFLAGS(%rsp), %r11 /* pt_regs->flags (in r11) */
|
||||||
@ -408,6 +416,7 @@ ENTRY(entry_INT80_compat)
|
|||||||
* gate turned them off.
|
* gate turned them off.
|
||||||
*/
|
*/
|
||||||
TRACE_IRQS_OFF
|
TRACE_IRQS_OFF
|
||||||
|
IBRS_ENTER
|
||||||
|
|
||||||
movq %rsp, %rdi
|
movq %rsp, %rdi
|
||||||
call do_int80_syscall_32
|
call do_int80_syscall_32
|
||||||
|
@ -203,7 +203,7 @@
|
|||||||
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
|
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
|
||||||
#define X86_FEATURE_SME ( 7*32+10) /* AMD Secure Memory Encryption */
|
#define X86_FEATURE_SME ( 7*32+10) /* AMD Secure Memory Encryption */
|
||||||
#define X86_FEATURE_PTI ( 7*32+11) /* Kernel Page Table Isolation enabled */
|
#define X86_FEATURE_PTI ( 7*32+11) /* Kernel Page Table Isolation enabled */
|
||||||
/* FREE! ( 7*32+12) */
|
#define X86_FEATURE_KERNEL_IBRS ( 7*32+12) /* "" Set/clear IBRS on kernel entry/exit */
|
||||||
/* FREE! ( 7*32+13) */
|
/* FREE! ( 7*32+13) */
|
||||||
#define X86_FEATURE_INTEL_PPIN ( 7*32+14) /* Intel Processor Inventory Number */
|
#define X86_FEATURE_INTEL_PPIN ( 7*32+14) /* Intel Processor Inventory Number */
|
||||||
#define X86_FEATURE_CDP_L2 ( 7*32+15) /* Code and Data Prioritization L2 */
|
#define X86_FEATURE_CDP_L2 ( 7*32+15) /* Code and Data Prioritization L2 */
|
||||||
|
Loading…
Reference in New Issue
Block a user