diff options
| author | Ada Couprie Diaz <ada.coupriediaz@arm.com> | 2025-07-07 12:41:06 +0100 |
|---|---|---|
| committer | Will Deacon <will@kernel.org> | 2025-07-08 13:27:42 +0100 |
| commit | 413f0bba005dacf2484bb8ecce212fab9be79d81 (patch) | |
| tree | bfc25ed89bcc47486cf5b00e491db56c44fc6aee /arch/arm64/kernel/entry-common.c | |
| parent | 0ac7584c08ceff13fc1e3082a0104548688d6b00 (diff) | |
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 <ada.coupriediaz@arm.com>
Tested-by: Luis Claudio R. Goncalves <lgoncalv@redhat.com>
Reviewed-by: Will Deacon <will@kernel.org>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Link: https://lore.kernel.org/r/20250707114109.35672-11-ada.coupriediaz@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
Diffstat (limited to 'arch/arm64/kernel/entry-common.c')
| -rw-r--r-- | arch/arm64/kernel/entry-common.c | 31 |
1 files changed, 31 insertions, 0 deletions
diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c index 7265bef96672..8a6d951c14cc 100644 --- a/arch/arm64/kernel/entry-common.c +++ b/arch/arm64/kernel/entry-common.c @@ -553,6 +553,18 @@ static void noinstr el1_softstp(struct pt_regs *regs, unsigned long esr) arm64_exit_el1_dbg(regs); } +static void noinstr el1_watchpt(struct pt_regs *regs, unsigned long esr) +{ + /* Watchpoints are the only debug exception to write FAR_EL1 */ + unsigned long far = read_sysreg(far_el1); + + arm64_enter_el1_dbg(regs); + debug_exception_enter(regs); + do_watchpoint(far, esr, regs); + debug_exception_exit(regs); + arm64_exit_el1_dbg(regs); +} + static void noinstr el1_dbg(struct pt_regs *regs, unsigned long esr) { unsigned long far = read_sysreg(far_el1); @@ -608,6 +620,8 @@ asmlinkage void noinstr el1h_64_sync_handler(struct pt_regs *regs) el1_softstp(regs, esr); break; case ESR_ELx_EC_WATCHPT_CUR: + el1_watchpt(regs, esr); + break; case ESR_ELx_EC_BRK64: el1_dbg(regs, esr); break; @@ -832,6 +846,19 @@ static void noinstr el0_softstp(struct pt_regs *regs, unsigned long esr) exit_to_user_mode(regs); } +static void noinstr el0_watchpt(struct pt_regs *regs, unsigned long esr) +{ + /* Watchpoints are the only debug exception to write FAR_EL1 */ + unsigned long far = read_sysreg(far_el1); + + enter_from_user_mode(regs); + debug_exception_enter(regs); + do_watchpoint(far, esr, regs); + debug_exception_exit(regs); + local_daif_restore(DAIF_PROCCTX); + exit_to_user_mode(regs); +} + static void noinstr el0_dbg(struct pt_regs *regs, unsigned long esr) { /* Only watchpoints write FAR_EL1, otherwise its UNKNOWN */ @@ -917,6 +944,8 @@ asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs) el0_softstp(regs, esr); break; case ESR_ELx_EC_WATCHPT_LOW: + el0_watchpt(regs, esr); + break; case ESR_ELx_EC_BRK64: el0_dbg(regs, esr); break; @@ -1041,6 +1070,8 @@ asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs) el0_softstp(regs, esr); break; case ESR_ELx_EC_WATCHPT_LOW: + el0_watchpt(regs, esr); + break; case ESR_ELx_EC_BKPT32: el0_dbg(regs, esr); break; |