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 <[email protected]> a écrit :
> 
> 
> 
>> On 8. Jul 2026, at 11:31, Peter Maydell <[email protected]> wrote:
>> 
>> On Tue, 7 Jul 2026 at 18:27, Richard Henderson
>> <[email protected]> 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...

Reply via email to