* [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; 13+ 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] 13+ 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
2026-07-07 17:27 ` Richard Henderson
0 siblings, 2 replies; 13+ 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] 13+ 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
2026-07-07 17:27 ` Richard Henderson
1 sibling, 1 reply; 13+ 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] 13+ 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; 13+ 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] 13+ 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; 13+ 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] 13+ 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-07 17:27 ` Richard Henderson
2026-07-07 17:43 ` gilles grimaud
` (2 more replies)
1 sibling, 3 replies; 13+ messages in thread
From: Richard Henderson @ 2026-07-07 17:27 UTC (permalink / raw)
To: Peter Maydell, Gilles Grimaud; +Cc: qemu-devel, qemu-arm, Alex Bennée
On 7/6/26 02:21, Peter Maydell wrote:
> 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 ?
We handle them via CPUBreakpoint structures; we never inject code changes.
The patch description explains the confusion: this is attempting to replicate a feature of
the Raspberry Pi Pico SDK, wherein a guest BPKT really is trapped by an external debugger.
The only question is whether we want to follow that, and I suspect the answer is no.
r~
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
2026-07-07 17:27 ` Richard Henderson
@ 2026-07-07 17:43 ` gilles grimaud
2026-07-08 9:00 ` Alex Bennée
2026-07-08 9:17 ` Mohamed Mediouni
2026-07-08 9:31 ` Peter Maydell
2 siblings, 1 reply; 13+ messages in thread
From: gilles grimaud @ 2026-07-07 17:43 UTC (permalink / raw)
To: Richard Henderson, Peter Maydell, qemu-devel, Alex Bennée; +Cc: qemu-arm
[-- Attachment #1: Type: text/plain, Size: 5137 bytes --]
Thanks for the comments.
Since this is an RFC, this is exactly the kind of guidance I’m looking for.
I am new to QEMU, so I am mainly trying to understand what the right model
should be if this patch is not the right approach.
To clarify the motivation: I hit this while working on RP2040/Pico support,
but I do not think the issue is RP2040-specific. The Pico SDK uses a
breakpoint instruction as part of its normal exit/debug path. On Arm
M-profile this is BKPT; on RP2350/RISC-V the analogous pattern use
EBREAK.
So the practical question for me is how QEMU should model firmware that
deliberately executes a breakpoint instruction as a debugger stop point through
The J-TAG.
For Arm M-profile, my understanding is that BKPT can be consumed by the
debug system when an external debugger is present, otherwise it follows the
architectural guest exception path. I was led to this interpretation partly
by QEMU's own gdbstub documentation, which describes the gdbstub as a
low-level debugging facility in the same general space as JTAG-style
debugging.
That led me to the proposed rule:
M-profile guest BKPT + attached QEMU gdbstub client:
report a stop to the gdbstub
otherwise:
keep the existing architectural guest-visible path
I understand Peter's point that guest breakpoint instructions may also be
part of a guest-visible debugging mechanism, for example with a debugger
running inside the guest. This RFC was not trying to solve that broader
problem.
If there is a better way to emulate this bare-metal firmware pattern,
ideally without touching QEMU's normal execution path, I would be very
happy to drop this RFC and try that instead.
Thanks again for your time,
Gilles
> Le 7 juil. 2026 à 19:27, Richard Henderson <richard.henderson@linaro.org> a écrit :
>
> On 7/6/26 02:21, Peter Maydell wrote:
>> 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 ?
>
> We handle them via CPUBreakpoint structures; we never inject code changes.
>
> The patch description explains the confusion: this is attempting to replicate a feature of the Raspberry Pi Pico SDK, wherein a guest BPKT really is trapped by an external debugger.
>
> The only question is whether we want to follow that, and I suspect the answer is no.
>
>
> r~
[-- Attachment #2: Type: text/html, Size: 12249 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
2026-07-07 17:43 ` gilles grimaud
@ 2026-07-08 9:00 ` Alex Bennée
0 siblings, 0 replies; 13+ messages in thread
From: Alex Bennée @ 2026-07-08 9:00 UTC (permalink / raw)
To: gilles grimaud; +Cc: Richard Henderson, Peter Maydell, qemu-devel, qemu-arm
gilles grimaud <gilles.grimaud@univ-lille.fr> writes:
> Thanks for the comments.
>
> Since this is an RFC, this is exactly the kind of guidance I’m looking for.
> I am new to QEMU, so I am mainly trying to understand what the right model
> should be if this patch is not the right approach.
>
> To clarify the motivation: I hit this while working on RP2040/Pico support,
> but I do not think the issue is RP2040-specific. The Pico SDK uses a
> breakpoint instruction as part of its normal exit/debug path. On Arm
> M-profile this is BKPT; on RP2350/RISC-V the analogous pattern use
> EBREAK.
>
> So the practical question for me is how QEMU should model firmware that
> deliberately executes a breakpoint instruction as a debugger stop point through
> The J-TAG.
>
> For Arm M-profile, my understanding is that BKPT can be consumed by the
> debug system when an external debugger is present, otherwise it follows the
> architectural guest exception path. I was led to this interpretation partly
> by QEMU's own gdbstub documentation, which describes the gdbstub as a
> low-level debugging facility in the same general space as JTAG-style
> debugging.
>
> That led me to the proposed rule:
>
> M-profile guest BKPT + attached QEMU gdbstub client:
> report a stop to the gdbstub
>
> otherwise:
> keep the existing architectural guest-visible path
>
> I understand Peter's point that guest breakpoint instructions may also be
> part of a guest-visible debugging mechanism, for example with a debugger
> running inside the guest. This RFC was not trying to solve that broader
> problem.
>
> If there is a better way to emulate this bare-metal firmware pattern,
> ideally without touching QEMU's normal execution path, I would be very
> happy to drop this RFC and try that instead.
Not for breakpoints but this is the sort of stuff that semihosting is
meant to deal with. On real hardware your code can make a semihosting
request and the jtag attached debugger will then service it for you. We
do support this in the gdbstub.
https://qemu.readthedocs.io/en/master/about/emulation.html#semihosting
>
> Thanks again for your time,
> Gilles
>
> Le 7 juil. 2026 à 19:27, Richard Henderson <richard.henderson@linaro.org> a écrit :
>
> On 7/6/26 02:21, Peter Maydell wrote:
>
> 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 ?
>
> We handle them via CPUBreakpoint structures; we never inject code changes.
>
> The patch description explains the confusion: this is attempting to replicate a feature of the Raspberry Pi Pico SDK, wherein a
> guest BPKT really is trapped by an external debugger.
>
> The only question is whether we want to follow that, and I suspect the answer is no.
>
> r~
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
2026-07-07 17:27 ` Richard Henderson
2026-07-07 17:43 ` gilles grimaud
@ 2026-07-08 9:17 ` Mohamed Mediouni
2026-07-08 9:31 ` Peter Maydell
2 siblings, 0 replies; 13+ messages in thread
From: Mohamed Mediouni @ 2026-07-08 9:17 UTC (permalink / raw)
To: Richard Henderson
Cc: Peter Maydell, Gilles Grimaud, qemu-devel, qemu-arm,
Alex Bennée
> On 7. Jul 2026, at 19:27, Richard Henderson <richard.henderson@linaro.org> wrote:
>
> On 7/6/26 02:21, Peter Maydell wrote:
>> 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 ?
>
> We handle them via CPUBreakpoint structures; we never inject code changes.
>
> The patch description explains the confusion: this is attempting to replicate a feature of the Raspberry Pi Pico SDK, wherein a guest BPKT really is trapped by an external debugger.
>
> The only question is whether we want to follow that, and I suspect the answer is no.
>
>
> r~
Hi,
It’s a typical debugging pattern on Cortex-M platforms.
Especially as quite some designs around of Arm M profile are with no HW breakpoints at all.
The Armv8-M TRM says:
(B13.4.4)
> When DHCSR.C_DEBUGEN == 0 or when the PE is in a state in which halting is prohibited,
> the BKPT instruction does not generate an entry to Debug state. If no DebugMonitor
> exception is generated, the BKPT instruction generates a HardFault exception or enters
> Lockup state.
With UDE added as an additional control in Armv8.1-M
(C2.4.25)
> BKPT
> Breakpoint. Breakpoint causes a DebugMonitor exception or a debug halt to occur
> depending on the configuration of the debug support.
with DebugMonitor being fairly new and not really used all that much because
self-hosting debug on Cortex-M is/was quite rare
Quite a number of Cortex-M platforms don’t have hardware breakpoints at all,
relying fully on BKPT. While there are other options when QEMU is around, I think
it’s still a good idea to be able to replicate the hardware behaviour.
Which in this case would mean having QEMU’s gdbstub match the behaviour
that can be obtained from OpenOCD providing a GDB stub.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
2026-07-07 17:27 ` Richard Henderson
2026-07-07 17:43 ` gilles grimaud
2026-07-08 9:17 ` Mohamed Mediouni
@ 2026-07-08 9:31 ` Peter Maydell
2026-07-08 10:10 ` Mohamed Mediouni
2 siblings, 1 reply; 13+ messages in thread
From: Peter Maydell @ 2026-07-08 9:31 UTC (permalink / raw)
To: Richard Henderson; +Cc: Gilles Grimaud, qemu-devel, qemu-arm, Alex Bennée
On Tue, 7 Jul 2026 at 18:27, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 7/6/26 02:21, Peter Maydell wrote:
> > 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 ?
>
> We handle them via CPUBreakpoint structures; we never inject code changes.
I don't mean "breakpoints that the gdbstub requests", I mean
"breakpoint insns that are already in the guest code that get
executed".
> The patch description explains the confusion: this is attempting to replicate a feature of
> the Raspberry Pi Pico SDK, wherein a guest BPKT really is trapped by an external debugger.
But aren't breakpoint instructions of all kinds typically trapped
to the external jtag debugger, on any architecture ? I'm a bit
surprised that we don't do this already.
thanks
-- PMM
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
2026-07-08 9:31 ` Peter Maydell
@ 2026-07-08 10:10 ` Mohamed Mediouni
2026-07-10 22:19 ` gilles grimaud
0 siblings, 1 reply; 13+ messages in thread
From: Mohamed Mediouni @ 2026-07-08 10:10 UTC (permalink / raw)
To: Peter Maydell
Cc: Richard Henderson, Gilles Grimaud, qemu-devel, qemu-arm,
Alex Bennée
> On 8. Jul 2026, at 11:31, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Tue, 7 Jul 2026 at 18:27, Richard Henderson
> <richard.henderson@linaro.org> wrote:
>>
>> On 7/6/26 02:21, Peter Maydell wrote:
>>> 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 ?
>>
>> We handle them via CPUBreakpoint structures; we never inject code changes.
>
> I don't mean "breakpoints that the gdbstub requests", I mean
> "breakpoint insns that are already in the guest code that get
> executed".
>
>> The patch description explains the confusion: this is attempting to replicate a feature of
>> the Raspberry Pi Pico SDK, wherein a guest BPKT really is trapped by an external debugger.
>
> But aren't breakpoint instructions of all kinds typically trapped
> to the external jtag debugger, on any architecture ? I'm a bit
> surprised that we don't do this already.
Some context on the gdbstub implementation from the x86 side:
WHPX's gdbstub support is implemented with overriding the instruction
instead of leveraging HW breakpoints - although a bit hackily.
A big concern is still allowing self-hosted debug to work fine which
makes things a bit harder.
x86 makes things easier by having multiple different breakpoint instructions.
Linux uses int3 and we use int1* for QEMU gdbstub-controlled breakpoints so
that things (mostly) work out for everyone.
And then, QEMU traps all debug exceptions from the guest when gdb is attached
and is supposed to take action when it’s an int1 breakpoint and not others.
Although we currently have a bug there that the non int1s just get ignored
instead of being injected back.
* on x86, the Intel manual says "Hardware vendors may use the INT1 instruction for hardware debug"
On Cortex-M self-hosted debug is far less used than for emulating bigger chips.
And then there’s emulating such a thing using hardware virt and using HW breakpoints
which quickly becomes quite complicated...
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
2026-07-08 10:10 ` Mohamed Mediouni
@ 2026-07-10 22:19 ` gilles grimaud
2026-07-13 11:11 ` Alex Bennée
0 siblings, 1 reply; 13+ messages in thread
From: gilles grimaud @ 2026-07-10 22:19 UTC (permalink / raw)
To: Mohamed Mediouni
Cc: Peter Maydell, Richard Henderson, qemu-devel, qemu-arm,
Alex Bennée
[-- Attachment #1: Type: text/plain, Size: 11860 bytes --]
Thanks everyone for the detailed comments.
Let me try to summarize where I think the discussion stands, and then go through the questions that have emerged, from the most specific ones to the more general and still unresolved ones.
My RFC originally came from reading QEMU's gdbstub as, at least conceptually, an analogue of an external low-level debugger such as a JTAG-attached debugger. From that perspective, having an attached GDB client consume a guest BKPT used as an external debugger stop point seemed natural.
The discussion has made clear that this assumption is precisely where the issue lies.
Alex pointed out that:
> "We could treat gdbstub as an external debug monitor but we don't currently model it as such."
Richard was more skeptical about whether QEMU should reproduce this behaviour at all, describing the patch as attempting to replicate a case where a guest BKPT is trapped by an external debugger, and adding:
> "The only question is whether we want to follow that, and I suspect the answer is no."
On the other hand, Mohamed pointed out that this is a typical Cortex-M debugging pattern, supported by the architectural behaviour of BKPT, and argued that QEMU's gdbstub matching the behaviour of an OpenOCD-provided GDB stub would be desirable.
Peter also broadened the issue beyond the Pico case, asking whether breakpoint instructions are not generally expected to be trapped by an external JTAG debugger, and expressing surprise that QEMU does not already handle this.
So, at this point, I think the RFC has raised the following questions.
1. Is semihosting an alternative here?
Alex mentioned semihosting as the mechanism normally used when code running on real hardware requests a service from a JTAG-attached debugger.
I agree that semihosting addresses a related class of interactions, but I do not think it is an alternative for this particular problem, as Alex also qualified with "not for breakpoints".
The goal here is precisely to run existing firmware unchanged. The Pico SDK already deliberately executes BKPT in its exit/debug path, and replacing that mechanism with semihosting would change the guest software rather than reproduce its existing behaviour.
So I think this particular question is mostly resolved: semihosting is relevant context, but not a transparent replacement for guest breakpoint instructions already present in existing firmware.
2. Is this problem specific to the Pico, or even to Arm M-profile?
I initially encountered the problem while working on RP2040 support, but the discussion strongly suggests that the underlying issue is more general.
I had already noted the analogous use of EBREAK on RISC-V, for example on RP2350 firmware.
Mohamed also described the x86 case, where QEMU distinguishes different kinds of breakpoint instructions and traps guest debug exceptions when GDB is attached, while noting the additional problem of reinjecting exceptions that are not intended for the external debugger.
Peter's comment also points in this direction:
> "But aren't breakpoint instructions of all kinds typically trapped to the external jtag debugger, on any architecture ? I'm a bit surprised that we don't do this already."
So my current understanding is that the problem should probably not be framed as an Arm M-profile-specific one.
If that is correct, then my current patch is addressing the issue at the wrong abstraction level by adding Arm-specific knowledge directly in target/arm/tcg/debug.c. I was in fact already expecting to face the same question for RISC-V EBREAK.
This would suggest that QEMU may instead need a more explicitly defined generic policy for guest breakpoint instructions executed while an external GDB client is attached, with architecture-specific code only deciding whether a particular debug event is eligible for external interception.
3. Which architectural state should control the interception?
Even if we agree that a guest breakpoint instruction may be consumed by an external debugger, my original condition is clearly too simple:
```
arm_feature(env, ARM_FEATURE_M) && gdb_cpu_is_attached(cs)
```
Alex explicitly mentioned DHCSR, DEMCR, and IMPDEF behaviour.
Mohamed cited, in particular, the architectural role of DHCSR.C_DEBUGEN and the Armv8-M rule that BKPT may lead either to debug halt, DebugMonitor, HardFault, or lockup depending on the debug configuration.
So the technical question is not merely whether a GDB client is attached. It is also which architectural state QEMU should take into account when deciding whether the breakpoint is consumed by the external debugger.
For Arm M-profile, this seems to include at least questions around:
* DHCSR.C_DEBUGEN;
* whether halting debug is enabled or permitted in the current processor state;
* DEMCR and DebugMonitor configuration;
* possible architecture-version-specific or IMPDEF behaviour.
This raises another conceptual question: should attaching GDB to QEMU merely make an external debugger available, or should it also imply some architectural debug configuration equivalent to what a real debug probe would establish?
4. How should self-hosted debugging be preserved?
This seems to me the main practical difficulty.
Peter explicitly pointed out that a guest breakpoint instruction may belong to a debugger running inside the guest itself.
Mohamed's x86 example makes the problem particularly clear: QEMU may first intercept a debug exception while GDB is attached, but if that exception is not intended for the external debugger, it must remain visible to the guest. Mohamed also mentioned that there is currently a bug in the x86 path where some such exceptions are ignored instead of reinjected.
So perhaps the real question is not simply:
```
external debugger or guest exception?
```
but rather:
```
Should the external debugger get a first chance to consume the debug event,
and if it does not, should the event be reinjected into the guest?
```
For Cortex-M, a rule that simply maps every BKPT to EXCP_DEBUG whenever GDB is attached could clearly steal an exception that should have remained guest-visible.
This means that any generic abstraction would need to define not only when an external debugger may intercept a guest breakpoint instruction, but also what happens when it does not consume it.
5. Should QEMU's gdbstub be modelled as an external debug monitor?
This now seems to me to be the most general question behind the RFC.
If the answer is yes, then some variant of the behaviour I proposed seems natural: a breakpoint instruction that would be consumed by an external debugger on real hardware should be reportable to an attached GDB client.
If the answer is no, then I think we need to clarify what the intended relationship is between QEMU's gdbstub and the architecture's external debug facilities.
Alex's remark captures the current situation well:
> "We could treat gdbstub as an external debug monitor but we don't currently model it as such."
So perhaps the main remaining design question is where this abstraction should live.
Should target-specific code directly know whether GDB is attached, as in my RFC?
Or should there be a generic QEMU debug layer representing an external debugger, with target code only reporting architecture-specific debug events and deciding, from architectural state, whether they are eligible for external interception?
The latter seems cleaner to me, especially if the same issue exists for Arm BKPT, RISC-V EBREAK, and breakpoint instructions on other architectures.
My current feeling is therefore that the original RFC probably puts the fix too low and too specifically in the Arm code, but that the underlying problem remains real: QEMU does not appear to have a clearly defined generic model for what should happen when guest code deliberately executes a breakpoint instruction while an external GDB client is attached.
----
I have tried to summarize the discussion as faithfully as possible, as I was beginning to lose track of the different points and positions.
This leaves me with one final question, which may actually be the key to choosing between the possible models discussed above.
Even if you decide that QEMU's gdbstub should represent an external debugger, what exactly should that imply?
Does attaching a GDB client itself mean that architectural external halting debug is active, so that eligible guest breakpoint instructions should naturally be consumed by the external debugger?
Or does it only mean that an external debugger is available, while actual interception must still depend on the architectural debug state, such as DHCSR.C_DEBUGEN and the relevant architecture-specific configuration?
My original patch implicitly assumes the first model:
GDB attached
=> external halting debug active
=> M-profile BKPT is reported to GDB
But the discussion around DHCSR, DEMCR, self-hosted debugging, and the role of the gdbstub suggests that this equivalence may be exactly what needs to be decided first.
Perhaps answering this question would also tell us where the abstraction should live: either directly in target-specific exception handling, or in a more generic QEMU external-debug layer with architecture-specific eligibility rules.
Have I missed an important case or misunderstood the emerging consensus here?
Thanks again for the discussion,
--
Gilles
> Le 8 juil. 2026 à 12:10, Mohamed Mediouni <mohamed@unpredictable.fr> a écrit :
>
>
>
>> On 8. Jul 2026, at 11:31, Peter Maydell <peter.maydell@linaro.org> wrote:
>>
>> On Tue, 7 Jul 2026 at 18:27, Richard Henderson
>> <richard.henderson@linaro.org> wrote:
>>>
>>> On 7/6/26 02:21, Peter Maydell wrote:
>>>> 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 ?
>>>
>>> We handle them via CPUBreakpoint structures; we never inject code changes.
>>
>> I don't mean "breakpoints that the gdbstub requests", I mean
>> "breakpoint insns that are already in the guest code that get
>> executed".
>>
>>> The patch description explains the confusion: this is attempting to replicate a feature of
>>> the Raspberry Pi Pico SDK, wherein a guest BPKT really is trapped by an external debugger.
>>
>> But aren't breakpoint instructions of all kinds typically trapped
>> to the external jtag debugger, on any architecture ? I'm a bit
>> surprised that we don't do this already.
>
> Some context on the gdbstub implementation from the x86 side:
>
> WHPX's gdbstub support is implemented with overriding the instruction
> instead of leveraging HW breakpoints - although a bit hackily.
>
> A big concern is still allowing self-hosted debug to work fine which
> makes things a bit harder.
>
> x86 makes things easier by having multiple different breakpoint instructions.
> Linux uses int3 and we use int1* for QEMU gdbstub-controlled breakpoints so
> that things (mostly) work out for everyone.
>
> And then, QEMU traps all debug exceptions from the guest when gdb is attached
> and is supposed to take action when it’s an int1 breakpoint and not others.
>
> Although we currently have a bug there that the non int1s just get ignored
> instead of being injected back.
>
> * on x86, the Intel manual says "Hardware vendors may use the INT1 instruction for hardware debug"
>
> On Cortex-M self-hosted debug is far less used than for emulating bigger chips.
>
> And then there’s emulating such a thing using hardware virt and using HW breakpoints
> which quickly becomes quite complicated...
[-- Attachment #2: Type: text/html, Size: 31560 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] target/arm: report M-profile BKPT to gdbstub when attached
2026-07-10 22:19 ` gilles grimaud
@ 2026-07-13 11:11 ` Alex Bennée
0 siblings, 0 replies; 13+ messages in thread
From: Alex Bennée @ 2026-07-13 11:11 UTC (permalink / raw)
To: gilles grimaud
Cc: Mohamed Mediouni, Peter Maydell, Richard Henderson, qemu-devel,
qemu-arm
gilles grimaud <gilles.grimaud@univ-lille.fr> writes:
> Thanks everyone for the detailed comments.
>
> Let me try to summarize where I think the discussion stands, and then go through the questions that have emerged, from the most
> specific ones to the more general and still unresolved ones.
>
> My RFC originally came from reading QEMU's gdbstub as, at least conceptually, an analogue of an external low-level debugger such as
> a JTAG-attached debugger. From that perspective, having an attached GDB client consume a guest BKPT used as an external
> debugger stop point seemed natural.
>
<snip>
>
> ```
> Should the external debugger get a first chance to consume the debug event,
> and if it does not, should the event be reinjected into the guest?
> ```
>
> For Cortex-M, a rule that simply maps every BKPT to EXCP_DEBUG whenever GDB is attached could clearly steal an exception that
> should have remained guest-visible.
>
> This means that any generic abstraction would need to define not only when an external debugger may intercept a guest breakpoint
> instruction, but also what happens when it does not consume it.
>
> 5. Should QEMU's gdbstub be modelled as an external debug monitor?
>
> This now seems to me to be the most general question behind the RFC.
>
> If the answer is yes, then some variant of the behaviour I proposed seems natural: a breakpoint instruction that would be consumed
> by an external debugger on real hardware should be reportable to an attached GDB client.
>
> If the answer is no, then I think we need to clarify what the intended relationship is between QEMU's gdbstub and the architecture's
> external debug facilities.
>
> Alex's remark captures the current situation well:
>
>> "We could treat gdbstub as an external debug monitor but we don't currently model it as such."
>
> So perhaps the main remaining design question is where this abstraction should live.
>
> Should target-specific code directly know whether GDB is attached, as in my RFC?
>
> Or should there be a generic QEMU debug layer representing an external debugger, with target code only reporting
> architecture-specific debug events and deciding, from architectural state, whether they are eligible for external interception?
>
> The latter seems cleaner to me, especially if the same issue exists for Arm BKPT, RISC-V EBREAK, and breakpoint instructions on other
> architectures.
>
> My current feeling is therefore that the original RFC probably puts the fix too low and too specifically in the Arm code, but that the
> underlying problem remains real: QEMU does not appear to have a clearly defined generic model for what should happen when guest
> code deliberately executes a breakpoint instruction while an external GDB client is attached.
>
> ----
>
> I have tried to summarize the discussion as faithfully as possible, as I was beginning to lose track of the different points and positions.
>
> This leaves me with one final question, which may actually be the key to choosing between the possible models discussed above.
>
> Even if you decide that QEMU's gdbstub should represent an external debugger, what exactly should that imply?
>
> Does attaching a GDB client itself mean that architectural external halting debug is active, so that eligible guest breakpoint instructions
> should naturally be consumed by the external debugger?
>
> Or does it only mean that an external debugger is available, while actual interception must still depend on the architectural debug
> state, such as DHCSR.C_DEBUGEN and the relevant architecture-specific configuration?
>
> My original patch implicitly assumes the first model:
>
> GDB attached
> => external halting debug active
> => M-profile BKPT is reported to GDB
>
> But the discussion around DHCSR, DEMCR, self-hosted debugging, and the role of the gdbstub suggests that this equivalence may be
> exactly what needs to be decided first.
>
> Perhaps answering this question would also tell us where the abstraction should live: either directly in target-specific exception
> handling, or in a more generic QEMU external-debug layer with
> architecture-specific eligibility rules.
I think if the gdbstub is going to intercept what would otherwise be
guest visible exceptions then that should be opt-in behaviour via some
sort of flag for gdbstub. Otherwise guests will get confused when they
miss them. For example we know Windows uses breakpoints during its
boot-up for some sort of internal book keeping.
While giving the option to intercept guest visible exceptions in gdbstub
could be done globally if we want to properly model the external debug
interface we probably need to come up with some new API's between
gdbstub and the guest architecture.
For example on ARM the guest can make changes to register state to limit
what the external debugger can do. I see arm_debug_check_breakpoints
does call arm_generate_debug_exceptions which takes into account the
state of OSLAR_EL1. Would anyone actually want to model
DBGAUTHSTATUS_EL1 which can limit which worlds the debugger can see?
Generally the big benefit of using gdbstub with emulation is you do get
full visibility of the system which is helpful when debugging low level
code. Also most of this stuff seems to be very IMPDEF which is not
normally something QEMU needs to worry about.
>
> Have I missed an important case or misunderstood the emerging consensus here?
>
> Thanks again for the discussion,
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-07-13 11:11 UTC | newest]
Thread overview: 13+ 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
2026-07-07 17:27 ` Richard Henderson
2026-07-07 17:43 ` gilles grimaud
2026-07-08 9:00 ` Alex Bennée
2026-07-08 9:17 ` Mohamed Mediouni
2026-07-08 9:31 ` Peter Maydell
2026-07-08 10:10 ` Mohamed Mediouni
2026-07-10 22:19 ` gilles grimaud
2026-07-13 11:11 ` 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.