* [PATCH] arm64/debug: mask debug exceptions when called from common code
@ 2026-09-07 16:31 Ada Couprie Diaz
0 siblings, 0 replies; only message in thread
From: Ada Couprie Diaz @ 2026-09-07 16:31 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Mark Rutland, Rob Herring, Anshuman Khandual, Catalin Marinas,
Will Deacon
We can currently take debug exceptions while manipulating the hardware
breakpoint registers and perf slots when called from generic kernel code.
This can lead to errors and unsound behaviours in some pathological
(and self-inflicted) edge cases[0].
Save and restore the interrupt mask around those manipulations.
This is to avoid a superfluous save/restore when calling
`toggle_bp_registers()` and `hw_breakpoint_control()` from our arch code,
as debug exceptions will already be masked in all cases.
It should also allow some clean ups by batching ISBs when we update
multiple registers, rather than doing one for each register write.
Clarify some comments and specify a few functions which should be called
with debug exceptions masked (even though that is true for most of them).
[0]: https://lore.kernel.org/linux-arm-kernel/adeE4MD0RgapI8aL@J2N7QTR9R3/
Signed-off-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
----
This is a follow-up of [1], which blanketly masks debug exceptions rather
than fiddling with specific edge cases one by one as discussed with Will.
I'm not sure about specifying that `toggle_bp_registers()` and
`hw_breakpoint_control()` must be called with debug exceptions masked,
as most other functions here do too, but those are the only one that need it
which are called via common code, rather than arch entry or debug code.
Thanks,
Ada
[1]: https://lore.kernel.org/r/20260527161553.97676-1-ada.coupriediaz@arm.com
---
arch/arm64/kernel/hw_breakpoint.c | 32 ++++++++++++++++++++++++++++---
1 file changed, 29 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 73cce8ac8368..32b38967433e 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -20,6 +20,7 @@
#include <linux/uaccess.h>
#include <asm/current.h>
+#include <asm/daifflags.h>
#include <asm/debug-monitors.h>
#include <asm/esr.h>
#include <asm/exception.h>
@@ -221,6 +222,12 @@ static int hw_breakpoint_slot_setup(struct perf_event **slots, int max_slots,
return -ENOSPC;
}
+/*
+ * Configure hardware breakpoint registers from a perf event,
+ * according to operation.
+ *
+ * Must be called with debug exceptions masked.
+ */
static int hw_breakpoint_control(struct perf_event *bp,
enum hw_breakpoint_ops ops)
{
@@ -286,15 +293,24 @@ static int hw_breakpoint_control(struct perf_event *bp,
/*
* Install a perf counter breakpoint.
+ *
+ * Perf can and will call this with debug exceptions unmasked.
+ * Mask them to avoid triggering one while manipulating their registers.
*/
int arch_install_hw_breakpoint(struct perf_event *bp)
{
- return hw_breakpoint_control(bp, HW_BREAKPOINT_INSTALL);
+ unsigned long daif = local_daif_save();
+ int ret = hw_breakpoint_control(bp, HW_BREAKPOINT_INSTALL);
+
+ local_daif_restore(daif);
+ return ret;
}
void arch_uninstall_hw_breakpoint(struct perf_event *bp)
{
+ unsigned long daif = local_daif_save();
hw_breakpoint_control(bp, HW_BREAKPOINT_UNINSTALL);
+ local_daif_restore(daif);
}
static int get_hbp_len(u8 hbp_len)
@@ -586,7 +602,10 @@ int hw_breakpoint_arch_parse(struct perf_event *bp,
/*
* Enable/disable all of the breakpoints active at the specified
* exception level at the register level.
- * This is used when single-stepping after a breakpoint exception.
+ * This is used when single-stepping after a breakpoint exception or switching
+ * threads.
+ *
+ * Must be called with debug exceptions masked.
*/
static void toggle_bp_registers(int reg, enum dbg_active_el el, int enable)
{
@@ -908,7 +927,11 @@ bool try_step_suspended_breakpoints(struct pt_regs *regs)
NOKPROBE_SYMBOL(try_step_suspended_breakpoints);
/*
- * Context-switcher for restoring suspended breakpoints.
+ * Context-switcher for restoring suspended CPU-bound breakpoints.
+ *
+ * Breakpoints of the previous task are uninstalled before
+ * this function is called, in perf_event_task_sched_out(), and those
+ * of the next task are installed after, in perf_event_task_sched_in().
*/
void hw_breakpoint_thread_switch(struct task_struct *next)
{
@@ -922,6 +945,7 @@ void hw_breakpoint_thread_switch(struct task_struct *next)
*/
struct debug_info *current_debug_info, *next_debug_info;
+ unsigned long daif = local_daif_save();
current_debug_info = ¤t->thread.debug;
next_debug_info = &next->thread.debug;
@@ -937,6 +961,8 @@ void hw_breakpoint_thread_switch(struct task_struct *next)
toggle_bp_registers(AARCH64_DBG_REG_WCR,
DBG_ACTIVE_EL0,
!next_debug_info->wps_disabled);
+
+ local_daif_restore(daif);
}
/*
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-07 16:31 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-07 16:31 [PATCH] arm64/debug: mask debug exceptions when called from common code Ada Couprie Diaz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox