diff options
-rw-r--r-- | target/ppc/excp_helper.c | 27 |
1 files changed, 15 insertions, 12 deletions
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 8407e0a..7c7c8d1 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -748,6 +748,7 @@ void ppc_cpu_do_interrupt(CPUState *cs) static void ppc_hw_interrupt(CPUPPCState *env) { PowerPCCPU *cpu = ppc_env_get_cpu(env); + bool async_deliver; /* External reset */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { @@ -769,11 +770,20 @@ static void ppc_hw_interrupt(CPUPPCState *env) return; } #endif + + /* + * For interrupts that gate on MSR:EE, we need to do something a + * bit more subtle, as we need to let them through even when EE is + * clear when coming out of some power management states (in order + * for them to become a 0x100). + */ + async_deliver = (msr_ee != 0) || env->in_pm_state; + /* Hypervisor decrementer exception */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { /* LPCR will be clear when not supported so this will work */ bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); - if ((msr_ee != 0 || msr_hv == 0) && hdice) { + if ((async_deliver || msr_hv == 0) && hdice) { /* HDEC clears on delivery */ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR); @@ -783,7 +793,7 @@ static void ppc_hw_interrupt(CPUPPCState *env) /* Extermal interrupt can ignore MSR:EE under some circumstances */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - if (msr_ee != 0 || (env->has_hv_mode && msr_hv == 0 && !lpes0)) { + if (async_deliver || (env->has_hv_mode && msr_hv == 0 && !lpes0)) { powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL); return; } @@ -795,7 +805,7 @@ static void ppc_hw_interrupt(CPUPPCState *env) return; } } - if (msr_ee != 0) { + if (async_deliver != 0) { /* Watchdog timer on embedded PowerPC */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); @@ -943,21 +953,14 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn) cs = CPU(ppc_env_get_cpu(env)); cs->halted = 1; - env->in_pm_state = true; /* The architecture specifies that HDEC interrupts are * discarded in PM states */ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); - /* Technically, nap doesn't set EE, but if we don't set it - * then ppc_hw_interrupt() won't deliver. We could add some - * other tests there based on LPCR but it's simpler to just - * whack EE in. It will be cleared by the 0x100 at wakeup - * anyway. It will still be observable by the guest in SRR1 - * but this doesn't seem to be a problem. - */ - env->msr |= (1ull << MSR_EE); + /* Condition for waking up at 0x100 */ + env->in_pm_state = true; } #endif /* defined(TARGET_PPC64) */ |