* [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.