aboutsummaryrefslogtreecommitdiff
path: root/target/ppc/excp_helper.c
diff options
context:
space:
mode:
authorMatheus Ferst <matheus.ferst@eldorado.org.br>2022-10-11 17:48:03 -0300
committerDaniel Henrique Barboza <danielhb413@gmail.com>2022-10-28 13:15:22 -0300
commitde76b85c961378795a9e3044881554b25a4db333 (patch)
tree27e0aee0cb429c7548bde207f6e861cfe7eff695 /target/ppc/excp_helper.c
parent7b694df6a6deeac8ede0512f983c70463968021a (diff)
downloadqemu-de76b85c961378795a9e3044881554b25a4db333.zip
qemu-de76b85c961378795a9e3044881554b25a4db333.tar.gz
qemu-de76b85c961378795a9e3044881554b25a4db333.tar.bz2
target/ppc: split interrupt masking and delivery from ppc_hw_interrupt
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> Reviewed-by: Fabiano Rosas <farosas@linux.ibm.com> Message-Id: <20221011204829.1641124-4-matheus.ferst@eldorado.org.br> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Diffstat (limited to 'target/ppc/excp_helper.c')
-rw-r--r--target/ppc/excp_helper.c201
1 files changed, 125 insertions, 76 deletions
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 37d3526..759bad5 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -1684,29 +1684,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
@@ -1724,9 +1717,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;
}
}
@@ -1735,8 +1726,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;
}
}
@@ -1748,77 +1738,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) {
@@ -1828,20 +1788,100 @@ 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;
+
+ 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 ppc */
+ 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
@@ -1853,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);
}
}
@@ -1890,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 */