@@ -179,6 +179,7 @@ const char *powerpc_excp_name(int excp);
void powerpc_set_excp_state(PowerPCCPU *cpu,
target_ulong vector, target_ulong msr);
void powerpc_reset_excp_state(PowerPCCPU *cpu);
+void powerpc_excp_40x(PowerPCCPU *cpu, int excp);
#define PPC_INPUT(env) ((env)->bus_model)
@@ -1,5 +1,5 @@
/*
- * CPU initialization for PowerPC 40x CPUs
+ * CPU initialization and exception dispatching for PowerPC 40x CPUs
*
* Copyright IBM Corp. 2022
*
@@ -8,9 +8,157 @@
*/
#include "qemu/osdep.h"
+#include "qemu/log.h"
#include "hw/ppc/ppc.h"
#include "cpu.h"
#include "spr_common.h"
+#include "trace.h"
+#include "helper_regs.h"
+
+#if !defined(CONFIG_USER_ONLY)
+void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+ target_ulong msr, new_msr, vector;
+ int srr0, srr1;
+
+ /* new srr1 value excluding must-be-zero bits */
+ msr = env->msr & ~0x783f0000ULL;
+
+ /*
+ * new interrupt handler msr preserves existing ME unless
+ * explicitly overriden.
+ */
+ new_msr = env->msr & (((target_ulong)1 << MSR_ME));
+
+ /* target registers */
+ srr0 = SPR_SRR0;
+ srr1 = SPR_SRR1;
+
+ /*
+ * Hypervisor emulation assistance interrupt only exists on server
+ * arch 2.05 server or later.
+ */
+ if (excp == POWERPC_EXCP_HV_EMU) {
+ excp = POWERPC_EXCP_PROGRAM;
+ }
+
+ vector = env->excp_vectors[excp];
+ if (vector == (target_ulong)-1ULL) {
+ cpu_abort(cs, "Raised an exception without defined vector %d\n",
+ excp);
+ }
+
+ vector |= env->excp_prefix;
+
+ switch (excp) {
+ case POWERPC_EXCP_CRITICAL: /* Critical input */
+ srr0 = SPR_40x_SRR2;
+ srr1 = SPR_40x_SRR3;
+ break;
+ case POWERPC_EXCP_MCHECK: /* Machine check exception */
+ if (msr_me == 0) {
+ /*
+ * Machine check exception is not enabled. Enter
+ * checkstop state.
+ */
+ fprintf(stderr, "Machine check while not allowed. "
+ "Entering checkstop state\n");
+ if (qemu_log_separate()) {
+ qemu_log("Machine check while not allowed. "
+ "Entering checkstop state\n");
+ }
+ cs->halted = 1;
+ cpu_interrupt_exittb(cs);
+ }
+
+ /* machine check exceptions don't have ME set */
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+
+ srr0 = SPR_40x_SRR2;
+ srr1 = SPR_40x_SRR3;
+ break;
+ case POWERPC_EXCP_DSI: /* Data storage exception */
+ trace_ppc_excp_dsi(env->spr[SPR_40x_ESR], env->spr[SPR_40x_DEAR]);
+ break;
+ case POWERPC_EXCP_ISI: /* Instruction storage exception */
+ trace_ppc_excp_isi(msr, env->nip);
+ break;
+ case POWERPC_EXCP_EXTERNAL: /* External input */
+ break;
+ case POWERPC_EXCP_ALIGN: /* Alignment exception */
+ break;
+ case POWERPC_EXCP_PROGRAM: /* Program exception */
+ switch (env->error_code & ~0xF) {
+ case POWERPC_EXCP_FP:
+ if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) {
+ trace_ppc_excp_fp_ignore();
+ powerpc_reset_excp_state(cpu);
+ return;
+ }
+ env->spr[SPR_40x_ESR] = ESR_FP;
+ break;
+ case POWERPC_EXCP_INVAL:
+ trace_ppc_excp_inval(env->nip);
+ env->spr[SPR_40x_ESR] = ESR_PIL;
+ break;
+ case POWERPC_EXCP_PRIV:
+ env->spr[SPR_40x_ESR] = ESR_PPR;
+ break;
+ case POWERPC_EXCP_TRAP:
+ env->spr[SPR_40x_ESR] = ESR_PTR;
+ break;
+ default:
+ cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ env->error_code);
+ break;
+ }
+ break;
+ case POWERPC_EXCP_SYSCALL: /* System call exception */
+ trace_ppc_syscall(env, 0);
+
+ /*
+ * We need to correct the NIP which in this case is supposed
+ * to point to the next instruction
+ */
+ env->nip += 4;
+ 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_DTLB: /* Data TLB error */
+ case POWERPC_EXCP_ITLB: /* Instruction TLB error */
+ break;
+ case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
+ trace_ppc_excp_print("PIT");
+ break;
+ case POWERPC_EXCP_DEBUG: /* Debug interrupt */
+ cpu_abort(cs, "%s exception not implemented\n",
+ powerpc_excp_name(excp));
+ break;
+ default:
+ cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ break;
+ }
+
+ /* Save PC */
+ env->spr[srr0] = env->nip;
+
+ /* Save MSR */
+ env->spr[srr1] = msr;
+
+ powerpc_set_excp_state(cpu, vector, new_msr);
+}
+#else
+void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
+{
+ g_assert_not_reached();
+}
+#endif
/* SPR shared between PowerPC 40x implementations */
static void register_40x_sprs(CPUPPCState *env)
@@ -376,144 +376,6 @@ void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector,
env->reserve_addr = -1;
}
-static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
-{
- CPUState *cs = CPU(cpu);
- CPUPPCState *env = &cpu->env;
- target_ulong msr, new_msr, vector;
- int srr0, srr1;
-
- /* new srr1 value excluding must-be-zero bits */
- msr = env->msr & ~0x783f0000ULL;
-
- /*
- * new interrupt handler msr preserves existing ME unless
- * explicitly overriden.
- */
- new_msr = env->msr & (((target_ulong)1 << MSR_ME));
-
- /* target registers */
- srr0 = SPR_SRR0;
- srr1 = SPR_SRR1;
-
- /*
- * Hypervisor emulation assistance interrupt only exists on server
- * arch 2.05 server or later.
- */
- if (excp == POWERPC_EXCP_HV_EMU) {
- excp = POWERPC_EXCP_PROGRAM;
- }
-
- vector = env->excp_vectors[excp];
- if (vector == (target_ulong)-1ULL) {
- cpu_abort(cs, "Raised an exception without defined vector %d\n",
- excp);
- }
-
- vector |= env->excp_prefix;
-
- switch (excp) {
- case POWERPC_EXCP_CRITICAL: /* Critical input */
- srr0 = SPR_40x_SRR2;
- srr1 = SPR_40x_SRR3;
- break;
- case POWERPC_EXCP_MCHECK: /* Machine check exception */
- if (msr_me == 0) {
- /*
- * Machine check exception is not enabled. Enter
- * checkstop state.
- */
- fprintf(stderr, "Machine check while not allowed. "
- "Entering checkstop state\n");
- if (qemu_log_separate()) {
- qemu_log("Machine check while not allowed. "
- "Entering checkstop state\n");
- }
- cs->halted = 1;
- cpu_interrupt_exittb(cs);
- }
-
- /* machine check exceptions don't have ME set */
- new_msr &= ~((target_ulong)1 << MSR_ME);
-
- srr0 = SPR_40x_SRR2;
- srr1 = SPR_40x_SRR3;
- break;
- case POWERPC_EXCP_DSI: /* Data storage exception */
- trace_ppc_excp_dsi(env->spr[SPR_40x_ESR], env->spr[SPR_40x_DEAR]);
- break;
- case POWERPC_EXCP_ISI: /* Instruction storage exception */
- trace_ppc_excp_isi(msr, env->nip);
- break;
- case POWERPC_EXCP_EXTERNAL: /* External input */
- break;
- case POWERPC_EXCP_ALIGN: /* Alignment exception */
- break;
- case POWERPC_EXCP_PROGRAM: /* Program exception */
- switch (env->error_code & ~0xF) {
- case POWERPC_EXCP_FP:
- if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) {
- trace_ppc_excp_fp_ignore();
- powerpc_reset_excp_state(cpu);
- return;
- }
- env->spr[SPR_40x_ESR] = ESR_FP;
- break;
- case POWERPC_EXCP_INVAL:
- trace_ppc_excp_inval(env->nip);
- env->spr[SPR_40x_ESR] = ESR_PIL;
- break;
- case POWERPC_EXCP_PRIV:
- env->spr[SPR_40x_ESR] = ESR_PPR;
- break;
- case POWERPC_EXCP_TRAP:
- env->spr[SPR_40x_ESR] = ESR_PTR;
- break;
- default:
- cpu_abort(cs, "Invalid program exception %d. Aborting\n",
- env->error_code);
- break;
- }
- break;
- case POWERPC_EXCP_SYSCALL: /* System call exception */
- trace_ppc_syscall(env, 0);
-
- /*
- * We need to correct the NIP which in this case is supposed
- * to point to the next instruction
- */
- env->nip += 4;
- 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_DTLB: /* Data TLB error */
- case POWERPC_EXCP_ITLB: /* Instruction TLB error */
- break;
- case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
- trace_ppc_excp_print("PIT");
- break;
- case POWERPC_EXCP_DEBUG: /* Debug interrupt */
- cpu_abort(cs, "%s exception not implemented\n",
- powerpc_excp_name(excp));
- break;
- default:
- cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
- break;
- }
-
- /* Save PC */
- env->spr[srr0] = env->nip;
-
- /* Save MSR */
- env->spr[srr1] = msr;
-
- powerpc_set_excp_state(cpu, vector, new_msr);
-}
-
static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp)
{
CPUState *cs = CPU(cpu);
This change along with the next few patches which move the powerpc_excp_foo functions into the cpu_foo.c files has the following purposes: - Move the exception dispatching (powerpc_excp) closer to the exception vectors initialization (init_excp); - Make it immediately clear which CPUs are affected by the exception dispatching code; - Move code which is not a TCG helper out of excp_helpers.c. Aside from the obvious, this also helps with separation between TCG and KVM code; excp_helpers could be TCG-only (we're not quite there yet); - We could start thinking about ways of simplifying the init_excp/powerpc_excp relationship since both users of the POWERPC_EXCP vector addresses are now in the same place. Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com> --- target/ppc/cpu.h | 1 + target/ppc/cpu_40x.c | 150 ++++++++++++++++++++++++++++++++++++++- target/ppc/excp_helper.c | 138 ----------------------------------- 3 files changed, 150 insertions(+), 139 deletions(-)