diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index a2801f6e6b..847f8a33be 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -28,6 +28,7 @@ #include "trace.h" #ifdef CONFIG_TCG +#include "sysemu/tcg.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #endif @@ -141,7 +142,7 @@ static inline bool insn_need_byteswap(CPUArchState *env) return !!(env->msr & ((target_ulong)1 << MSR_LE)); } -static uint32_t ppc_ldl_code(CPUArchState *env, hwaddr addr) +static uint32_t ppc_ldl_code(CPUArchState *env, abi_ptr addr) { uint32_t insn = cpu_ldl_code(env, addr); @@ -1348,6 +1349,72 @@ static bool books_vhyp_handles_hv_excp(PowerPCCPU *cpu) return false; } +#ifdef CONFIG_TCG +static bool is_prefix_insn(CPUPPCState *env, uint32_t insn) +{ + if (!(env->insns_flags2 & PPC2_ISA310)) { + return false; + } + return ((insn & 0xfc000000) == 0x04000000); +} + +static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) +{ + CPUPPCState *env = &cpu->env; + + if (!tcg_enabled()) { + /* + * This does not load instructions and set the prefix bit correctly + * for injected interrupts with KVM. That may have to be discovered + * and set by the KVM layer before injecting. + */ + return false; + } + + switch (excp) { + case POWERPC_EXCP_HDSI: + /* HDSI PRTABLE_FAULT has the originating access type in error_code */ + if ((env->spr[SPR_HDSISR] & DSISR_PRTABLE_FAULT) && + (env->error_code == MMU_INST_FETCH)) { + /* + * Fetch failed due to partition scope translation, so prefix + * indication is not relevant (and attempting to load the + * instruction at NIP would cause recursive faults with the same + * translation). + */ + break; + } + /* fall through */ + case POWERPC_EXCP_MCHECK: + case POWERPC_EXCP_DSI: + case POWERPC_EXCP_DSEG: + case POWERPC_EXCP_ALIGN: + case POWERPC_EXCP_PROGRAM: + case POWERPC_EXCP_FPU: + case POWERPC_EXCP_TRACE: + case POWERPC_EXCP_HV_EMU: + case POWERPC_EXCP_VPU: + case POWERPC_EXCP_VSXU: + case POWERPC_EXCP_FU: + case POWERPC_EXCP_HV_FU: { + uint32_t insn = ppc_ldl_code(env, env->nip); + if (is_prefix_insn(env, insn)) { + return true; + } + break; + } + default: + break; + } + return false; +} +#else +static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) +{ + return false; +} +#endif + static void powerpc_excp_books(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); @@ -1395,6 +1462,10 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) vector |= env->excp_prefix; + if (is_prefix_insn_excp(cpu, excp)) { + msr |= PPC_BIT(34); + } + switch (excp) { case POWERPC_EXCP_MCHECK: /* Machine check exception */ if (!FIELD_EX64(env->msr, MSR, ME)) { diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 1fc1ba3ecf..920084bd8f 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -145,6 +145,13 @@ static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type, CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; + env->error_code = 0; + if (cause & DSISR_PRTABLE_FAULT) { + /* HDSI PRTABLE_FAULT gets the originating access type in error_code */ + env->error_code = access_type; + access_type = MMU_DATA_LOAD; + } + qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx" 0x%" HWADDR_PRIx" cause %08x\n", __func__, access_str(access_type), @@ -166,7 +173,6 @@ static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type, env->spr[SPR_HDSISR] = cause; env->spr[SPR_HDAR] = eaddr; env->spr[SPR_ASDR] = g_raddr; - env->error_code = 0; break; default: g_assert_not_reached(); @@ -369,13 +375,14 @@ static bool validate_pate(PowerPCCPU *cpu, uint64_t lpid, ppc_v3_pate_t *pate) } static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, - MMUAccessType access_type, + MMUAccessType orig_access_type, vaddr eaddr, hwaddr g_raddr, ppc_v3_pate_t pate, hwaddr *h_raddr, int *h_prot, int *h_page_size, bool pde_addr, int mmu_idx, bool guest_visible) { + MMUAccessType access_type = orig_access_type; int fault_cause = 0; hwaddr pte_addr; uint64_t pte; @@ -404,7 +411,8 @@ static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, fault_cause |= DSISR_PRTABLE_FAULT; } if (guest_visible) { - ppc_radix64_raise_hsi(cpu, access_type, eaddr, g_raddr, fault_cause); + ppc_radix64_raise_hsi(cpu, orig_access_type, + eaddr, g_raddr, fault_cause); } return 1; }