From: Glenn Miles <[email protected]> Add support for the IBM PPE42 exception model including new exception vectors, exception priorities and setting of PPE42 SPRs for determining the cause of an exception.
Signed-off-by: Glenn Miles <[email protected]> Reviewed-by: Chinmay Rath <[email protected]> Signed-off-by: Harsh Prateek Bora <[email protected]> Link: https://lore.kernel.org/r/[email protected] Message-ID: <[email protected]> --- target/ppc/cpu_init.c | 39 ++++++++- target/ppc/excp_helper.c | 163 +++++++++++++++++++++++++++++++++++ target/ppc/tcg-excp_helper.c | 12 +++ 3 files changed, 213 insertions(+), 1 deletion(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index c78b255085..9e4ea8fd13 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -1720,6 +1720,30 @@ static void init_excp_4xx(CPUPPCState *env) #endif } +static void init_excp_ppe42(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* Machine Check vector changed after version 0 */ + if (((env->spr[SPR_PVR] & 0xf00000ul) >> 20) == 0) { + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000000; + } else { + env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000020; + } + env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000040; + env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000060; + env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000080; + env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x000000A0; + env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x000000C0; + env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x000000E0; + env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000100; + env->excp_vectors[POWERPC_EXCP_FIT] = 0x00000120; + env->excp_vectors[POWERPC_EXCP_WDT] = 0x00000140; + env->ivpr_mask = 0xFFFFFE00UL; + /* Hardware reset vector */ + env->hreset_vector = 0x00000040UL; +#endif +} + static void init_excp_MPC5xx(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) @@ -2245,6 +2269,7 @@ static void init_proc_ppe42(CPUPPCState *env) { register_ppe42_sprs(env); + init_excp_ppe42(env); env->dcache_line_size = 32; env->icache_line_size = 32; /* Allocate hardware IRQ controller */ @@ -2278,7 +2303,7 @@ static void ppe42_class_common_init(PowerPCCPUClass *pcc) (1ull << MSR_IPE) | R_MSR_SIBRCA_MASK; pcc->mmu_model = POWERPC_MMU_REAL; - pcc->excp_model = POWERPC_EXCP_40x; + pcc->excp_model = POWERPC_EXCP_PPE42; pcc->bus_model = PPC_FLAGS_INPUT_PPE42; pcc->bfd_mach = bfd_mach_ppc_403; pcc->flags = POWERPC_FLAG_PPE42 | POWERPC_FLAG_BUS_CLK; @@ -7856,6 +7881,18 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) * they can be read with "p $ivor0", "p $ivor1", etc. */ break; + case POWERPC_EXCP_PPE42: + qemu_fprintf(f, "SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx "\n", + env->spr[SPR_SRR0], env->spr[SPR_SRR1]); + + qemu_fprintf(f, " TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx + " ISR " TARGET_FMT_lx " EDR " TARGET_FMT_lx "\n", + env->spr[SPR_PPE42_TCR], env->spr[SPR_PPE42_TSR], + env->spr[SPR_PPE42_ISR], env->spr[SPR_PPE42_EDR]); + + qemu_fprintf(f, " PIR " TARGET_FMT_lx " IVPR " TARGET_FMT_lx "\n", + env->spr[SPR_PPE42_PIR], env->spr[SPR_PPE42_IVPR]); + break; case POWERPC_EXCP_40x: qemu_fprintf(f, " TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " ESR " TARGET_FMT_lx " DEAR " TARGET_FMT_lx "\n", diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 1efdc4066e..d8bca19fff 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -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; diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index f835be5156..edecfb8572 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -229,6 +229,18 @@ void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, case POWERPC_MMU_BOOKE206: env->spr[SPR_BOOKE_DEAR] = vaddr; break; + case POWERPC_MMU_REAL: + if (env->flags & POWERPC_FLAG_PPE42) { + env->spr[SPR_PPE42_EDR] = vaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_PPE42_ISR] |= PPE42_ISR_ST; + } else { + env->spr[SPR_PPE42_ISR] &= ~PPE42_ISR_ST; + } + } else { + env->spr[SPR_DAR] = vaddr; + } + break; default: env->spr[SPR_DAR] = vaddr; break; -- 2.43.5
