Skip to content

Commit 43e2ae7

Browse files
Ada Couprie Diazwilldeacon
authored andcommitted
arm64: debug: split hardware breakpoint exception entry
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. Hardware breakpoints exceptions are generated by the hardware after user configuration. As such, they can be exploited when training branch predictors outside of the userspace VA range: they still need to call `arm64_apply_bp_hardening()` if needed to mitigate against this attack. However, they do not need to handle the Cortex-A76 erratum #1463225 as it only applies to single stepping exceptions. It does not set an address in FAR_EL1 either, only the hardware watchpoint does. As the hardware breakpoint handler only returns 0 and never triggers the call to `arm64_notify_die()`, we can call it directly from `entry-common.c`. Split the hardware breakpoint exception entry, adjust the function signature, and handling of the Cortex-A76 erratum to fit the behaviour of the exception. Move the call to `arm64_apply_bp_hardening()` to `entry-common.c` so that we can do it as early as possible, and only for the exceptions coming from EL0, where it is needed. This is safe to do as it is `noinstr`, as are all the functions it may call. `el0_ia()` and `el0_pc()` already call it this way. Signed-off-by: Ada Couprie Diaz <[email protected]> Tested-by: Luis Claudio R. Goncalves <[email protected]> Reviewed-by: Will Deacon <[email protected]> Acked-by: Mark Rutland <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent eaff68b commit 43e2ae7

File tree

3 files changed

+39
-10
lines changed

3 files changed

+39
-10
lines changed

arch/arm64/include/asm/exception.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ void do_el0_gcs(struct pt_regs *regs, unsigned long esr);
6161
void do_el1_gcs(struct pt_regs *regs, unsigned long esr);
6262
void do_debug_exception(unsigned long addr_if_watchpoint, unsigned long esr,
6363
struct pt_regs *regs);
64+
#ifdef CONFIG_HAVE_HW_BREAKPOINT
65+
void do_breakpoint(unsigned long esr, struct pt_regs *regs);
66+
#else
67+
static inline void do_breakpoint(unsigned long esr, struct pt_regs *regs) {}
68+
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
6469
void do_fpsimd_acc(unsigned long esr, struct pt_regs *regs);
6570
void do_sve_acc(unsigned long esr, struct pt_regs *regs);
6671
void do_sme_acc(unsigned long esr, struct pt_regs *regs);

arch/arm64/kernel/entry-common.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,15 @@ static void noinstr el1_mops(struct pt_regs *regs, unsigned long esr)
526526
exit_to_kernel_mode(regs);
527527
}
528528

529+
static void noinstr el1_breakpt(struct pt_regs *regs, unsigned long esr)
530+
{
531+
arm64_enter_el1_dbg(regs);
532+
debug_exception_enter(regs);
533+
do_breakpoint(esr, regs);
534+
debug_exception_exit(regs);
535+
arm64_exit_el1_dbg(regs);
536+
}
537+
529538
static void noinstr el1_dbg(struct pt_regs *regs, unsigned long esr)
530539
{
531540
unsigned long far = read_sysreg(far_el1);
@@ -575,6 +584,8 @@ asmlinkage void noinstr el1h_64_sync_handler(struct pt_regs *regs)
575584
el1_mops(regs, esr);
576585
break;
577586
case ESR_ELx_EC_BREAKPT_CUR:
587+
el1_breakpt(regs, esr);
588+
break;
578589
case ESR_ELx_EC_SOFTSTP_CUR:
579590
case ESR_ELx_EC_WATCHPT_CUR:
580591
case ESR_ELx_EC_BRK64:
@@ -769,6 +780,19 @@ static void noinstr el0_inv(struct pt_regs *regs, unsigned long esr)
769780
exit_to_user_mode(regs);
770781
}
771782

783+
static void noinstr el0_breakpt(struct pt_regs *regs, unsigned long esr)
784+
{
785+
if (!is_ttbr0_addr(regs->pc))
786+
arm64_apply_bp_hardening();
787+
788+
enter_from_user_mode(regs);
789+
debug_exception_enter(regs);
790+
do_breakpoint(esr, regs);
791+
debug_exception_exit(regs);
792+
local_daif_restore(DAIF_PROCCTX);
793+
exit_to_user_mode(regs);
794+
}
795+
772796
static void noinstr el0_dbg(struct pt_regs *regs, unsigned long esr)
773797
{
774798
/* Only watchpoints write FAR_EL1, otherwise its UNKNOWN */
@@ -848,6 +872,8 @@ asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs)
848872
el0_gcs(regs, esr);
849873
break;
850874
case ESR_ELx_EC_BREAKPT_LOW:
875+
el0_breakpt(regs, esr);
876+
break;
851877
case ESR_ELx_EC_SOFTSTP_LOW:
852878
case ESR_ELx_EC_WATCHPT_LOW:
853879
case ESR_ELx_EC_BRK64:
@@ -968,6 +994,8 @@ asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
968994
el0_cp15(regs, esr);
969995
break;
970996
case ESR_ELx_EC_BREAKPT_LOW:
997+
el0_breakpt(regs, esr);
998+
break;
971999
case ESR_ELx_EC_SOFTSTP_LOW:
9721000
case ESR_ELx_EC_WATCHPT_LOW:
9731001
case ESR_ELx_EC_BKPT32:

arch/arm64/kernel/hw_breakpoint.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <asm/current.h>
2323
#include <asm/debug-monitors.h>
2424
#include <asm/esr.h>
25+
#include <asm/exception.h>
2526
#include <asm/hw_breakpoint.h>
2627
#include <asm/traps.h>
2728
#include <asm/cputype.h>
@@ -618,8 +619,7 @@ NOKPROBE_SYMBOL(toggle_bp_registers);
618619
/*
619620
* Debug exception handlers.
620621
*/
621-
static int breakpoint_handler(unsigned long unused, unsigned long esr,
622-
struct pt_regs *regs)
622+
void do_breakpoint(unsigned long esr, struct pt_regs *regs)
623623
{
624624
int i, step = 0, *kernel_step;
625625
u32 ctrl_reg;
@@ -662,15 +662,15 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr,
662662
}
663663

664664
if (!step)
665-
return 0;
665+
return;
666666

667667
if (user_mode(regs)) {
668668
debug_info->bps_disabled = 1;
669669
toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL0, 0);
670670

671671
/* If we're already stepping a watchpoint, just return. */
672672
if (debug_info->wps_disabled)
673-
return 0;
673+
return;
674674

675675
if (test_thread_flag(TIF_SINGLESTEP))
676676
debug_info->suspended_step = 1;
@@ -681,7 +681,7 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr,
681681
kernel_step = this_cpu_ptr(&stepping_kernel_bp);
682682

683683
if (*kernel_step != ARM_KERNEL_STEP_NONE)
684-
return 0;
684+
return;
685685

686686
if (kernel_active_single_step()) {
687687
*kernel_step = ARM_KERNEL_STEP_SUSPEND;
@@ -690,10 +690,8 @@ static int breakpoint_handler(unsigned long unused, unsigned long esr,
690690
kernel_enable_single_step(regs);
691691
}
692692
}
693-
694-
return 0;
695693
}
696-
NOKPROBE_SYMBOL(breakpoint_handler);
694+
NOKPROBE_SYMBOL(do_breakpoint);
697695

698696
/*
699697
* Arm64 hardware does not always report a watchpoint hit address that matches
@@ -988,8 +986,6 @@ static int __init arch_hw_breakpoint_init(void)
988986
core_num_brps, core_num_wrps);
989987

990988
/* Register debug fault handlers. */
991-
hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP,
992-
TRAP_HWBKPT, "hw-breakpoint handler");
993989
hook_debug_fault_code(DBG_ESR_EVT_HWWP, watchpoint_handler, SIGTRAP,
994990
TRAP_HWBKPT, "hw-watchpoint handler");
995991

0 commit comments

Comments
 (0)