[PATCH v2 00/11] arm64: debug: remove hook registration, split exception entry
Luis Claudio R. Goncalves
lgoncalv at redhat.com
Tue May 13 05:25:12 PDT 2025
On Mon, May 12, 2025 at 06:43:15PM +0100, Ada Couprie Diaz wrote:
> Hi,
>
> This series simplifies the debug exception entry path by removing handler
> registration mechanisms for the debug exception handlers, a holdover from
> the arm kernel, as well as the break and stepping handlers.
> This moves much of the code related to debug exceptions outside of
> `mm/fault.c` where it didn't make much sense.
> This allows us to split the debug exception entries: going from one common
> path per EL for all debug exceptions to a unique one per exception and EL.
>
> The result is a much simpler and fully static exception entry path, which
> we tailor to the different exceptions and their constraints.
>
> The series is structured as such :
> 1 : related clean-up in the signle step handler
> 2-4 : software breakpoints and single step handlers registration removal
> 5: preparatory function duplication that gets cleaned-up in patch 11
> 6-11 : debug exception splitting and handler registration removal
>
> * Patch 2 copies and extends the `early_brk64` break handling for the
> normal path, byassing the dynamic registration.
> * Patch 3 does something similar for the single stepping handlers.
> * Patches 6 through 10 split each individual debug exception from
> the unified path by creating specific handlers, adapting them to
> their constraints and calling into them, bypassing the dynamically
> registered handlers used before.
> * Patches 4 and 11 are clean-ups removing the code that has been replaced
> and made redundant by the preceding patches.
>
> Single Step Exception
> ===
>
> Of note, this allows us to make the single exception handling mostly
> preemptible coming from EL0 in patch 7, fixing an issue with PREEMPT_RT[0].
> The commit message details the reasoning on why this should be safe.
> It is *definitely* not preemptible at EL1 in the current state, given
> that the EL1 software step exception is handled by KGDB.
>
> CC-ing Luis and Sebastian as they were active on the original bug report.
I have been running the ssdd test in a tight loop on a kernel with your
patches and PREEMPT_RT enabled, on two different aarch64 boxes (8 cores and
144 cores). So far, so good. The patch series seems to have solved the
problem I reported.
For the first 10h of tests the kernel also had LOCKDEP and locking debug
features enabled. Now I am running the test with a production-like
configuration. As soon as these tests are done I will run a few more sanity
tests and reply here with my test ACK.
Is there any specific test you would like me to run on that test setup I
have?
As for the code review, I am not that well versed on ARM debug exceptions,
I may take a bit longer to complete the task with the required accuracy.
Best regards,
Luis
> Cc: "Luis Claudio R. Goncalves" <lgoncalv at redhat.com>
> Cc: Sebastian Andrzej Siewior <bigeasy at linutronix.de>
>
> Testing
> ===
>
> Testing EL1 debug exceptions can only be properly done with perf.
> A simple test of hardware breakpoints, watchpoints, and software stepping
> can be achieved by using `perf stat sleep 0.01` and adding hardware events
> for `jiffies` and `hrtimer_nanosleep`, which guarantees a
> hardware watchpoint and breakpoint.
> Inserting a `WARN()` at a convenient place will allow testing both early
> and late software breakpoints at EL1, or using KGDB to test without
> changing code.
>
> For EL0 debug exceptions, the easiest is to setup a basic program and use
> GDB with a list of pre-programmed commands setting hardware and software
> breakpoints as well as watchpoints. Setpping will occur naturally when
> encountering breakpoints.
>
> All tests maintained behaviour after the patches.
>
> I also tested with KASAN on, with no apparent impact.
>
> See below for some testing examples.
>
> Regarding [0], I tested a PREEMPT_RT kernel with the patches, running
> `ssdd` on loop as well as some spammy GDB stepping, and some
> hardware watchpoints on a tight loop without any issues.
>
>
> Based on v6.15-rc6.
> Thanks,
> Ada
>
> v2 :
> - Move the BP hardening call outside of the handlers to `entry-common`,
> as they are not needed coming from EL1.
> - Make the EL0 software stepping exception mostly preemptible
> - Move `reinstall_hw_bps()` call to `entry-common`
> - Don't disable preemption in `el0_softstp()`
> - Unmask DAIF before `do_softstep()` in `el0_softstp()`
> - Update comments to make sense with the changes
> - Simplify the single step handler, as it always returns 0 and could not
> trigger the `arm64_notify_die()` call.
> - Fix some commit messages
> v1 : https://lore.kernel.org/linux-arm-kernel/20250425153647.438508-1-ada.coupriediaz@arm.com
>
> [0]: https://lore.kernel.org/linux-arm-kernel/Z6YW_Kx4S2tmj2BP@uudg.org/
>
>
> Testing examples
> ===
>
> Perf (for EL1):
> ~~~
> Assuming that `perf` is on your $PATH and building with `kallsyms`
>
> #!/bin/bash
>
> watch_addr=$(sudo cat /proc/kallsyms | grep "D jiffies$" | cut -f1 -d\ )
> break_addr=$(sudo cat /proc/kallsyms | grep "clock_nanosleep$" | cut -f1 -d\ )
>
> cmd="sleep 0.01"
>
> sudo perf stat -a -e mem:0x${watch_addr}/8:w -e mem:0x${break_addr}:x ${cmd}
>
> NB: This does /not/ test EL1 BRKs.
>
>
> GDB commands (for EL0):
> ~~~
> The following C example, compiled with `-g -O0`
>
> int main() {
> int add = 0xAA;
> int target = 0;
>
> target += add;
>
> #ifdef COMPAT
> __asm__("BKPT");
> #else
> __asm__("BRK 1");
> #endif
>
> return target;
> }
>
> Combined with the following GDB command-list
>
> start
> hbreak 3
> watch target
> commands 2
> continue
> end
> commands 3
> continue
> end
> continue
> jump 11
> continue
> quit
>
> Executed as such : `gdb -x ${COMMAND_LIST_FILE} ./a.out`
> should go through the whole program, return 0252/170/0xAA, and
> exercise all EL0 debug exception entries.
> By using a cross-compiler and passing and additional `-DCOMPAT` argument
> during compilation, the `BKPT32` path can also be tested.
> NOTE: `BKPT` *will* make GDB loop infinitely, that is expected. Sending
> SIGINT to GDB will break the loop and the execution should complete.
>
> Ada Couprie Diaz (11):
> arm64: debug: clean up single_step_handler logic
> arm64: debug: call software break handlers statically
> arm64: debug: call step handlers statically
> arm64: debug: remove break/step handler registration infrastructure
> arm64: entry: Add entry and exit functions for debug exceptions
> arm64: debug: split hardware breakpoint exeception entry
> arm64: debug: split single stepping exception entry
> arm64: debug: split hardware watchpoint exception entry
> arm64: debug: split brk64 exception entry
> arm64: debug: split bkpt32 exception entry
> arm64: debug: remove debug exception registration infrastructure
>
> arch/arm64/include/asm/debug-monitors.h | 26 ---
> arch/arm64/include/asm/exception.h | 6 +-
> arch/arm64/include/asm/kgdb.h | 4 +
> arch/arm64/include/asm/kprobes.h | 6 +
> arch/arm64/include/asm/system_misc.h | 4 -
> arch/arm64/include/asm/traps.h | 6 +
> arch/arm64/include/asm/uprobes.h | 3 +
> arch/arm64/kernel/debug-monitors.c | 205 +++++++-----------
> arch/arm64/kernel/entry-common.c | 148 ++++++++++++-
> arch/arm64/kernel/hw_breakpoint.c | 35 ++-
> arch/arm64/kernel/kgdb.c | 39 +---
> arch/arm64/kernel/probes/kprobes.c | 31 +--
> arch/arm64/kernel/probes/kprobes_trampoline.S | 2 +-
> arch/arm64/kernel/probes/uprobes.c | 18 +-
> arch/arm64/kernel/traps.c | 77 +------
> arch/arm64/mm/fault.c | 75 -------
> 16 files changed, 294 insertions(+), 391 deletions(-)
>
>
> base-commit: 82f2b0b97b36ee3fcddf0f0780a9a0825d52fec3
> --
> 2.43.0
>
---end quoted text---
More information about the linux-arm-kernel
mailing list