summaryrefslogtreecommitdiffstats
path: root/arch/arm64/kernel/traps.c
diff options
context:
space:
mode:
authorMark Rutland <mark.rutland@arm.com>2022-10-19 15:41:23 +0100
committerWill Deacon <will@kernel.org>2022-11-15 13:46:19 +0000
commit124c49b1b5d947b7180c5d6cbb09ddf76ea45ea2 (patch)
tree6951862857951bbbc5888bc61ab7dc5131987246 /arch/arm64/kernel/traps.c
parent0c5f416219da3795dc8b33e5bb7865a6b3c4e55c (diff)
downloadlinux-124c49b1b5d947b7180c5d6cbb09ddf76ea45ea2.tar.gz
linux-124c49b1b5d947b7180c5d6cbb09ddf76ea45ea2.tar.bz2
linux-124c49b1b5d947b7180c5d6cbb09ddf76ea45ea2.zip
arm64: armv8_deprecated: rework deprected instruction handling
Support for deprecated instructions can be enabled or disabled at runtime. To handle this, the code in armv8_deprecated.c registers and unregisters undef_hooks, and makes cross CPU calls to configure HW support. This is rather complicated, and the synchronization required to make this safe ends up serializing the handling of instructions which have been trapped. This patch simplifies the deprecated instruction handling by removing the dynamic registration and unregistration, and changing the trap handling code to determine whether a handler should be invoked. This removes the need for dynamic list management, and simplifies the locking requirements, making it possible to handle trapped instructions entirely in parallel. Where changing the emulation state requires a cross-call, this is serialized by locally disabling interrupts, ensuring that the CPU is not left in an inconsistent state. To simplify sysctl management, each insn_emulation is given a separate sysctl table, permitting these to be registered separately. The core sysctl code will iterate over all of these when walking sysfs. I've tested this with userspace programs which use each of the deprecated instructions, and I've concurrently modified the support level for each of the features back-and-forth between HW and emulated to check that there are no spurious SIGILLs sent to userspace when the support level is changed. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: James Morse <james.morse@arm.com> Cc: Joey Gouly <joey.gouly@arm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Will Deacon <will@kernel.org> Link: https://lore.kernel.org/r/20221019144123.612388-10-mark.rutland@arm.com Signed-off-by: Will Deacon <will@kernel.org>
Diffstat (limited to 'arch/arm64/kernel/traps.c')
-rw-r--r--arch/arm64/kernel/traps.c40
1 files changed, 1 insertions, 39 deletions
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 96eaf1aaec12..4c0caa589e12 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -373,27 +373,6 @@ void arm64_skip_faulting_instruction(struct pt_regs *regs, unsigned long size)
regs->pstate &= ~PSR_BTYPE_MASK;
}
-static LIST_HEAD(undef_hook);
-static DEFINE_RAW_SPINLOCK(undef_lock);
-
-void register_undef_hook(struct undef_hook *hook)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&undef_lock, flags);
- list_add(&hook->node, &undef_hook);
- raw_spin_unlock_irqrestore(&undef_lock, flags);
-}
-
-void unregister_undef_hook(struct undef_hook *hook)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&undef_lock, flags);
- list_del(&hook->node);
- raw_spin_unlock_irqrestore(&undef_lock, flags);
-}
-
static int user_insn_read(struct pt_regs *regs, u32 *insnp)
{
u32 instr;
@@ -425,23 +404,6 @@ static int user_insn_read(struct pt_regs *regs, u32 *insnp)
return 0;
}
-static int call_undef_hook(struct pt_regs *regs, u32 instr)
-{
- struct undef_hook *hook;
- unsigned long flags;
- int (*fn)(struct pt_regs *regs, u32 instr) = NULL;
-
- raw_spin_lock_irqsave(&undef_lock, flags);
- list_for_each_entry(hook, &undef_hook, node)
- if ((instr & hook->instr_mask) == hook->instr_val &&
- (regs->pstate & hook->pstate_mask) == hook->pstate_val)
- fn = hook->fn;
-
- raw_spin_unlock_irqrestore(&undef_lock, flags);
-
- return fn ? fn(regs, instr) : 1;
-}
-
void force_signal_inject(int signal, int code, unsigned long address, unsigned long err)
{
const char *desc;
@@ -502,7 +464,7 @@ void do_el0_undef(struct pt_regs *regs, unsigned long esr)
if (try_emulate_mrs(regs, insn))
return;
- if (call_undef_hook(regs, insn) == 0)
+ if (try_emulate_armv8_deprecated(regs, insn))
return;
out_err: