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 <[email protected]> a 
> écrit :
> 
> On 7/6/26 02:21, Peter Maydell wrote:
>> On Sun, 5 Jul 2026 at 23:44, Gilles Grimaud
>> <[email protected]> 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 <[email protected]>
>>> ---
>>>  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~

Reply via email to