[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 = &current->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