[PATCH] arm64/debug: mask debug exceptions when called from common code
Mark Rutland
mark.rutland at arm.com
Thu Oct 15 07:31:48 PDT 2026
On Mon, Sep 07, 2026 at 05:31:01PM +0100, Ada Couprie Diaz wrote:
> 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 at arm.com>
FWIW, this looks good to me, so:
Acked-by: Mark Rutland <mark.rutland at arm.com>
Mark.
> ----
> 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
>
More information about the linux-arm-kernel
mailing list