On 2026/09/03 01:50 PM, Kirill A. Korinsky wrote:
> OpenBSD writes zero to an edge triggered decrementer, then enters idle
> expecting its interrupt. QEMU defers the already due edge to a timer at
> the current virtual time, leaving the guest stalled.
> 
> Signed-off-by: Kirill A. Korinsky <[email protected]>
> ---
>  hw/ppc/ppc.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
> index b123b4cc1c..d99e47d532 100644
> --- a/hw/ppc/ppc.c
> +++ b/hw/ppc/ppc.c
> @@ -885,6 +885,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, int64_t 
> now, uint64_t *nextp,
>       * an edge interrupt, so raise it here too.
>       */
>      if (((flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) ||
> +        ((flags & PPC_DECR_UNDERFLOW_TRIGGERED) && value == 0) ||

The fix is correct for mac99/G3/G4, but the guard flag is too broad and
introduces a regression on PPE42.

Commit 17dd1354c1d1 ("target/ppc: Decrementer fix BookE semantics") explicitly
removed the immediate raise on value==0 with the rationale: "BookE says a
decrementer interrupt should not be raised on a store of 0, only of a decrement
from 1."

The new clause:

    ((flags & PPC_DECR_UNDERFLOW_TRIGGERED) && value == 0)

also fires for PPE42, which goes through ppc_booke_timers_init() with
PPC_TIMER_PPE and gets PPC_DECR_UNDERFLOW_TRIGGERED set - introduced by commit
7197f6f7baf2 ("hw/ppc: Support for an IBM PPE42 CPU decrementer").  That commit
explicitly documents that PPE42 raises an interrupt when DEC[0] transitions from
0 to -1 (non-negative to negative) — not on a store of zero.  The new clause
contradicts this.

Note that __cpu_ppc_store_decr() is reached by BookE machines too — both on
direct guest writes to SPR_DECR and via the auto-reload path in booke_decr_cb()
(which calls cpu_ppc_store_decr(env, DECAR) when TCR_ARE is set; the existing
DECAR != 0 guard there already prevents a zero value from arriving via that
route).  So the safety of normal BookE targets (e500, sam460ex, bamboo, virtex)
is flag-based: they have PPC_DECR_ZERO_TRIGGERED set but not
PPC_DECR_UNDERFLOW_TRIGGERED, so neither the old clause nor the new one fires
for a guest write of zero.  That is the correct BookE behaviour per the
architecture.

PPC_DECR_ZERO_TRIGGERED already exists in ppc.h (defined as "Decr interrupt
triggered when the decrementer reaches zero") but is never checked in
__cpu_ppc_store_decr().  I think the minimal fix is to set it in
cpu_ppc_tb_init() for the non-64-bit path and guard the new clause on it:

diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index b123b4cc1cce..6f99b61a2876 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -885,6 +885,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, int64_t 
now, uint64_t *nextp,
      * an edge interrupt, so raise it here too.
      */
     if (((flags & PPC_DECR_UNDERFLOW_LEVEL) && signed_value < 0) ||
+        ((flags & PPC_DECR_ZERO_TRIGGERED) && value == 0) ||
         ((flags & PPC_DECR_UNDERFLOW_TRIGGERED) && signed_value < 0
           && signed_decr >= 0)) {
         (*raise_excp)(cpu);
@@ -1104,6 +1105,12 @@ void cpu_ppc_tb_init(CPUPPCState *env, uint32_t freq)
     if (is_book3s_arch2x(env)) {
         /* All Book3S 64bit CPUs implement level based DEC logic */
         tb_env->flags |= PPC_DECR_UNDERFLOW_LEVEL;
+    } else {
+        /*
+         * Classic 32-bit edge-triggered decrementer: zero write fires
+         * immediately
+         */
+        tb_env->flags |= PPC_DECR_ZERO_TRIGGERED;
     }
     /* Create new timer */
     tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,


All callers of cpu_ppc_tb_init() that use a 32-bit classic CPU (mac99 with 7400,
mac_oldworld/750, prep/604, pegasos/7457, amigaone/7457) take the else branch
and get PPC_DECR_ZERO_TRIGGERED.  64-bit Book3S machines (spapr, pnv, mac99 with
970fx) take the if branch and are unchanged.  PPE42 goes through
ppc_booke_timers_init() and never gets PPC_DECR_ZERO_TRIGGERED, so the new
clause does not fire for it.

This scopes the fix to the classic 32-bit edge-triggered path (mac99, pegasos,
prep, mac_oldworld, amigaone) and leaves PPE42 behaviour unchanged.  What do you
think?

Thanks,
Amit

Reply via email to