]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
arm64: debug: split bkpt32 exception entry
authorAda Couprie Diaz <ada.coupriediaz@arm.com>
Mon, 7 Jul 2025 11:41:08 +0000 (12:41 +0100)
committerWill Deacon <will@kernel.org>
Tue, 8 Jul 2025 12:27:42 +0000 (13:27 +0100)
Currently all debug exceptions share common entry code and are routed
to `do_debug_exception()`, which calls dynamically-registered
handlers for each specific debug exception. This is unfortunate as
different debug exceptions have different entry handling requirements,
and it would be better to handle these distinct requirements earlier.

The BKPT32 exception can only be triggered by a BKPT instruction. Thus,
we know that the PC is a legitimate address and isn't being used to train
a branch predictor with a bogus address : we don't need to call
`arm64_apply_bp_hardening()`.

The handler for this exception only pends a signal and doesn't depend
on any per-CPU state : we don't need to inhibit preemption, nor do we
need to keep the DAIF exceptions masked, so we can unmask them earlier.

Split the BKPT32 exception entry and adjust function signatures and its
behaviour to match its relaxed constraints compared to other
debug exceptions.
We can also remove `NOKRPOBE_SYMBOL`, as this cannot lead to a kprobe
recursion.

This replaces the last usage of `el0_dbg()`, so remove it.

Signed-off-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
Tested-by: Luis Claudio R. Goncalves <lgoncalv@redhat.com>
Reviewed-by: Will Deacon <will@kernel.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Link: https://lore.kernel.org/r/20250707114109.35672-13-ada.coupriediaz@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
arch/arm64/include/asm/exception.h
arch/arm64/kernel/debug-monitors.c
arch/arm64/kernel/entry-common.c

index ca556583b1287940453dc3e20f6cd001b161bbcd..cdce3c7137668be2fe71e7c42254c6e681c5dd60 100644 (file)
@@ -74,6 +74,7 @@ void do_el0_softstep(unsigned long esr, struct pt_regs *regs);
 void do_el1_softstep(unsigned long esr, struct pt_regs *regs);
 void do_el0_brk64(unsigned long esr, struct pt_regs *regs);
 void do_el1_brk64(unsigned long esr, struct pt_regs *regs);
+void do_bkpt32(unsigned long esr, struct pt_regs *regs);
 void do_fpsimd_acc(unsigned long esr, struct pt_regs *regs);
 void do_sve_acc(unsigned long esr, struct pt_regs *regs);
 void do_sme_acc(unsigned long esr, struct pt_regs *regs);
index 7c1c89bb9ad1519c853128284223cc6b9add8386..43a612eaa7d2fc2702e7724f7f4085e9e8c971e4 100644 (file)
@@ -270,6 +270,13 @@ void do_el1_brk64(unsigned long esr, struct pt_regs *regs)
 }
 NOKPROBE_SYMBOL(do_el1_brk64);
 
+#ifdef CONFIG_COMPAT
+void do_bkpt32(unsigned long esr, struct pt_regs *regs)
+{
+       arm64_notify_die("aarch32 BKPT", regs, SIGTRAP, TRAP_BRKPT, regs->pc, esr);
+}
+#endif /* CONFIG_COMPAT */
+
 bool try_handle_aarch32_break(struct pt_regs *regs)
 {
        u32 arm_instr;
index b2415c7b074316bf4686dbdb61d8bcd72d8019ce..bbf58fcab142759d345d0745cf2d01538879fc82 100644 (file)
@@ -866,18 +866,6 @@ static void noinstr el0_brk64(struct pt_regs *regs, unsigned long esr)
        exit_to_user_mode(regs);
 }
 
-static void noinstr __maybe_unused
-el0_dbg(struct pt_regs *regs, unsigned long esr)
-{
-       /* Only watchpoints write FAR_EL1, otherwise its UNKNOWN */
-       unsigned long far = read_sysreg(far_el1);
-
-       enter_from_user_mode(regs);
-       do_debug_exception(far, esr, regs);
-       local_daif_restore(DAIF_PROCCTX);
-       exit_to_user_mode(regs);
-}
-
 static void noinstr el0_svc(struct pt_regs *regs)
 {
        enter_from_user_mode(regs);
@@ -1038,6 +1026,14 @@ static void noinstr el0_svc_compat(struct pt_regs *regs)
        exit_to_user_mode(regs);
 }
 
+static void noinstr el0_bkpt32(struct pt_regs *regs, unsigned long esr)
+{
+       enter_from_user_mode(regs);
+       local_daif_restore(DAIF_PROCCTX);
+       do_bkpt32(esr, regs);
+       exit_to_user_mode(regs);
+}
+
 asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
 {
        unsigned long esr = read_sysreg(esr_el1);
@@ -1081,7 +1077,7 @@ asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
                el0_watchpt(regs, esr);
                break;
        case ESR_ELx_EC_BKPT32:
-               el0_dbg(regs, esr);
+               el0_bkpt32(regs, esr);
                break;
        default:
                el0_inv(regs, esr);