@@ -1678,29 +1678,22 @@ void ppc_cpu_do_interrupt(CPUState *cs)
powerpc_excp(cpu, cs->exception_index);
}
-static void ppc_hw_interrupt(CPUPPCState *env)
+static int ppc_next_unmasked_interrupt(CPUPPCState *env)
{
- PowerPCCPU *cpu = env_archcpu(env);
bool async_deliver;
/* External reset */
if (env->pending_interrupts & PPC_INTERRUPT_RESET) {
- env->pending_interrupts &= ~PPC_INTERRUPT_RESET;
- powerpc_excp(cpu, POWERPC_EXCP_RESET);
- return;
+ return PPC_INTERRUPT_RESET;
}
/* Machine check exception */
if (env->pending_interrupts & PPC_INTERRUPT_MCK) {
- env->pending_interrupts &= ~PPC_INTERRUPT_MCK;
- powerpc_excp(cpu, POWERPC_EXCP_MCHECK);
- return;
+ return PPC_INTERRUPT_MCK;
}
#if 0 /* TODO */
/* External debug exception */
if (env->pending_interrupts & PPC_INTERRUPT_DEBUG) {
- env->pending_interrupts &= ~PPC_INTERRUPT_DEBUG;
- powerpc_excp(cpu, POWERPC_EXCP_DEBUG);
- return;
+ return PPC_INTERRUPT_DEBUG;
}
#endif
@@ -1718,9 +1711,7 @@ static void ppc_hw_interrupt(CPUPPCState *env)
bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
if ((async_deliver || !FIELD_EX64_HV(env->msr)) && hdice) {
/* HDEC clears on delivery */
- env->pending_interrupts &= ~PPC_INTERRUPT_HDECR;
- powerpc_excp(cpu, POWERPC_EXCP_HDECR);
- return;
+ return PPC_INTERRUPT_HDECR;
}
}
@@ -1729,8 +1720,7 @@ static void ppc_hw_interrupt(CPUPPCState *env)
/* LPCR will be clear when not supported so this will work */
bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE);
if ((async_deliver || !FIELD_EX64_HV(env->msr)) && hvice) {
- powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
- return;
+ return PPC_INTERRUPT_HVIRT;
}
}
@@ -1742,77 +1732,47 @@ static void ppc_hw_interrupt(CPUPPCState *env)
if ((async_deliver && !(heic && FIELD_EX64_HV(env->msr) &&
!FIELD_EX64(env->msr, MSR, PR))) ||
(env->has_hv_mode && !FIELD_EX64_HV(env->msr) && !lpes0)) {
- if (books_vhyp_promotes_external_to_hvirt(cpu)) {
- powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
- } else {
- powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL);
- }
- return;
+ return PPC_INTERRUPT_EXT;
}
}
if (FIELD_EX64(env->msr, MSR, CE)) {
/* External critical interrupt */
if (env->pending_interrupts & PPC_INTERRUPT_CEXT) {
- powerpc_excp(cpu, POWERPC_EXCP_CRITICAL);
- return;
+ return PPC_INTERRUPT_CEXT;
}
}
if (async_deliver != 0) {
/* Watchdog timer on embedded PowerPC */
if (env->pending_interrupts & PPC_INTERRUPT_WDT) {
- env->pending_interrupts &= ~PPC_INTERRUPT_WDT;
- powerpc_excp(cpu, POWERPC_EXCP_WDT);
- return;
+ return PPC_INTERRUPT_WDT;
}
if (env->pending_interrupts & PPC_INTERRUPT_CDOORBELL) {
- env->pending_interrupts &= ~PPC_INTERRUPT_CDOORBELL;
- powerpc_excp(cpu, POWERPC_EXCP_DOORCI);
- return;
+ return PPC_INTERRUPT_CDOORBELL;
}
/* Fixed interval timer on embedded PowerPC */
if (env->pending_interrupts & PPC_INTERRUPT_FIT) {
- env->pending_interrupts &= ~PPC_INTERRUPT_FIT;
- powerpc_excp(cpu, POWERPC_EXCP_FIT);
- return;
+ return PPC_INTERRUPT_FIT;
}
/* Programmable interval timer on embedded PowerPC */
if (env->pending_interrupts & PPC_INTERRUPT_PIT) {
- env->pending_interrupts &= ~PPC_INTERRUPT_PIT;
- powerpc_excp(cpu, POWERPC_EXCP_PIT);
- return;
+ return PPC_INTERRUPT_PIT;
}
/* Decrementer exception */
if (env->pending_interrupts & PPC_INTERRUPT_DECR) {
- if (ppc_decr_clear_on_delivery(env)) {
- env->pending_interrupts &= ~PPC_INTERRUPT_DECR;
- }
- powerpc_excp(cpu, POWERPC_EXCP_DECR);
- return;
+ return PPC_INTERRUPT_DECR;
}
if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
- env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL;
- if (is_book3s_arch2x(env)) {
- powerpc_excp(cpu, POWERPC_EXCP_SDOOR);
- } else {
- powerpc_excp(cpu, POWERPC_EXCP_DOORI);
- }
- return;
+ return PPC_INTERRUPT_DOORBELL;
}
if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) {
- env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL;
- powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV);
- return;
+ return PPC_INTERRUPT_HDOORBELL;
}
if (env->pending_interrupts & PPC_INTERRUPT_PERFM) {
- env->pending_interrupts &= ~PPC_INTERRUPT_PERFM;
- powerpc_excp(cpu, POWERPC_EXCP_PERFM);
- return;
+ return PPC_INTERRUPT_PERFM;
}
/* Thermal interrupt */
if (env->pending_interrupts & PPC_INTERRUPT_THERM) {
- env->pending_interrupts &= ~PPC_INTERRUPT_THERM;
- powerpc_excp(cpu, POWERPC_EXCP_THERM);
- return;
+ return PPC_INTERRUPT_THERM;
}
/* EBB exception */
if (env->pending_interrupts & PPC_INTERRUPT_EBB) {
@@ -1822,20 +1782,106 @@ static void ppc_hw_interrupt(CPUPPCState *env)
*/
if (FIELD_EX64(env->msr, MSR, PR) &&
(env->spr[SPR_BESCR] & BESCR_GE)) {
- env->pending_interrupts &= ~PPC_INTERRUPT_EBB;
-
- if (env->spr[SPR_BESCR] & BESCR_PMEO) {
- powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB);
- } else if (env->spr[SPR_BESCR] & BESCR_EEO) {
- powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB);
- }
-
- return;
+ return PPC_INTERRUPT_EBB;
}
}
}
- if (env->resume_as_sreset) {
+ return 0;
+}
+
+static void ppc_deliver_interrupt(CPUPPCState *env, int interrupt)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+
+ switch (interrupt) {
+ case PPC_INTERRUPT_RESET: /* External reset */
+ env->pending_interrupts &= ~PPC_INTERRUPT_RESET;
+ powerpc_excp(cpu, POWERPC_EXCP_RESET);
+ break;
+ case PPC_INTERRUPT_MCK: /* Machine check exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_MCK;
+ powerpc_excp(cpu, POWERPC_EXCP_MCHECK);
+ break;
+#if 0 /* TODO */
+ case PPC_INTERRUPT_DEBUG: /* External debug exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_DEBUG;
+ powerpc_excp(cpu, POWERPC_EXCP_DEBUG);
+ break;
+#endif
+
+ case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */
+ /* HDEC clears on delivery */
+ env->pending_interrupts &= ~PPC_INTERRUPT_HDECR;
+ powerpc_excp(cpu, POWERPC_EXCP_HDECR);
+ break;
+ case PPC_INTERRUPT_HVIRT: /* Hypervisor virtualization interrupt */
+ powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
+ break;
+
+ case PPC_INTERRUPT_EXT:
+ if (books_vhyp_promotes_external_to_hvirt(cpu)) {
+ powerpc_excp(cpu, POWERPC_EXCP_HVIRT);
+ } else {
+ powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL);
+ }
+ break;
+ case PPC_INTERRUPT_CEXT: /* External critical interrupt */
+ powerpc_excp(cpu, POWERPC_EXCP_CRITICAL);
+ break;
+
+ case PPC_INTERRUPT_WDT: /* Watchdog timer on embedded PowerPC */
+ env->pending_interrupts &= ~PPC_INTERRUPT_WDT;
+ powerpc_excp(cpu, POWERPC_EXCP_WDT);
+ break;
+ case PPC_INTERRUPT_CDOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_CDOORBELL;
+ powerpc_excp(cpu, POWERPC_EXCP_DOORCI);
+ break;
+ case PPC_INTERRUPT_FIT: /* Fixed interval timer on embedded PowerPC */
+ env->pending_interrupts &= ~PPC_INTERRUPT_FIT;
+ powerpc_excp(cpu, POWERPC_EXCP_FIT);
+ break;
+ case PPC_INTERRUPT_PIT: /* Programmable interval timer on embedded PowerPC */
+ env->pending_interrupts &= ~PPC_INTERRUPT_PIT;
+ powerpc_excp(cpu, POWERPC_EXCP_PIT);
+ break;
+ case PPC_INTERRUPT_DECR: /* Decrementer exception */
+ if (ppc_decr_clear_on_delivery(env)) {
+ env->pending_interrupts &= ~PPC_INTERRUPT_DECR;
+ }
+ powerpc_excp(cpu, POWERPC_EXCP_DECR);
+ break;
+ case PPC_INTERRUPT_DOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL;
+ if (is_book3s_arch2x(env)) {
+ powerpc_excp(cpu, POWERPC_EXCP_SDOOR);
+ } else {
+ powerpc_excp(cpu, POWERPC_EXCP_DOORI);
+ }
+ break;
+ case PPC_INTERRUPT_HDOORBELL:
+ env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL;
+ powerpc_excp(cpu, POWERPC_EXCP_SDOOR_HV);
+ break;
+ case PPC_INTERRUPT_PERFM:
+ env->pending_interrupts &= ~PPC_INTERRUPT_PERFM;
+ powerpc_excp(cpu, POWERPC_EXCP_PERFM);
+ break;
+ case PPC_INTERRUPT_THERM: /* Thermal interrupt */
+ env->pending_interrupts &= ~PPC_INTERRUPT_THERM;
+ powerpc_excp(cpu, POWERPC_EXCP_THERM);
+ break;
+ case PPC_INTERRUPT_EBB: /* EBB exception */
+ env->pending_interrupts &= ~PPC_INTERRUPT_EBB;
+ if (env->spr[SPR_BESCR] & BESCR_PMEO) {
+ powerpc_excp(cpu, POWERPC_EXCP_PERFM_EBB);
+ } else if (env->spr[SPR_BESCR] & BESCR_EEO) {
+ powerpc_excp(cpu, POWERPC_EXCP_EXTERNAL_EBB);
+ }
+ break;
+ case 0:
/*
* This is a bug ! It means that has_work took us out of halt without
* anything to deliver while in a PM state that requires getting
@@ -1847,8 +1893,10 @@ static void ppc_hw_interrupt(CPUPPCState *env)
* It generally means a discrepancy between the wakeup conditions in the
* processor has_work implementation and the logic in this function.
*/
- cpu_abort(env_cpu(env),
- "Wakeup from PM state but interrupt Undelivered");
+ assert(!env->resume_as_sreset);
+ break;
+ default:
+ cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt);
}
}
@@ -1884,15 +1932,22 @@ bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
+ int interrupt;
- if (interrupt_request & CPU_INTERRUPT_HARD) {
- ppc_hw_interrupt(env);
- if (env->pending_interrupts == 0) {
- cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
- }
- return true;
+ if ((interrupt_request & CPU_INTERRUPT_HARD) == 0) {
+ return false;
}
- return false;
+
+ interrupt = ppc_next_unmasked_interrupt(env);
+ if (interrupt == 0) {
+ return false;
+ }
+
+ ppc_deliver_interrupt(env, interrupt);
+ if (env->pending_interrupts == 0) {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+ return true;
}
#endif /* !CONFIG_USER_ONLY */
Split ppc_hw_interrupt into an interrupt masking method, ppc_next_unmasked_interrupt, and an interrupt processing method, ppc_deliver_interrupt. Signed-off-by: Matheus Ferst <matheus.ferst@eldorado.org.br> --- target/ppc/excp_helper.c | 207 +++++++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 76 deletions(-)