Borislav Petkov 727875
From: Peter Zijlstra <peterz@infradead.org>
Borislav Petkov 727875
Date: Tue, 14 Jun 2022 23:15:53 +0200
Borislav Petkov 727875
Subject: x86/entry: Add kernel IBRS implementation
Borislav Petkov 727875
Git-commit: 2dbb887e875b1de3ca8f40ddf26bcfe55798c609
Borislav Petkov 727875
Patch-mainline: v5.19-rc7
Borislav Petkov 727875
References: bsc#1199657 CVE-2022-29900 CVE-2022-29901
Borislav Petkov 727875
Borislav Petkov 727875
Implement Kernel IBRS - currently the only known option to mitigate RSB
Borislav Petkov 727875
underflow speculation issues on Skylake hardware.
Borislav Petkov 727875
Borislav Petkov 727875
Note: since IBRS_ENTER requires fuller context established than
Borislav Petkov 727875
UNTRAIN_RET, it must be placed after it. However, since UNTRAIN_RET
Borislav Petkov 727875
itself implies a RET, it must come after IBRS_ENTER. This means
Borislav Petkov 727875
IBRS_ENTER needs to also move UNTRAIN_RET.
Borislav Petkov 727875
Borislav Petkov 727875
Note 2: KERNEL_IBRS is sub-optimal for XenPV.
Borislav Petkov 727875
Borislav Petkov 727875
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Borislav Petkov 727875
Signed-off-by: Borislav Petkov <bp@suse.de>
Borislav Petkov 727875
Reviewed-by: Josh Poimboeuf <jpoimboe@kernel.org>
Borislav Petkov 727875
Signed-off-by: Borislav Petkov <bp@suse.de>
Borislav Petkov 727875
---
Borislav Petkov 727875
 arch/x86/entry/calling.h           |   58 +++++++++++++++++++++++++++++++++++++
Borislav Petkov 727875
 arch/x86/entry/entry_64.S          |   41 ++++++++++++++++++++++++--
Borislav Petkov 727875
 arch/x86/entry/entry_64_compat.S   |   12 +++++--
Borislav Petkov 727875
 arch/x86/include/asm/cpufeatures.h |    2 -
Borislav Petkov 727875
 4 files changed, 106 insertions(+), 7 deletions(-)
Borislav Petkov 727875
Borislav Petkov 727875
--- a/arch/x86/entry/calling.h
Borislav Petkov 727875
+++ b/arch/x86/entry/calling.h
Borislav Petkov 727875
@@ -5,6 +5,8 @@
Borislav Petkov 727875
 #include <asm/percpu.h>
Borislav Petkov 727875
 #include <asm/asm-offsets.h>
Borislav Petkov 727875
 #include <asm/processor-flags.h>
Borislav Petkov 727875
+#include <asm/msr.h>
Borislav Petkov 727875
+#include <asm/nospec-branch.h>
Borislav Petkov 727875
 
Borislav Petkov 727875
 /*
Borislav Petkov 727875
 
Borislav Petkov 727875
@@ -336,6 +338,62 @@ For 32-bit we have the following convent
Borislav Petkov 727875
 #endif
Borislav Petkov 727875
 
Borislav Petkov 727875
 /*
Borislav Petkov 727875
+ * IBRS kernel mitigation for Spectre_v2.
Borislav Petkov 727875
+ *
Borislav Petkov 727875
+ * Assumes full context is established (PUSH_REGS, CR3 and GS) and it clobbers
Borislav Petkov 727875
+ * the regs it uses (AX, CX, DX). Must be called before the first RET
Borislav Petkov 727875
+ * instruction (NOTE! UNTRAIN_RET includes a RET instruction)
Borislav Petkov 727875
+ *
Borislav Petkov 727875
+ * The optional argument is used to save/restore the current value,
Borislav Petkov 727875
+ * which is used on the paranoid paths.
Borislav Petkov 727875
+ *
Borislav Petkov 727875
+ * Assumes x86_spec_ctrl_{base,current} to have SPEC_CTRL_IBRS set.
Borislav Petkov 727875
+ */
Borislav Petkov 727875
+.macro IBRS_ENTER save_reg
Borislav Petkov 727875
+	ALTERNATIVE "jmp .Lend_\@", "", X86_FEATURE_KERNEL_IBRS
Borislav Petkov 727875
+	movl	$MSR_IA32_SPEC_CTRL, %ecx
Borislav Petkov 727875
+
Borislav Petkov 727875
+.ifnb \save_reg
Borislav Petkov 727875
+	rdmsr
Borislav Petkov 727875
+	shl	$32, %rdx
Borislav Petkov 727875
+	or	%rdx, %rax
Borislav Petkov 727875
+	mov	%rax, \save_reg
Borislav Petkov 727875
+	test	$SPEC_CTRL_IBRS, %eax
Borislav Petkov 727875
+	jz	.Ldo_wrmsr_\@
Borislav Petkov 727875
+	lfence
Borislav Petkov 727875
+	jmp	.Lend_\@
Borislav Petkov 727875
+.Ldo_wrmsr_\@:
Borislav Petkov 727875
+.endif
Borislav Petkov 727875
+
Borislav Petkov 727875
+	movq	PER_CPU_VAR(x86_spec_ctrl_current), %rdx
Borislav Petkov 727875
+	movl	%edx, %eax
Borislav Petkov 727875
+	shr	$32, %rdx
Borislav Petkov 727875
+	wrmsr
Borislav Petkov 727875
+.Lend_\@:
Borislav Petkov 727875
+.endm
Borislav Petkov 727875
+
Borislav Petkov 727875
+/*
Borislav Petkov 727875
+ * Similar to IBRS_ENTER, requires KERNEL GS,CR3 and clobbers (AX, CX, DX)
Borislav Petkov 727875
+ * regs. Must be called after the last RET.
Borislav Petkov 727875
+ */
Borislav Petkov 727875
+.macro IBRS_EXIT save_reg
Borislav Petkov 727875
+	ALTERNATIVE "jmp .Lend_\@", "", X86_FEATURE_KERNEL_IBRS
Borislav Petkov 727875
+	movl	$MSR_IA32_SPEC_CTRL, %ecx
Borislav Petkov 727875
+
Borislav Petkov 727875
+.ifnb \save_reg
Borislav Petkov 727875
+	mov	\save_reg, %rdx
Borislav Petkov 727875
+.else
Borislav Petkov 727875
+	movq	PER_CPU_VAR(x86_spec_ctrl_current), %rdx
Borislav Petkov 727875
+	andl	$(~SPEC_CTRL_IBRS), %edx
Borislav Petkov 727875
+.endif
Borislav Petkov 727875
+
Borislav Petkov 727875
+	movl	%edx, %eax
Borislav Petkov 727875
+	shr	$32, %rdx
Borislav Petkov 727875
+	wrmsr
Borislav Petkov 727875
+.Lend_\@:
Borislav Petkov 727875
+.endm
Borislav Petkov 727875
+
Borislav Petkov 727875
+/*
Borislav Petkov 727875
  * Mitigate Spectre v1 for conditional swapgs code paths.
Borislav Petkov 727875
  *
Borislav Petkov 727875
  * FENCE_SWAPGS_USER_ENTRY is used in the user entry swapgs code path, to
Borislav Petkov 727875
--- a/arch/x86/entry/entry_64_compat.S
Borislav Petkov 727875
+++ b/arch/x86/entry/entry_64_compat.S
Borislav Petkov 727875
@@ -3,7 +3,6 @@
Borislav Petkov 727875
  *
Borislav Petkov 727875
  * Copyright 2000-2002 Andi Kleen, SuSE Labs.
Borislav Petkov 727875
  */
Borislav Petkov 727875
-#include "calling.h"
Borislav Petkov 727875
 #include <asm/asm-offsets.h>
Borislav Petkov 727875
 #include <asm/current.h>
Borislav Petkov 727875
 #include <asm/errno.h>
Borislav Petkov 727875
@@ -17,6 +16,8 @@
Borislav Petkov 727875
 #include <linux/linkage.h>
Borislav Petkov 727875
 #include <linux/err.h>
Borislav Petkov 727875
 
Borislav Petkov 727875
+#include "calling.h"
Borislav Petkov 727875
+
Borislav Petkov 727875
 	.section .entry.text, "ax"
Borislav Petkov 727875
 
Borislav Petkov 727875
 /*
Borislav Petkov 727875
@@ -106,6 +107,7 @@ ENTRY(entry_SYSENTER_compat)
Borislav Petkov 727875
 	xorl	%r15d, %r15d		/* nospec   r15 */
Borislav Petkov 727875
 	cld
Borislav Petkov 727875
 
Borislav Petkov 727875
+	IBRS_ENTER
Borislav Petkov 727875
 	UNTRAIN_RET
Borislav Petkov 727875
 
Borislav Petkov 727875
 	/*
Borislav Petkov 727875
@@ -252,6 +254,9 @@ GLOBAL(entry_SYSCALL_compat_after_hwfram
Borislav Petkov 727875
 	 */
Borislav Petkov 727875
 	TRACE_IRQS_OFF
Borislav Petkov 727875
 
Borislav Petkov 727875
+	IBRS_ENTER
Borislav Petkov 727875
+	UNTRAIN_RET
Borislav Petkov 727875
+
Borislav Petkov 727875
 	movq	%rsp, %rdi
Borislav Petkov 727875
 	call	do_fast_syscall_32
Borislav Petkov 727875
 	/* XEN PV guests always use IRET path */
Borislav Petkov 727875
@@ -261,6 +266,8 @@ GLOBAL(entry_SYSCALL_compat_after_hwfram
Borislav Petkov 727875
 	/* Opportunistic SYSRET */
Borislav Petkov 727875
 sysret32_from_system_call:
Borislav Petkov 727875
 	TRACE_IRQS_ON			/* User mode traces as IRQs on. */
Borislav Petkov 727875
+	IBRS_EXIT
Borislav Petkov 727875
+
Borislav Petkov 727875
 	movq	RBX(%rsp), %rbx		/* pt_regs->rbx */
Borislav Petkov 727875
 	movq	RBP(%rsp), %rbp		/* pt_regs->rbp */
Borislav Petkov 727875
 	movq	EFLAGS(%rsp), %r11	/* pt_regs->flags (in r11) */
Borislav Petkov 727875
@@ -321,8 +328,6 @@ ENTRY(switch_to_thread_stack_compat)
Borislav Petkov 727875
 	ALTERNATIVE "movq %rsp, %rdi", "jmp .Lcompat_keep_stack", X86_FEATURE_XENPV
Borislav Petkov 727875
 	movq    %rsp, %rdi
Borislav Petkov 727875
 	movq    PER_CPU_VAR(cpu_current_top_of_stack), %rsp
Borislav Petkov 727875
-	/* Restrict Indirect Branch Speculation */
Borislav Petkov 727875
-	RESTRICT_IB_SPEC
Borislav Petkov 727875
 	UNWIND_HINT sp_offset=16 sp_reg=ORC_REG_DI
Borislav Petkov 727875
 
Borislav Petkov 727875
 	pushq   7*8(%rdi)		/* regs->ss */
Borislav Petkov 727875
@@ -419,6 +424,7 @@ ENTRY(entry_INT80_compat)
Borislav Petkov 727875
 	 */
Borislav Petkov 727875
 	TRACE_IRQS_OFF
Borislav Petkov 727875
 
Borislav Petkov 727875
+	IBRS_ENTER
Borislav Petkov 727875
 	UNTRAIN_RET
Borislav Petkov 727875
 
Borislav Petkov 727875
 	movq	%rsp, %rdi
Borislav Petkov 727875
--- a/arch/x86/entry/entry_64.S
Borislav Petkov 727875
+++ b/arch/x86/entry/entry_64.S
Borislav Petkov 727875
@@ -230,6 +230,8 @@ GLOBAL(entry_SYSCALL_64_after_hwframe)
Borislav Petkov 727875
 	/* IRQs are off. */
Borislav Petkov 727875
 	movq	%rsp, %rdi
Borislav Petkov 727875
 
Borislav Petkov 727875
+	/* clobbers %rax, make sure it is after saving the syscall nr */
Borislav Petkov 727875
+	IBRS_ENTER
Borislav Petkov 727875
 	UNTRAIN_RET
Borislav Petkov 727875
 
Borislav Petkov 727875
 	call	do_syscall_64		/* returns with IRQs disabled */
Borislav Petkov 727875
@@ -302,6 +304,7 @@ GLOBAL(entry_SYSCALL_64_after_hwframe)
Borislav Petkov 727875
 	 * perf profiles. Nothing jumps here.
Borislav Petkov 727875
 	 */
Borislav Petkov 727875
 syscall_return_via_sysret:
Borislav Petkov 727875
+	IBRS_EXIT
Borislav Petkov 727875
 	POP_REGS pop_rdi=0
Borislav Petkov 727875
 
Borislav Petkov 727875
 	/*
Borislav Petkov 727875
@@ -554,6 +557,9 @@ END(irq_entries_start)
Borislav Petkov 727875
 	 */
Borislav Petkov 727875
 	TRACE_IRQS_OFF
Borislav Petkov 727875
 
Borislav Petkov 727875
+	IBRS_ENTER
Borislav Petkov 727875
+	UNTRAIN_RET
Borislav Petkov 727875
+
Borislav Petkov 727875
 	CALL_enter_from_user_mode
Borislav Petkov 727875
 
Borislav Petkov 727875
 1:
Borislav Petkov 727875
@@ -590,6 +596,7 @@ GLOBAL(retint_user)
Borislav Petkov 727875
 	TRACE_IRQS_IRETQ
Borislav Petkov 727875
 
Borislav Petkov 727875
 GLOBAL(swapgs_restore_regs_and_return_to_usermode)
Borislav Petkov 727875
+	IBRS_EXIT
Borislav Petkov 727875
 #ifdef CONFIG_DEBUG_ENTRY
Borislav Petkov 727875
 	/* Assert that pt_regs indicates user mode. */
Borislav Petkov 727875
 	testb	$3, CS(%rsp)
Borislav Petkov 727875
@@ -1160,6 +1167,8 @@ idtentry machine_check		do_mce			has_err
Borislav Petkov 727875
  * Save all registers in pt_regs, and switch gs if needed.
Borislav Petkov 727875
  * Use slow, but surefire "are we in kernel?" check.
Borislav Petkov 727875
  * Return: ebx=0: need swapgs on exit, ebx=1: otherwise
Borislav Petkov 727875
+ * R14 - old CR3
Borislav Petkov 727875
+ * R15 - old SPEC_CTRL
Borislav Petkov 727875
  */
Borislav Petkov 727875
 ENTRY(paranoid_entry)
Borislav Petkov 727875
 	UNWIND_HINT_FUNC
Borislav Petkov 727875
@@ -1173,7 +1182,6 @@ ENTRY(paranoid_entry)
Borislav Petkov 727875
 	js	1f				/* negative -> in kernel */
Borislav Petkov 727875
 	SWAPGS
Borislav Petkov 727875
 	xorl	%ebx, %ebx
Borislav Petkov 727875
-
Borislav Petkov 727875
 1:
Borislav Petkov 727875
 	SAVE_AND_SWITCH_TO_KERNEL_CR3 scratch_reg=%rax save_reg=%r14
Borislav Petkov 727875
 	/*
Borislav Petkov 727875
@@ -1183,7 +1191,14 @@ ENTRY(paranoid_entry)
Borislav Petkov 727875
 	 */
Borislav Petkov 727875
 	FENCE_SWAPGS_KERNEL_ENTRY
Borislav Petkov 727875
 
Borislav Petkov 727875
-	ret
Borislav Petkov 727875
+	/*
Borislav Petkov 727875
+	 * Once we have CR3 and %GS setup save and set SPEC_CTRL. Just like
Borislav Petkov 727875
+	 * CR3 above, keep the old value in a callee saved register.
Borislav Petkov 727875
+	 */
Borislav Petkov 727875
+	IBRS_ENTER save_reg=%r15
Borislav Petkov 727875
+	UNTRAIN_RET
Borislav Petkov 727875
+
Borislav Petkov 727875
+	RET
Borislav Petkov 727875
 END(paranoid_entry)
Borislav Petkov 727875
 
Borislav Petkov 727875
 /*
Borislav Petkov 727875
@@ -1197,9 +1212,19 @@ END(paranoid_entry)
Borislav Petkov 727875
  * to try to handle preemption here.
Borislav Petkov 727875
  *
Borislav Petkov 727875
  * On entry, ebx is "no swapgs" flag (1: don't need swapgs, 0: need it)
Borislav Petkov 727875
+ *
Borislav Petkov 727875
+ * R14 - old CR3
Borislav Petkov 727875
+ * R15 - old SPEC_CTRL
Borislav Petkov 727875
  */
Borislav Petkov 727875
 ENTRY(paranoid_exit)
Borislav Petkov 727875
 	UNWIND_HINT_REGS
Borislav Petkov 727875
+
Borislav Petkov 727875
+	/*
Borislav Petkov 727875
+	 * Must restore IBRS state before both CR3 and %GS since we need access
Borislav Petkov 727875
+	 * to the per-CPU x86_spec_ctrl_shadow variable.
Borislav Petkov 727875
+	 */
Borislav Petkov 727875
+	IBRS_EXIT save_reg=%r15
Borislav Petkov 727875
+
Borislav Petkov 727875
 	DISABLE_INTERRUPTS(CLBR_ANY)
Borislav Petkov 727875
 	TRACE_IRQS_OFF_DEBUG
Borislav Petkov 727875
 	testl	%ebx, %ebx			/* swapgs needed? */
Borislav Petkov 727875
@@ -1234,9 +1259,11 @@ ENTRY(error_entry)
Borislav Petkov 727875
 	FENCE_SWAPGS_USER_ENTRY
Borislav Petkov 727875
 	/* We have user CR3.  Change to kernel CR3. */
Borislav Petkov 727875
 	SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
Borislav Petkov 727875
+	IBRS_ENTER
Borislav Petkov 727875
 	UNTRAIN_RET
Borislav Petkov 727875
 
Borislav Petkov 727875
 .Lerror_entry_from_usermode_after_swapgs:
Borislav Petkov 727875
+
Borislav Petkov 727875
 	/* Put us onto the real thread stack. */
Borislav Petkov 727875
 	popq	%r12				/* save return addr in %12 */
Borislav Petkov 727875
 	movq	%rsp, %rdi			/* arg0 = pt_regs pointer */
Borislav Petkov 727875
@@ -1284,6 +1311,7 @@ ENTRY(error_entry)
Borislav Petkov 727875
 	SWAPGS
Borislav Petkov 727875
 	FENCE_SWAPGS_USER_ENTRY
Borislav Petkov 727875
 	SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
Borislav Petkov 727875
+	IBRS_ENTER
Borislav Petkov 727875
 	UNTRAIN_RET
Borislav Petkov 727875
 	jmp .Lerror_entry_done
Borislav Petkov 727875
 
Borislav Petkov 727875
@@ -1300,6 +1328,8 @@ ENTRY(error_entry)
Borislav Petkov 727875
 	SWAPGS
Borislav Petkov 727875
 	FENCE_SWAPGS_USER_ENTRY
Borislav Petkov 727875
 	SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
Borislav Petkov 727875
+	IBRS_ENTER
Borislav Petkov 727875
+	UNTRAIN_RET
Borislav Petkov 727875
 
Borislav Petkov 727875
 	/*
Borislav Petkov 727875
 	 * Pretend that the exception came from user mode: set up pt_regs
Borislav Petkov 727875
@@ -1376,7 +1406,6 @@ ENTRY(nmi)
Borislav Petkov 727875
 
Borislav Petkov 727875
 	testb	$3, CS-RIP+8(%rsp)
Borislav Petkov 727875
 	jz	.Lnmi_from_kernel
Borislav Petkov 727875
-	UNTRAIN_RET
Borislav Petkov 727875
 
Borislav Petkov 727875
 	/*
Borislav Petkov 727875
 	 * NMI from user mode.  We need to run on the thread stack, but we
Borislav Petkov 727875
@@ -1406,6 +1435,9 @@ ENTRY(nmi)
Borislav Petkov 727875
 	PUSH_AND_CLEAR_REGS rdx=(%rdx)
Borislav Petkov 727875
 	ENCODE_FRAME_POINTER
Borislav Petkov 727875
 
Borislav Petkov 727875
+	IBRS_ENTER
Borislav Petkov 727875
+	UNTRAIN_RET
Borislav Petkov 727875
+
Borislav Petkov 727875
 	/*
Borislav Petkov 727875
 	 * At this point we no longer need to worry about stack damage
Borislav Petkov 727875
 	 * due to nesting -- we're on the normal thread stack and we're
Borislav Petkov 727875
@@ -1629,6 +1661,9 @@ end_repeat_nmi:
Borislav Petkov 727875
 	movq	$-1, %rsi
Borislav Petkov 727875
 	call	do_nmi
Borislav Petkov 727875
 
Borislav Petkov 727875
+	/* Always restore stashed SPEC_CTRL value (see paranoid_entry) */
Borislav Petkov 727875
+	IBRS_EXIT save_reg=%r15
Borislav Petkov 727875
+
Borislav Petkov 727875
 	RESTORE_CR3 scratch_reg=%r15 save_reg=%r14
Borislav Petkov 727875
 
Borislav Petkov 727875
 	testl	%ebx, %ebx			/* swapgs needed? */
Borislav Petkov 727875
--- a/arch/x86/include/asm/cpufeatures.h
Borislav Petkov 727875
+++ b/arch/x86/include/asm/cpufeatures.h
Borislav Petkov 727875
@@ -203,7 +203,7 @@
Borislav Petkov 727875
 #define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
Borislav Petkov 727875
 #define X86_FEATURE_SME		( 7*32+10) /* AMD Secure Memory Encryption */
Borislav Petkov 727875
 #define X86_FEATURE_PTI			( 7*32+11) /* Kernel Page Table Isolation enabled */
Borislav Petkov 727875
-/* FREE!				( 7*32+12) */
Borislav Petkov 727875
+#define X86_FEATURE_KERNEL_IBRS		( 7*32+12) /* "" Set/clear IBRS on kernel entry/exit */
Borislav Petkov 727875
 /* FREE!				( 7*32+13) */
Borislav Petkov 727875
 #define X86_FEATURE_INTEL_PPIN	( 7*32+14) /* Intel Processor Inventory Number */
Borislav Petkov 727875
 #define X86_FEATURE_CDP_L2		( 7*32+15) /* Code and Data Prioritization L2 */