Commit fe379fa4 authored by Peter Zijlstra's avatar Peter Zijlstra
Browse files

x86/ibt: Disable IBT around firmware



Assume firmware isn't IBT clean and disable it across calls.

Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Acked-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20220308154318.759989383@infradead.org
parent 99c95c5d
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#include <asm/tlb.h>
#include <asm/nospec-branch.h>
#include <asm/mmu_context.h>
#include <asm/ibt.h>
#include <linux/build_bug.h>
#include <linux/kernel.h>
#include <linux/pgtable.h>
@@ -120,8 +121,12 @@ extern asmlinkage u64 __efi_call(void *fp, ...);
	efi_enter_mm();							\
})

#define arch_efi_call_virt(p, f, args...)				\
	efi_call((void *)p->f, args)					\
#define arch_efi_call_virt(p, f, args...) ({				\
	u64 ret, ibt = ibt_save();					\
	ret = efi_call((void *)p->f, args);				\
	ibt_restore(ibt);						\
	ret;								\
})

#define arch_efi_call_virt_teardown()					\
({									\
+6 −0
Original line number Diff line number Diff line
@@ -52,6 +52,9 @@ static inline bool is_endbr(u32 val)
	return val == gen_endbr();
}

extern __noendbr u64 ibt_save(void);
extern __noendbr void ibt_restore(u64 save);

#else /* __ASSEMBLY__ */

#ifdef CONFIG_X86_64
@@ -74,6 +77,9 @@ static inline bool is_endbr(u32 val)

static inline bool is_endbr(u32 val) { return false; }

static inline u64 ibt_save(void) { return 0; }
static inline void ibt_restore(u64 save) { }

#else /* __ASSEMBLY__ */

#define ENDBR
+7 −0
Original line number Diff line number Diff line
@@ -232,6 +232,7 @@
#include <asm/paravirt.h>
#include <asm/reboot.h>
#include <asm/nospec-branch.h>
#include <asm/ibt.h>

#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
extern int (*console_blank_hook)(int);
@@ -598,6 +599,7 @@ static long __apm_bios_call(void *_call)
	struct desc_struct	save_desc_40;
	struct desc_struct	*gdt;
	struct apm_bios_call	*call = _call;
	u64			ibt;

	cpu = get_cpu();
	BUG_ON(cpu != 0);
@@ -607,11 +609,13 @@ static long __apm_bios_call(void *_call)

	apm_irq_save(flags);
	firmware_restrict_branch_speculation_start();
	ibt = ibt_save();
	APM_DO_SAVE_SEGS;
	apm_bios_call_asm(call->func, call->ebx, call->ecx,
			  &call->eax, &call->ebx, &call->ecx, &call->edx,
			  &call->esi);
	APM_DO_RESTORE_SEGS;
	ibt_restore(ibt);
	firmware_restrict_branch_speculation_end();
	apm_irq_restore(flags);
	gdt[0x40 / 8] = save_desc_40;
@@ -676,6 +680,7 @@ static long __apm_bios_call_simple(void *_call)
	struct desc_struct	save_desc_40;
	struct desc_struct	*gdt;
	struct apm_bios_call	*call = _call;
	u64			ibt;

	cpu = get_cpu();
	BUG_ON(cpu != 0);
@@ -685,10 +690,12 @@ static long __apm_bios_call_simple(void *_call)

	apm_irq_save(flags);
	firmware_restrict_branch_speculation_start();
	ibt = ibt_save();
	APM_DO_SAVE_SEGS;
	error = apm_bios_call_simple_asm(call->func, call->ebx, call->ecx,
					 &call->eax);
	APM_DO_RESTORE_SEGS;
	ibt_restore(ibt);
	firmware_restrict_branch_speculation_end();
	apm_irq_restore(flags);
	gdt[0x40 / 8] = save_desc_40;
+28 −0
Original line number Diff line number Diff line
@@ -517,6 +517,34 @@ static __init int setup_disable_pku(char *arg)
__setup("nopku", setup_disable_pku);
#endif /* CONFIG_X86_64 */

#ifdef CONFIG_X86_KERNEL_IBT

__noendbr u64 ibt_save(void)
{
	u64 msr = 0;

	if (cpu_feature_enabled(X86_FEATURE_IBT)) {
		rdmsrl(MSR_IA32_S_CET, msr);
		wrmsrl(MSR_IA32_S_CET, msr & ~CET_ENDBR_EN);
	}

	return msr;
}

__noendbr void ibt_restore(u64 save)
{
	u64 msr;

	if (cpu_feature_enabled(X86_FEATURE_IBT)) {
		rdmsrl(MSR_IA32_S_CET, msr);
		msr &= ~CET_ENDBR_EN;
		msr |= (save & CET_ENDBR_EN);
		wrmsrl(MSR_IA32_S_CET, msr);
	}
}

#endif

static __always_inline void setup_cet(struct cpuinfo_x86 *c)
{
	u64 msr = CET_ENDBR_EN;