Signed-off-by: Fabiano Rosas <faro...@linux.ibm.com> --- target/ppc/cpu.h | 1 + target/ppc/cpu_7xx.c | 193 ++++++++++++++++++++++++++++++++++++++- target/ppc/excp_helper.c | 181 ------------------------------------ 3 files changed, 193 insertions(+), 182 deletions(-)
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 1a5150f712..10920464ad 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -182,6 +182,7 @@ void powerpc_reset_excp_state(PowerPCCPU *cpu); 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); #define PPC_INPUT(env) ((env)->bus_model) diff --git a/target/ppc/cpu_7xx.c b/target/ppc/cpu_7xx.c index 423f4cb97a..6f99521aba 100644 --- a/target/ppc/cpu_7xx.c +++ b/target/ppc/cpu_7xx.c @@ -1,5 +1,5 @@ /* - * CPU initialization for PowerPC 7xx CPUs + * CPU initialization and exception dispatching for PowerPC 7xx CPUs * * Copyright IBM Corp. 2022 * @@ -8,10 +8,201 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "sysemu/hw_accel.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_7xx(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 7xx CPUs, so although the 7xx 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_IFTLB: /* Instruction fetch TLB error */ + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + ppc_excp_debug_sw_tlb(env, excp); + + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; + + 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 */ + 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_7xx(PowerPCCPU *cpu, int excp) +{ + g_assert_not_reached(); +} +#endif static int check_pow_hid0(CPUPPCState *env) { diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 2cbd7be499..1306dcedcf 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -376,187 +376,6 @@ void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, env->reserve_addr = -1; } -static void powerpc_excp_7xx(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 7xx CPUs, so although the 7xx 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_IFTLB: /* Instruction fetch TLB error */ - case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - ppc_excp_debug_sw_tlb(env, excp); - - msr |= env->crf[0] << 28; - msr |= env->error_code; /* key, D/I, S/L bits */ - /* Set way using a LRU mechanism */ - msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - - 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 */ - 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_74xx(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); -- 2.34.1