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

* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
  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
  0 siblings, 1 reply; 5+ messages in thread
From: Peter Maydell @ 2026-07-06  9:21 UTC (permalink / raw)
  To: Gilles Grimaud; +Cc: qemu-devel, qemu-arm, Alex Bennée, Richard Henderson

On Sun, 5 Jul 2026 at 23:44, Gilles Grimaud
<gilles.grimaud@univ-lille.fr> wrote:
>
> 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;
> +}

No other architecture, CPU or board in QEMU needs to do this,
so my instinct is to say that M-profile should not be special here.
Richard, Alex: how do we usually handle breakpoint insns for the
gdbstub ?

-- PMM


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

* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
  2026-07-06  9:21 ` Peter Maydell
@ 2026-07-06 10:33   ` Alex Bennée
  2026-07-06 10:39     ` Peter Maydell
  0 siblings, 1 reply; 5+ messages in thread
From: Alex Bennée @ 2026-07-06 10:33 UTC (permalink / raw)
  To: Peter Maydell; +Cc: Gilles Grimaud, qemu-devel, qemu-arm, Richard Henderson

Peter Maydell <peter.maydell@linaro.org> writes:

> On Sun, 5 Jul 2026 at 23:44, Gilles Grimaud
> <gilles.grimaud@univ-lille.fr> wrote:
>>
>> 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;
>> +}
>
> No other architecture, CPU or board in QEMU needs to do this,
> so my instinct is to say that M-profile should not be special here.
> Richard, Alex: how do we usually handle breakpoint insns for the
> gdbstub ?

For TCG guests we never insert BKPT instructions and treat the system as
having infinite hardware breakpoints.

From what I can tell we should only route the debug event to an external
monitor if the right DHCSR and DEMCR bits are set. Even then the spec
has a lot of behaviour with is IMPDEF.

I think the v7M rules are different to the v7a/v8a ones.

>
> -- PMM

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


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

* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
  2026-07-06 10:33   ` Alex Bennée
@ 2026-07-06 10:39     ` Peter Maydell
  2026-07-06 11:00       ` Alex Bennée
  0 siblings, 1 reply; 5+ messages in thread
From: Peter Maydell @ 2026-07-06 10:39 UTC (permalink / raw)
  To: Alex Bennée; +Cc: Gilles Grimaud, qemu-devel, qemu-arm, Richard Henderson

On Mon, 6 Jul 2026 at 11:33, Alex Bennée <alex.bennee@linaro.org> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
>
> > On Sun, 5 Jul 2026 at 23:44, Gilles Grimaud
> > <gilles.grimaud@univ-lille.fr> wrote:
> >>
> >> 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.

> > No other architecture, CPU or board in QEMU needs to do this,
> > so my instinct is to say that M-profile should not be special here.
> > Richard, Alex: how do we usually handle breakpoint insns for the
> > gdbstub ?
>
> For TCG guests we never insert BKPT instructions and treat the system as
> having infinite hardware breakpoints.

This isn't about gdbstub-inserted breakpoints, it's about
what happens when the guest code has a breakpoint insn in it
already (either hardcoded, or also could happen if we're running
gdb inside the guest). That's something that can happen for any
CPU type.

thanks
-- PMM


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

* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
  2026-07-06 10:39     ` Peter Maydell
@ 2026-07-06 11:00       ` Alex Bennée
  0 siblings, 0 replies; 5+ messages in thread
From: Alex Bennée @ 2026-07-06 11:00 UTC (permalink / raw)
  To: Peter Maydell; +Cc: Gilles Grimaud, qemu-devel, qemu-arm, Richard Henderson

Peter Maydell <peter.maydell@linaro.org> writes:

> On Mon, 6 Jul 2026 at 11:33, Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> Peter Maydell <peter.maydell@linaro.org> writes:
>>
>> > On Sun, 5 Jul 2026 at 23:44, Gilles Grimaud
>> > <gilles.grimaud@univ-lille.fr> wrote:
>> >>
>> >> 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.
>
>> > No other architecture, CPU or board in QEMU needs to do this,
>> > so my instinct is to say that M-profile should not be special here.
>> > Richard, Alex: how do we usually handle breakpoint insns for the
>> > gdbstub ?
>>
>> For TCG guests we never insert BKPT instructions and treat the system as
>> having infinite hardware breakpoints.
>
> This isn't about gdbstub-inserted breakpoints, it's about
> what happens when the guest code has a breakpoint insn in it
> already (either hardcoded, or also could happen if we're running
> gdb inside the guest). That's something that can happen for any
> CPU type.

So far nothing - there is a helper_debug in sparc which raises the
internal EXCP_DEBUG exception but as far as I can tell it is never
called.

We could treat gdbstub as an external debug monitor but we don't
currently model it as such.


>
> thanks
> -- PMM

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


^ permalink raw reply	[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.