Skip to content

Commit 413f0bb

Browse files
Ada Couprie Diazwilldeacon
authored andcommitted
arm64: debug: split hardware watchpoint 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 watchpoints are the only debug exceptions that will write FAR_EL1, so we need to preserve it and pass it down. However, they cannot be used to maliciously train branch predictors, so we can omit calling `arm64_bp_hardening()`, nor do they need to handle the Cortex-A76 erratum #1463225, as it only applies to single stepping exceptions. As the hardware watchpoint 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 watchpoint exception entry and adjust the behaviour to match the lack of needed mitigations. 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 0ac7584 commit 413f0bb

File tree

3 files changed

+40
-12
lines changed

3 files changed

+40
-12
lines changed

arch/arm64/include/asm/exception.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,12 @@ void do_debug_exception(unsigned long addr_if_watchpoint, unsigned long esr,
6363
struct pt_regs *regs);
6464
#ifdef CONFIG_HAVE_HW_BREAKPOINT
6565
void do_breakpoint(unsigned long esr, struct pt_regs *regs);
66+
void do_watchpoint(unsigned long addr, unsigned long esr,
67+
struct pt_regs *regs);
6668
#else
6769
static inline void do_breakpoint(unsigned long esr, struct pt_regs *regs) {}
70+
static inline void do_watchpoint(unsigned long addr, unsigned long esr,
71+
struct pt_regs *regs) {}
6872
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
6973
void do_el0_softstep(unsigned long esr, struct pt_regs *regs);
7074
void do_el1_softstep(unsigned long esr, struct pt_regs *regs);

arch/arm64/kernel/entry-common.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,18 @@ static void noinstr el1_softstp(struct pt_regs *regs, unsigned long esr)
553553
arm64_exit_el1_dbg(regs);
554554
}
555555

556+
static void noinstr el1_watchpt(struct pt_regs *regs, unsigned long esr)
557+
{
558+
/* Watchpoints are the only debug exception to write FAR_EL1 */
559+
unsigned long far = read_sysreg(far_el1);
560+
561+
arm64_enter_el1_dbg(regs);
562+
debug_exception_enter(regs);
563+
do_watchpoint(far, esr, regs);
564+
debug_exception_exit(regs);
565+
arm64_exit_el1_dbg(regs);
566+
}
567+
556568
static void noinstr el1_dbg(struct pt_regs *regs, unsigned long esr)
557569
{
558570
unsigned long far = read_sysreg(far_el1);
@@ -608,6 +620,8 @@ asmlinkage void noinstr el1h_64_sync_handler(struct pt_regs *regs)
608620
el1_softstp(regs, esr);
609621
break;
610622
case ESR_ELx_EC_WATCHPT_CUR:
623+
el1_watchpt(regs, esr);
624+
break;
611625
case ESR_ELx_EC_BRK64:
612626
el1_dbg(regs, esr);
613627
break;
@@ -832,6 +846,19 @@ static void noinstr el0_softstp(struct pt_regs *regs, unsigned long esr)
832846
exit_to_user_mode(regs);
833847
}
834848

849+
static void noinstr el0_watchpt(struct pt_regs *regs, unsigned long esr)
850+
{
851+
/* Watchpoints are the only debug exception to write FAR_EL1 */
852+
unsigned long far = read_sysreg(far_el1);
853+
854+
enter_from_user_mode(regs);
855+
debug_exception_enter(regs);
856+
do_watchpoint(far, esr, regs);
857+
debug_exception_exit(regs);
858+
local_daif_restore(DAIF_PROCCTX);
859+
exit_to_user_mode(regs);
860+
}
861+
835862
static void noinstr el0_dbg(struct pt_regs *regs, unsigned long esr)
836863
{
837864
/* Only watchpoints write FAR_EL1, otherwise its UNKNOWN */
@@ -917,6 +944,8 @@ asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs)
917944
el0_softstp(regs, esr);
918945
break;
919946
case ESR_ELx_EC_WATCHPT_LOW:
947+
el0_watchpt(regs, esr);
948+
break;
920949
case ESR_ELx_EC_BRK64:
921950
el0_dbg(regs, esr);
922951
break;
@@ -1041,6 +1070,8 @@ asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
10411070
el0_softstp(regs, esr);
10421071
break;
10431072
case ESR_ELx_EC_WATCHPT_LOW:
1073+
el0_watchpt(regs, esr);
1074+
break;
10441075
case ESR_ELx_EC_BKPT32:
10451076
el0_dbg(regs, esr);
10461077
break;

arch/arm64/kernel/hw_breakpoint.c

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -750,8 +750,7 @@ static int watchpoint_report(struct perf_event *wp, unsigned long addr,
750750
return step;
751751
}
752752

753-
static int watchpoint_handler(unsigned long addr, unsigned long esr,
754-
struct pt_regs *regs)
753+
void do_watchpoint(unsigned long addr, unsigned long esr, struct pt_regs *regs)
755754
{
756755
int i, step = 0, *kernel_step, access, closest_match = 0;
757756
u64 min_dist = -1, dist;
@@ -806,7 +805,7 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr,
806805
rcu_read_unlock();
807806

808807
if (!step)
809-
return 0;
808+
return;
810809

811810
/*
812811
* We always disable EL0 watchpoints because the kernel can
@@ -819,7 +818,7 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr,
819818

820819
/* If we're already stepping a breakpoint, just return. */
821820
if (debug_info->bps_disabled)
822-
return 0;
821+
return;
823822

824823
if (test_thread_flag(TIF_SINGLESTEP))
825824
debug_info->suspended_step = 1;
@@ -830,7 +829,7 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr,
830829
kernel_step = this_cpu_ptr(&stepping_kernel_bp);
831830

832831
if (*kernel_step != ARM_KERNEL_STEP_NONE)
833-
return 0;
832+
return;
834833

835834
if (kernel_active_single_step()) {
836835
*kernel_step = ARM_KERNEL_STEP_SUSPEND;
@@ -839,10 +838,8 @@ static int watchpoint_handler(unsigned long addr, unsigned long esr,
839838
kernel_enable_single_step(regs);
840839
}
841840
}
842-
843-
return 0;
844841
}
845-
NOKPROBE_SYMBOL(watchpoint_handler);
842+
NOKPROBE_SYMBOL(do_watchpoint);
846843

847844
/*
848845
* Handle single-step exception.
@@ -984,10 +981,6 @@ static int __init arch_hw_breakpoint_init(void)
984981
pr_info("found %d breakpoint and %d watchpoint registers.\n",
985982
core_num_brps, core_num_wrps);
986983

987-
/* Register debug fault handlers. */
988-
hook_debug_fault_code(DBG_ESR_EVT_HWWP, watchpoint_handler, SIGTRAP,
989-
TRAP_HWBKPT, "hw-watchpoint handler");
990-
991984
/*
992985
* Reset the breakpoint resources. We assume that a halting
993986
* debugger will leave the world in a nice state for us.

0 commit comments

Comments
 (0)