Skip to content

Commit bff8f41

Browse files
mrutland-armwilldeacon
authored andcommitted
arm64: factor out EL1 SSBS emulation hook
Currently call_undef_hook() is used to handle UNDEFINED exceptions from EL0 and EL1. As support for deprecated instructions may be enabled independently, the handlers for individual instructions are organised as a linked list of struct undef_hook which can be manipulated dynamically. As this can be manipulated dynamically, the list is protected with a raw_spinlock which must be acquired when handling UNDEFINED exceptions or when manipulating the list of handlers. This locking is unfortunate as it serialises handling of UNDEFINED exceptions, and requires RCU to be enabled for lockdep, requiring the use of RCU_NONIDLE() in resume path of cpu_suspend() since commit: a2c42bb ("arm64: spectre: Prevent lockdep splat on v4 mitigation enable path") The list of UNDEFINED handlers largely consist of handlers for exceptions taken from EL0, and the only handler for exceptions taken from EL1 handles `MSR SSBS, #imm` on CPUs which feature PSTATE.SSBS but lack the corresponding MSR (Immediate) instruction. Other than this we never expect to take an UNDEFINED exception from EL1 in normal operation. This patch reworks do_el0_undef() to invoke the EL1 SSBS handler directly, relegating call_undef_hook() to only handle EL0 UNDEFs. This removes redundant work to iterate the list for EL1 UNDEFs, and removes the need for locking, permitting EL1 UNDEFs to be handled in parallel without contention. The RCU_NONIDLE() call in cpu_suspend() will be removed in a subsequent patch, as there are other potential issues with the use of instrumentable code and RCU in the CPU suspend code. I've tested this by forcing the detection of SSBS on a CPU that doesn't have it, and verifying that the try_emulate_el1_ssbs() callback is invoked. Signed-off-by: Mark Rutland <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: James Morse <[email protected]> Cc: Joey Gouly <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Will Deacon <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent 61d64a3 commit bff8f41

File tree

3 files changed

+17
-26
lines changed

3 files changed

+17
-26
lines changed

arch/arm64/include/asm/spectre.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum mitigation_state {
2626
SPECTRE_VULNERABLE,
2727
};
2828

29+
struct pt_regs;
2930
struct task_struct;
3031

3132
/*
@@ -98,5 +99,6 @@ enum mitigation_state arm64_get_spectre_bhb_state(void);
9899
bool is_spectre_bhb_affected(const struct arm64_cpu_capabilities *entry, int scope);
99100
u8 spectre_bhb_loop_affected(int scope);
100101
void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *__unused);
102+
bool try_emulate_el1_ssbs(struct pt_regs *regs, u32 instr);
101103
#endif /* __ASSEMBLY__ */
102104
#endif /* __ASM_SPECTRE_H */

arch/arm64/kernel/proton-pack.c

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -521,30 +521,25 @@ bool has_spectre_v4(const struct arm64_cpu_capabilities *cap, int scope)
521521
return state != SPECTRE_UNAFFECTED;
522522
}
523523

524-
static int ssbs_emulation_handler(struct pt_regs *regs, u32 instr)
524+
bool try_emulate_el1_ssbs(struct pt_regs *regs, u32 instr)
525525
{
526-
if (user_mode(regs))
527-
return 1;
526+
const u32 instr_mask = ~(1U << PSTATE_Imm_shift);
527+
const u32 instr_val = 0xd500401f | PSTATE_SSBS;
528+
529+
if ((instr & instr_mask) != instr_val)
530+
return false;
528531

529532
if (instr & BIT(PSTATE_Imm_shift))
530533
regs->pstate |= PSR_SSBS_BIT;
531534
else
532535
regs->pstate &= ~PSR_SSBS_BIT;
533536

534537
arm64_skip_faulting_instruction(regs, 4);
535-
return 0;
538+
return true;
536539
}
537540

538-
static struct undef_hook ssbs_emulation_hook = {
539-
.instr_mask = ~(1U << PSTATE_Imm_shift),
540-
.instr_val = 0xd500401f | PSTATE_SSBS,
541-
.fn = ssbs_emulation_handler,
542-
};
543-
544541
static enum mitigation_state spectre_v4_enable_hw_mitigation(void)
545542
{
546-
static bool undef_hook_registered = false;
547-
static DEFINE_RAW_SPINLOCK(hook_lock);
548543
enum mitigation_state state;
549544

550545
/*
@@ -555,13 +550,6 @@ static enum mitigation_state spectre_v4_enable_hw_mitigation(void)
555550
if (state != SPECTRE_MITIGATED || !this_cpu_has_cap(ARM64_SSBS))
556551
return state;
557552

558-
raw_spin_lock(&hook_lock);
559-
if (!undef_hook_registered) {
560-
register_undef_hook(&ssbs_emulation_hook);
561-
undef_hook_registered = true;
562-
}
563-
raw_spin_unlock(&hook_lock);
564-
565553
if (spectre_v4_mitigations_off()) {
566554
sysreg_clear_set(sctlr_el1, 0, SCTLR_ELx_DSSBS);
567555
set_pstate_ssbs(1);

arch/arm64/kernel/traps.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -402,12 +402,7 @@ static int call_undef_hook(struct pt_regs *regs)
402402
int (*fn)(struct pt_regs *regs, u32 instr) = NULL;
403403
unsigned long pc = instruction_pointer(regs);
404404

405-
if (!user_mode(regs)) {
406-
__le32 instr_le;
407-
if (get_kernel_nofault(instr_le, (__le32 *)pc))
408-
goto exit;
409-
instr = le32_to_cpu(instr_le);
410-
} else if (compat_thumb_mode(regs)) {
405+
if (compat_thumb_mode(regs)) {
411406
/* 16-bit Thumb instruction */
412407
__le16 instr_le;
413408
if (get_user(instr_le, (__le16 __user *)pc))
@@ -500,9 +495,15 @@ void do_el0_undef(struct pt_regs *regs, unsigned long esr)
500495

501496
void do_el1_undef(struct pt_regs *regs, unsigned long esr)
502497
{
503-
if (call_undef_hook(regs) == 0)
498+
u32 insn;
499+
500+
if (aarch64_insn_read((void *)regs->pc, &insn))
501+
goto out_err;
502+
503+
if (try_emulate_el1_ssbs(regs, insn))
504504
return;
505505

506+
out_err:
506507
die("Oops - Undefined instruction", regs, esr);
507508
}
508509

0 commit comments

Comments
 (0)