aboutsummaryrefslogtreecommitdiff
path: root/target/ppc/excp_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/ppc/excp_helper.c')
-rw-r--r--target/ppc/excp_helper.c165
1 files changed, 164 insertions, 1 deletions
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index c941c89..d8bca19 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -19,11 +19,11 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "qemu/log.h"
+#include "system/memory.h"
#include "system/tcg.h"
#include "system/system.h"
#include "system/runstate.h"
#include "cpu.h"
-#include "exec/exec-all.h"
#include "internal.h"
#include "helper_regs.h"
#include "hw/ppc/ppc.h"
@@ -949,6 +949,125 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
powerpc_set_excp_state(cpu, vector, new_msr);
}
+static void powerpc_excp_ppe42(PowerPCCPU *cpu, int excp)
+{
+ CPUPPCState *env = &cpu->env;
+ target_ulong msr, new_msr, vector;
+ target_ulong mcs = PPE42_ISR_MCS_INSTRUCTION;
+ bool promote_unmaskable;
+
+ msr = env->msr;
+
+ /*
+ * New interrupt handler msr preserves SIBRC and ME unless explicitly
+ * overridden by the exception. All other MSR bits are zeroed out.
+ */
+ new_msr = env->msr & (((target_ulong)1 << MSR_ME) | R_MSR_SIBRC_MASK);
+
+ /* HV emu assistance interrupt only exists on server arch 2.05 or later */
+ if (excp == POWERPC_EXCP_HV_EMU) {
+ excp = POWERPC_EXCP_PROGRAM;
+ }
+
+ /*
+ * Unmaskable interrupts (Program, ISI, Alignment and DSI) are promoted to
+ * machine check if MSR_UIE is 0.
+ */
+ promote_unmaskable = !(msr & ((target_ulong)1 << MSR_UIE));
+
+
+ switch (excp) {
+ case POWERPC_EXCP_MCHECK: /* Machine check exception */
+ break;
+ case POWERPC_EXCP_DSI: /* Data storage exception */
+ trace_ppc_excp_dsi(env->spr[SPR_PPE42_ISR], env->spr[SPR_PPE42_EDR]);
+ if (promote_unmaskable) {
+ excp = POWERPC_EXCP_MCHECK;
+ mcs = PPE42_ISR_MCS_DSI;
+ }
+ break;
+ case POWERPC_EXCP_ISI: /* Instruction storage exception */
+ trace_ppc_excp_isi(msr, env->nip);
+ if (promote_unmaskable) {
+ excp = POWERPC_EXCP_MCHECK;
+ mcs = PPE42_ISR_MCS_ISI;
+ }
+ break;
+ case POWERPC_EXCP_EXTERNAL: /* External input */
+ break;
+ case POWERPC_EXCP_ALIGN: /* Alignment exception */
+ if (promote_unmaskable) {
+ excp = POWERPC_EXCP_MCHECK;
+ mcs = PPE42_ISR_MCS_ALIGNMENT;
+ }
+ break;
+ case POWERPC_EXCP_PROGRAM: /* Program exception */
+ if (promote_unmaskable) {
+ excp = POWERPC_EXCP_MCHECK;
+ mcs = PPE42_ISR_MCS_PROGRAM;
+ }
+ switch (env->error_code & ~0xF) {
+ case POWERPC_EXCP_INVAL:
+ trace_ppc_excp_inval(env->nip);
+ env->spr[SPR_PPE42_ISR] &= ~((target_ulong)1 << PPE42_ISR_PTR);
+ break;
+ case POWERPC_EXCP_TRAP:
+ env->spr[SPR_PPE42_ISR] |= ((target_ulong)1 << PPE42_ISR_PTR);
+ break;
+ default:
+ /* Should never occur */
+ cpu_abort(env_cpu(env), "Invalid program exception %d. Aborting\n",
+ env->error_code);
+ break;
+ }
+#ifdef CONFIG_TCG
+ env->spr[SPR_PPE42_EDR] = ppc_ldl_code(env, env->nip);
+#endif
+ break;
+ case POWERPC_EXCP_DECR: /* Decrementer exception */
+ break;
+ case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */
+ trace_ppc_excp_print("FIT");
+ break;
+ case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */
+ trace_ppc_excp_print("WDT");
+ break;
+ case POWERPC_EXCP_RESET: /* System reset exception */
+ /* reset exceptions don't have ME set */
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+ break;
+ default:
+ cpu_abort(env_cpu(env), "Invalid PPE42 exception %d. Aborting\n",
+ excp);
+ break;
+ }
+
+ env->spr[SPR_SRR0] = env->nip;
+ env->spr[SPR_SRR1] = msr;
+
+ vector = env->excp_vectors[excp];
+ if (vector == (target_ulong)-1ULL) {
+ cpu_abort(env_cpu(env),
+ "Raised an exception without defined vector %d\n", excp);
+ }
+ vector |= env->spr[SPR_PPE42_IVPR];
+
+ if (excp == POWERPC_EXCP_MCHECK) {
+ /* Also set the Machine Check Status (MCS) */
+ env->spr[SPR_PPE42_ISR] &= ~R_PPE42_ISR_MCS_MASK;
+ env->spr[SPR_PPE42_ISR] |= (mcs & R_PPE42_ISR_MCS_MASK);
+ env->spr[SPR_PPE42_ISR] &= ~((target_ulong)1 << PPE42_ISR_MFE);
+
+ /* Machine checks halt execution if MSR_ME is 0 */
+ powerpc_mcheck_checkstop(env);
+
+ /* machine check exceptions don't have ME set */
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+ }
+
+ powerpc_set_excp_state(cpu, vector, new_msr);
+}
+
static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
{
CPUPPCState *env = &cpu->env;
@@ -1589,6 +1708,9 @@ void powerpc_excp(PowerPCCPU *cpu, int excp)
case POWERPC_EXCP_POWER11:
powerpc_excp_books(cpu, excp);
break;
+ case POWERPC_EXCP_PPE42:
+ powerpc_excp_ppe42(cpu, excp);
+ break;
default:
g_assert_not_reached();
}
@@ -1945,6 +2067,43 @@ static int p9_next_unmasked_interrupt(CPUPPCState *env,
}
#endif /* TARGET_PPC64 */
+static int ppe42_next_unmasked_interrupt(CPUPPCState *env)
+{
+ bool async_deliver;
+
+ /* External reset */
+ if (env->pending_interrupts & PPC_INTERRUPT_RESET) {
+ return PPC_INTERRUPT_RESET;
+ }
+ /* Machine check exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_MCK) {
+ return PPC_INTERRUPT_MCK;
+ }
+
+ async_deliver = FIELD_EX64(env->msr, MSR, EE);
+
+ if (async_deliver != 0) {
+ /* Watchdog timer */
+ if (env->pending_interrupts & PPC_INTERRUPT_WDT) {
+ return PPC_INTERRUPT_WDT;
+ }
+ /* External Interrupt */
+ if (env->pending_interrupts & PPC_INTERRUPT_EXT) {
+ return PPC_INTERRUPT_EXT;
+ }
+ /* Fixed interval timer */
+ if (env->pending_interrupts & PPC_INTERRUPT_FIT) {
+ return PPC_INTERRUPT_FIT;
+ }
+ /* Decrementer exception */
+ if (env->pending_interrupts & PPC_INTERRUPT_DECR) {
+ return PPC_INTERRUPT_DECR;
+ }
+ }
+
+ return 0;
+}
+
static int ppc_next_unmasked_interrupt(CPUPPCState *env)
{
uint32_t pending_interrupts = env->pending_interrupts;
@@ -1970,6 +2129,10 @@ static int ppc_next_unmasked_interrupt(CPUPPCState *env)
}
#endif
+ if (env->excp_model == POWERPC_EXCP_PPE42) {
+ return ppe42_next_unmasked_interrupt(env);
+ }
+
/* External reset */
if (pending_interrupts & PPC_INTERRUPT_RESET) {
return PPC_INTERRUPT_RESET;