@@ -183,6 +183,7 @@ void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp);
void powerpc_excp_40x(PowerPCCPU *cpu, int excp);
void powerpc_excp_6xx(PowerPCCPU *cpu, int excp);
void powerpc_excp_7xx(PowerPCCPU *cpu, int excp);
+void powerpc_excp_74xx(PowerPCCPU *cpu, int excp);
#define PPC_INPUT(env) ((env)->bus_model)
@@ -1,5 +1,5 @@
/*
- * CPU initialization for PowerPC 74xx CPUs
+ * CPU initialization and exception dispatching for PowerPC 74xx CPUs
*
* Copyright IBM Corp. 2022
*
@@ -8,11 +8,14 @@
*/
#include "qemu/osdep.h"
+#include "qemu/log.h"
#include "sysemu/hw_accel.h"
#include "hw/ppc/ppc.h"
#include "fpu/softfloat.h"
#include "cpu.h"
#include "spr_common.h"
+#include "trace.h"
+#include "helper_regs.h"
static int check_pow_hid0(CPUPPCState *env)
{
@@ -32,6 +35,186 @@ static int check_pow_hid0_74xx(CPUPPCState *env)
return 0;
}
+#if !defined(CONFIG_USER_ONLY)
+void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+ target_ulong msr, new_msr, vector;
+
+ /* 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);
+
+ /*
+ * 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_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);
+
+ break;
+ case POWERPC_EXCP_DSI: /* Data storage exception */
+ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]);
+ break;
+ case POWERPC_EXCP_ISI: /* Instruction storage exception */
+ trace_ppc_excp_isi(msr, env->nip);
+ msr |= env->error_code;
+ break;
+ case POWERPC_EXCP_EXTERNAL: /* External input */
+ break;
+ case POWERPC_EXCP_ALIGN: /* Alignment exception */
+ /* Get rS/rD and rA from faulting opcode */
+ /*
+ * Note: the opcode fields will not be set properly for a
+ * direct store load/store, but nobody cares as nobody
+ * actually uses direct store segments.
+ */
+ env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16;
+ 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;
+ }
+
+ /*
+ * FP exceptions always have NIP pointing to the faulting
+ * instruction, so always use store_next and claim we are
+ * precise in the MSR.
+ */
+ msr |= 0x00100000;
+ break;
+ case POWERPC_EXCP_INVAL:
+ trace_ppc_excp_inval(env->nip);
+ msr |= 0x00080000;
+ break;
+ case POWERPC_EXCP_PRIV:
+ msr |= 0x00040000;
+ break;
+ case POWERPC_EXCP_TRAP:
+ msr |= 0x00020000;
+ break;
+ default:
+ /* Should never occur */
+ cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ env->error_code);
+ break;
+ }
+ break;
+ case POWERPC_EXCP_SYSCALL: /* System call exception */
+ {
+ int lev = env->error_code;
+
+ trace_ppc_syscall(env, lev);
+
+ /*
+ * We need to correct the NIP which in this case is supposed
+ * to point to the next instruction
+ */
+ env->nip += 4;
+
+ /*
+ * The Virtual Open Firmware (VOF) relies on the 'sc 1'
+ * instruction to communicate with QEMU. The pegasos2 machine
+ * uses VOF and the 74xx CPUs, so although the 74xx don't have
+ * HV mode, we need to keep hypercall support.
+ */
+ if ((lev == 1) && cpu->vhyp) {
+ PPCVirtualHypervisorClass *vhc =
+ PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+ vhc->hypercall(cpu->vhyp, cpu);
+ return;
+ }
+
+ break;
+ }
+ case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
+ case POWERPC_EXCP_DECR: /* Decrementer exception */
+ break;
+ case POWERPC_EXCP_RESET: /* System reset exception */
+ if (msr_pow) {
+ cpu_abort(cs, "Trying to deliver power-saving system reset "
+ "exception %d with no HV support\n", excp);
+ }
+ break;
+ case POWERPC_EXCP_TRACE: /* Trace exception */
+ break;
+ case POWERPC_EXCP_VPU: /* Vector unavailable exception */
+ break;
+ case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
+ case POWERPC_EXCP_SMI: /* System management interrupt */
+ case POWERPC_EXCP_THERM: /* Thermal interrupt */
+ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
+ case POWERPC_EXCP_VPUA: /* Vector assist exception */
+ 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;
+ }
+
+ /*
+ * Sort out endianness of interrupt, this differs depending on the
+ * CPU, the HV mode, etc...
+ */
+ if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
+ new_msr |= (target_ulong)1 << MSR_LE;
+ }
+
+ /* Save PC */
+ env->spr[SPR_SRR0] = env->nip;
+
+ /* Save MSR */
+ env->spr[SPR_SRR1] = msr;
+
+ powerpc_set_excp_state(cpu, vector, new_msr);
+}
+#else
+void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
+{
+ g_assert_not_reached();
+}
+#endif
+
static inline void vscr_init(CPUPPCState *env, uint32_t val)
{
/* Altivec always uses round-to-nearest */
@@ -376,179 +376,6 @@ void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector,
env->reserve_addr = -1;
}
-static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
-{
- CPUState *cs = CPU(cpu);
- CPUPPCState *env = &cpu->env;
- target_ulong msr, new_msr, vector;
-
- /* 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);
-
- /*
- * 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_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);
-
- break;
- case POWERPC_EXCP_DSI: /* Data storage exception */
- trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]);
- break;
- case POWERPC_EXCP_ISI: /* Instruction storage exception */
- trace_ppc_excp_isi(msr, env->nip);
- msr |= env->error_code;
- break;
- case POWERPC_EXCP_EXTERNAL: /* External input */
- break;
- case POWERPC_EXCP_ALIGN: /* Alignment exception */
- /* Get rS/rD and rA from faulting opcode */
- /*
- * Note: the opcode fields will not be set properly for a
- * direct store load/store, but nobody cares as nobody
- * actually uses direct store segments.
- */
- env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16;
- 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;
- }
-
- /*
- * FP exceptions always have NIP pointing to the faulting
- * instruction, so always use store_next and claim we are
- * precise in the MSR.
- */
- msr |= 0x00100000;
- break;
- case POWERPC_EXCP_INVAL:
- trace_ppc_excp_inval(env->nip);
- msr |= 0x00080000;
- break;
- case POWERPC_EXCP_PRIV:
- msr |= 0x00040000;
- break;
- case POWERPC_EXCP_TRAP:
- msr |= 0x00020000;
- break;
- default:
- /* Should never occur */
- cpu_abort(cs, "Invalid program exception %d. Aborting\n",
- env->error_code);
- break;
- }
- break;
- case POWERPC_EXCP_SYSCALL: /* System call exception */
- {
- int lev = env->error_code;
-
- trace_ppc_syscall(env, lev);
-
- /*
- * We need to correct the NIP which in this case is supposed
- * to point to the next instruction
- */
- env->nip += 4;
-
- /*
- * The Virtual Open Firmware (VOF) relies on the 'sc 1'
- * instruction to communicate with QEMU. The pegasos2 machine
- * uses VOF and the 74xx CPUs, so although the 74xx don't have
- * HV mode, we need to keep hypercall support.
- */
- if ((lev == 1) && cpu->vhyp) {
- PPCVirtualHypervisorClass *vhc =
- PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
- vhc->hypercall(cpu->vhyp, cpu);
- return;
- }
-
- break;
- }
- case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
- case POWERPC_EXCP_DECR: /* Decrementer exception */
- break;
- case POWERPC_EXCP_RESET: /* System reset exception */
- if (msr_pow) {
- cpu_abort(cs, "Trying to deliver power-saving system reset "
- "exception %d with no HV support\n", excp);
- }
- break;
- case POWERPC_EXCP_TRACE: /* Trace exception */
- break;
- case POWERPC_EXCP_VPU: /* Vector unavailable exception */
- break;
- case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
- case POWERPC_EXCP_SMI: /* System management interrupt */
- case POWERPC_EXCP_THERM: /* Thermal interrupt */
- case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
- case POWERPC_EXCP_VPUA: /* Vector assist exception */
- 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;
- }
-
- /*
- * Sort out endianness of interrupt, this differs depending on the
- * CPU, the HV mode, etc...
- */
- if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
- new_msr |= (target_ulong)1 << MSR_LE;
- }
-
- /* Save PC */
- env->spr[SPR_SRR0] = env->nip;
-
- /* Save MSR */
- env->spr[SPR_SRR1] = msr;
-
- powerpc_set_excp_state(cpu, vector, new_msr);
-}
-
static void powerpc_excp_booke(PowerPCCPU *cpu, int excp)
{
CPUState *cs = CPU(cpu);
Signed-off-by: Fabiano Rosas <farosas@linux.ibm.com> --- target/ppc/cpu.h | 1 + target/ppc/cpu_74xx.c | 185 ++++++++++++++++++++++++++++++++++++++- target/ppc/excp_helper.c | 173 ------------------------------------ 3 files changed, 185 insertions(+), 174 deletions(-)