All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
@ 2026-07-05 22:44 Gilles Grimaud
  2026-07-06  9:21 ` Peter Maydell
  0 siblings, 1 reply; 5+ messages in thread
From: Gilles Grimaud @ 2026-07-05 22:44 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-arm, Gilles Grimaud

While working on Raspberry Pi Pico/RP2040 support, I noticed that Pico SDK
programs deliberately execute BKPT from _exit(). On real hardware this is
useful when a debug probe is attached: returning from main() stops the
debugger at the program exit point.

Under QEMU this currently does not behave like the debug-probe case. For
M-profile guests, the BKPT instruction is routed through the architectural
guest debug exception path. Without halting debug, that path should remain a
guest-visible DebugMonitor exception when DebugMonitor is enabled, or escalate
towards HardFault otherwise. This patch deliberately leaves that no-debugger
architectural path unchanged.

The problem addressed here is the case where GDB is connected to QEMU's
gdbstub. In that situation, firmware that uses BKPT as a debugger stop point
should stop in the attached debugger. Instead, the M-profile guest currently
continues down the guest exception path and may end in HardFault/lockup rather
than reporting a clean trap to GDB.

This is not specific to the RP2040 machine model. It is a generic
Cortex-M/gdbstub interaction: firmware that uses BKPT as a debugger stop point
should be reported to the attached debugger, while keeping the guest
architectural exception path when no debugger is attached.

Expose a small gdbstub helper to test whether a CPU is visible to an attached
debugger. When an M-profile BKPT instruction is executed with such a debugger
attached, leave the translated block with EXCP_DEBUG so the existing gdbstub
stop path reports a trap to GDB.

Signed-off-by: Gilles Grimaud <gilles.grimaud@univ-lille.fr>
---
 gdbstub/gdbstub.c      | 13 ++++++++++++-
 include/exec/gdbstub.h |  6 ++++++
 target/arm/tcg/debug.c |  9 +++++++++
 3 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index c3c944e965..9f259fc005 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -237,6 +237,18 @@ static GDBProcess *gdb_get_cpu_process(CPUState *cpu)
     return gdb_get_process(gdb_get_cpu_pid(cpu));
 }
 
+bool gdb_cpu_is_attached(CPUState *cpu)
+{
+    GDBProcess *process;
+
+    if (!gdbserver_state.init || !cpu) {
+        return false;
+    }
+
+    process = gdb_get_cpu_process(cpu);
+    return process && process->attached;
+}
+
 static CPUState *find_cpu(uint32_t thread_id)
 {
     CPUState *cpu;
@@ -2520,4 +2532,3 @@ void gdb_create_default_process(GDBState *s)
     process->attached = false;
     process->target_xml = NULL;
 }
-
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index 75eb4d9c36..6e5f75a1f3 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -59,6 +59,12 @@ void gdb_unregister_coprocessor_all(CPUState *cpu);
  */
 bool gdbserver_start(const char *port_or_device, Error **errp);
 
+/**
+ * gdb_cpu_is_attached() - return whether @cpu is visible to an attached GDB
+ * @cpu: The CPU to test.
+ */
+bool gdb_cpu_is_attached(CPUState *cpu);
+
 /**
  * gdb_feature_builder_init() - Initialize GDBFeatureBuilder.
  * @builder: The builder to be initialized.
diff --git a/target/arm/tcg/debug.c b/target/arm/tcg/debug.c
index 07a52643e7..2c60f1c49f 100644
--- a/target/arm/tcg/debug.c
+++ b/target/arm/tcg/debug.c
@@ -12,7 +12,9 @@
 #include "internals.h"
 #include "cpu-features.h"
 #include "cpregs.h"
+#include "accel/tcg/cpu-loop.h"
 #include "exec/watchpoint.h"
+#include "exec/gdbstub.h"
 #include "system/tcg.h"
 
 /* Return the Exception Level targeted by debug exceptions. */
@@ -513,6 +515,7 @@ void arm_debug_excp_handler(CPUState *cs)
  */
 void HELPER(exception_bkpt_insn)(CPUARMState *env, uint32_t syndrome)
 {
+    CPUState *cs = env_cpu(env);
     int debug_el = arm_debug_target_el(env);
     int cur_el = arm_current_el(env);
 
@@ -535,6 +538,12 @@ void HELPER(exception_bkpt_insn)(CPUARMState *env, uint32_t syndrome)
     if (debug_el < cur_el) {
         debug_el = cur_el;
     }
+
+    if (arm_feature(env, ARM_FEATURE_M) && gdb_cpu_is_attached(cs)) {
+        cs->exception_index = EXCP_DEBUG;
+        cpu_loop_exit(cs);
+    }
+
     raise_exception(env, EXCP_BKPT, syndrome, debug_el);
 }
 
-- 
2.55.0



^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-07-06 11:01 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-05 22:44 [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached Gilles Grimaud
2026-07-06  9:21 ` Peter Maydell
2026-07-06 10:33   ` Alex Bennée
2026-07-06 10:39     ` Peter Maydell
2026-07-06 11:00       ` Alex Bennée

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.