From c700b5e162208a0fa4211fc6d9dab271b1342640 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 21 Feb 2024 20:08:31 +1000 Subject: [PATCH 01/72] spapr: avoid overhead of finding vhyp class in critical operations PPC_VIRTUAL_HYPERVISOR_GET_CLASS is used in critical operations like interrupts and TLB misses and is quite costly. Running the kvm-unit-tests sieve program with radix MMU enabled thrashes the TCG TLB and spends a lot of time in TLB and page table walking code. The test takes 67 seconds to complete with a lot of time being spent in code related to finding the vhyp class: 12.01% [.] g_str_hash 8.94% [.] g_hash_table_lookup 8.06% [.] object_class_dynamic_cast 6.21% [.] address_space_ldq 4.94% [.] __strcmp_avx2 4.28% [.] tlb_set_page_full 4.08% [.] address_space_translate_internal 3.17% [.] object_class_dynamic_cast_assert 2.84% [.] ppc_radix64_xlate Keep a pointer to the class and avoid this lookup. This reduces the execution time to 40 seconds. Reviewed-by: Harsh Prateek Bora Signed-off-by: Nicholas Piggin --- hw/ppc/pegasos2.c | 1 + target/ppc/cpu.h | 3 ++- target/ppc/cpu_init.c | 9 +++------ target/ppc/excp_helper.c | 16 ++++------------ target/ppc/kvm.c | 4 +--- target/ppc/mmu-book3s-v3.h | 4 +--- target/ppc/mmu-hash64.c | 16 ++++------------ target/ppc/mmu-radix64.c | 4 +--- 8 files changed, 17 insertions(+), 40 deletions(-) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 04d6decb2b..c22e8b336d 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -400,6 +400,7 @@ static void pegasos2_machine_reset(MachineState *machine, ShutdownCause reason) machine->fdt = fdt; pm->cpu->vhyp = PPC_VIRTUAL_HYPERVISOR(machine); + pm->cpu->vhyp_class = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(pm->cpu->vhyp); } enum pegasos2_rtas_tokens { diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 0ac55d6b25..a5f46d0b10 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1435,6 +1435,7 @@ struct ArchCPU { int vcpu_id; uint32_t compat_pvr; PPCVirtualHypervisor *vhyp; + PPCVirtualHypervisorClass *vhyp_class; void *machine_data; int32_t node_id; /* NUMA node this CPU belongs to */ PPCHash64Options *hash64_opts; @@ -1532,7 +1533,7 @@ DECLARE_OBJ_CHECKERS(PPCVirtualHypervisor, PPCVirtualHypervisorClass, static inline bool vhyp_cpu_in_nested(PowerPCCPU *cpu) { - return PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp)->cpu_in_nested(cpu); + return cpu->vhyp_class->cpu_in_nested(cpu); } #endif /* CONFIG_USER_ONLY */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index c11a69fd90..914c6e0f18 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6661,6 +6661,7 @@ void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) CPUPPCState *env = &cpu->env; cpu->vhyp = vhyp; + cpu->vhyp_class = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(vhyp); /* * With a virtual hypervisor mode we never allow the CPU to go @@ -7248,9 +7249,7 @@ static void ppc_cpu_exec_enter(CPUState *cs) PowerPCCPU *cpu = POWERPC_CPU(cs); if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->cpu_exec_enter(cpu->vhyp, cpu); + cpu->vhyp_class->cpu_exec_enter(cpu->vhyp, cpu); } } @@ -7259,9 +7258,7 @@ static void ppc_cpu_exec_exit(CPUState *cs) PowerPCCPU *cpu = POWERPC_CPU(cs); if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->cpu_exec_exit(cpu->vhyp, cpu); + cpu->vhyp_class->cpu_exec_exit(cpu->vhyp, cpu); } } #endif /* CONFIG_TCG */ diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 0712098cf7..9df17f93bf 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -794,9 +794,7 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) * 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); + cpu->vhyp_class->hypercall(cpu->vhyp, cpu); powerpc_reset_excp_state(cpu); return; } @@ -946,9 +944,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) * 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); + cpu->vhyp_class->hypercall(cpu->vhyp, cpu); powerpc_reset_excp_state(cpu); return; } @@ -1437,9 +1433,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) /* "PAPR mode" built-in hypercall emulation */ if (lev == 1 && books_vhyp_handles_hcall(cpu)) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); + cpu->vhyp_class->hypercall(cpu->vhyp, cpu); powerpc_reset_excp_state(cpu); return; } @@ -1574,10 +1568,8 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) } if ((new_msr & MSR_HVB) && books_vhyp_handles_hv_excp(cpu)) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); /* Deliver interrupt to L1 by returning from the H_ENTER_NESTED call */ - vhc->deliver_hv_excp(cpu, excp); + cpu->vhyp_class->deliver_hv_excp(cpu, excp); powerpc_reset_excp_state(cpu); } else { /* Sanity check */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 46fccff786..005f2239f3 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -865,9 +865,7 @@ int kvmppc_put_books_sregs(PowerPCCPU *cpu) sregs.pvr = env->spr[SPR_PVR]; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - sregs.u.s.sdr1 = vhc->encode_hpt_for_kvm_pr(cpu->vhyp); + sregs.u.s.sdr1 = cpu->vhyp_class->encode_hpt_for_kvm_pr(cpu->vhyp); } else { sregs.u.s.sdr1 = env->spr[SPR_SDR1]; } diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index 674377a19e..f3f7993958 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -108,9 +108,7 @@ static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) uint64_t base; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - return vhc->hpt_mask(cpu->vhyp); + return cpu->vhyp_class->hpt_mask(cpu->vhyp); } if (cpu->env.mmu_model == POWERPC_MMU_3_00) { ppc_v3_pate_t pate; diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 0966422a55..accbf0b2d8 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -517,9 +517,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - return vhc->map_hptes(cpu->vhyp, ptex, n); + return cpu->vhyp_class->map_hptes(cpu->vhyp, ptex, n); } base = ppc_hash64_hpt_base(cpu); @@ -539,9 +537,7 @@ void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, hwaddr ptex, int n) { if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->unmap_hptes(cpu->vhyp, hptes, ptex, n); + cpu->vhyp_class->unmap_hptes(cpu->vhyp, hptes, ptex, n); return; } @@ -821,9 +817,7 @@ static void ppc_hash64_set_r(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1) hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + HPTE64_DW1_R; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hpte_set_r(cpu->vhyp, ptex, pte1); + cpu->vhyp_class->hpte_set_r(cpu->vhyp, ptex, pte1); return; } base = ppc_hash64_hpt_base(cpu); @@ -838,9 +832,7 @@ static void ppc_hash64_set_c(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1) hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + HPTE64_DW1_C; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hpte_set_c(cpu->vhyp, ptex, pte1); + cpu->vhyp_class->hpte_set_c(cpu->vhyp, ptex, pte1); return; } base = ppc_hash64_hpt_base(cpu); diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 8daf71d2db..fefa55a5f1 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -678,9 +678,7 @@ static bool ppc_radix64_xlate_impl(PowerPCCPU *cpu, vaddr eaddr, /* Get Partition Table */ if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc; - vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - if (!vhc->get_pate(cpu->vhyp, cpu, lpid, &pate)) { + if (!cpu->vhyp_class->get_pate(cpu->vhyp, cpu, lpid, &pate)) { if (guest_visible) { ppc_radix64_raise_hsi(cpu, access_type, eaddr, eaddr, DSISR_R_BADCONFIG); From 95912ce1ebe4303d17118219691573ae6227b0e2 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 22 Jan 2024 16:21:15 +1000 Subject: [PATCH 02/72] ppc/spapr: Add ibm,pi-features The ibm,pi-features property has a bit to say whether or not msgsndp should be used. Linux checks if it is being run under KVM and avoids msgsndp anyway, but it would be preferable to rely on this bit. Reviewed-by: Harsh Prateek Bora Signed-off-by: Nicholas Piggin --- hw/ppc/spapr.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d2d1e310a3..4345764bce 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -353,6 +353,32 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); } +static void spapr_dt_pi_features(SpaprMachineState *spapr, + PowerPCCPU *cpu, + void *fdt, int offset) +{ + uint8_t pi_features[] = { 1, 0, + 0x00 }; + + if (kvm_enabled() && ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, + 0, cpu->compat_pvr)) { + /* + * POWER9 and later CPUs with KVM run in LPAR-per-thread mode where + * all threads are essentially independent CPUs, and msgsndp does not + * work (because it is physically-addressed) and therefore is + * emulated by KVM, so disable it here to ensure XIVE will be used. + * This is both KVM and CPU implementation-specific behaviour so a KVM + * cap would be cleanest, but for now this works. If KVM ever permits + * native msgsndp execution by guests, a cap could be added at that + * time. + */ + pi_features[2] |= 0x08; /* 4: No msgsndp */ + } + + _FDT((fdt_setprop(fdt, offset, "ibm,pi-features", pi_features, + sizeof(pi_features)))); +} + static hwaddr spapr_node0_size(MachineState *machine) { if (machine->numa_state->num_nodes) { @@ -815,6 +841,8 @@ static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset, spapr_dt_pa_features(spapr, cpu, fdt, offset); + spapr_dt_pi_features(spapr, cpu, fdt, offset); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", cs->cpu_index / vcpus_per_socket))); From 82676f1fc4b1511a5fe32256aaec885d200ffbf6 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 26 Mar 2024 23:20:43 +1000 Subject: [PATCH 03/72] target/ppc: Fix broadcast tlbie synchronisation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With mttcg, broadcast tlbie instructions do not wait until other vCPUs have been kicked out of TCG execution before they complete (including necessary subsequent tlbsync, etc., instructions). This is contrary to the ISA, and it permits other vCPUs to use translations after the TLB flush. For example: CPU0 // *memP is initially 0, memV maps to memP with *pte *pte = 0; ptesync ; tlbie ; eieio ; tlbsync ; ptesync *memP = 1; CPU1 assert(*memV == 0); It is possible for the assertion to fail because CPU1 translates memV using the TLB after CPU0 has stored 1 to the underlying memory. This race was observed with a careful test case where CPU1 checks run in a very large expensive TB so it can run for the entire CPU0 period between clearing the pte and storing the memory, but host vCPU thread preemption could cause the race to hit anywhere. As explained in commit 4ddc104689b ("target/ppc: Fix tlbie"), it is not enough to just use tlb_flush_all_cpus_synced(), because that does not execute until the calling CPU has finished its TB. It is also required that the TB is ended at the point where the TLB flush must subsequently take effect. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Nicholas Piggin --- target/ppc/helper_regs.c | 2 +- target/ppc/mmu_helper.c | 2 +- target/ppc/translate.c | 7 +++++++ target/ppc/translate/storage-ctrl-impl.c.inc | 7 +++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 25258986e3..9094ae5004 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -334,7 +334,7 @@ void check_tlb_flush(CPUPPCState *env, bool global) if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) { env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH; env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; - tlb_flush_all_cpus(cs); + tlb_flush_all_cpus_synced(cs); return; } diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index b35a93c198..d9d950e220 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -534,7 +534,7 @@ void helper_tlbie_isa300(CPUPPCState *env, target_ulong rb, target_ulong rs, if (local) { tlb_flush_page(env_cpu(env), addr); } else { - tlb_flush_page_all_cpus(env_cpu(env), addr); + tlb_flush_page_all_cpus_synced(env_cpu(env), addr); } return; diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 49dee6cab0..24461c2d1b 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3494,6 +3494,13 @@ static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) gen_helper_check_tlb_flush_local(tcg_env); } gen_set_label(l); + if (global) { + /* + * Global TLB flush uses async-work which must run before the + * next instruction, so this must be the last in the TB. + */ + ctx->base.is_jmp = DISAS_EXIT_UPDATE; + } } #else static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) { } diff --git a/target/ppc/translate/storage-ctrl-impl.c.inc b/target/ppc/translate/storage-ctrl-impl.c.inc index 74c23a4191..b8b4454663 100644 --- a/target/ppc/translate/storage-ctrl-impl.c.inc +++ b/target/ppc/translate/storage-ctrl-impl.c.inc @@ -224,6 +224,13 @@ static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) a->prs << TLBIE_F_PRS_SHIFT | a->r << TLBIE_F_R_SHIFT | local << TLBIE_F_LOCAL_SHIFT)); + if (!local) { + /* + * Global TLB flush uses async-work which must run before the + * next instruction, so this must be the last in the TB. + */ + ctx->base.is_jmp = DISAS_EXIT_UPDATE; + } return true; #endif From 99cd12ced16d15a1ffde055f842497747f070f91 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 27 Mar 2024 00:04:20 +1000 Subject: [PATCH 04/72] tcg/cputlb: Remove non-synced variants of global TLB flushes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are no longer used. tlb_flush_all_cpus: removed by previous commit. tlb_flush_page_all_cpus: removed by previous commit. tlb_flush_page_bits_by_mmuidx_all_cpus: never used. tlb_flush_page_by_mmuidx_all_cpus: never used. tlb_flush_page_bits_by_mmuidx_all_cpus: never used, thus: tlb_flush_range_by_mmuidx_all_cpus: never used. tlb_flush_by_mmuidx_all_cpus: never used. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Nicholas Piggin --- accel/tcg/cputlb.c | 103 -------------------------------- docs/devel/multi-thread-tcg.rst | 13 ++-- include/exec/exec-all.h | 101 ++++++------------------------- 3 files changed, 21 insertions(+), 196 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index cdb3e12dfb..45799869eb 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -431,21 +431,6 @@ void tlb_flush(CPUState *cpu) tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS); } -void tlb_flush_by_mmuidx_all_cpus(CPUState *src_cpu, uint16_t idxmap) -{ - const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; - - tlb_debug("mmu_idx: 0x%"PRIx16"\n", idxmap); - - flush_all_helper(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); - fn(src_cpu, RUN_ON_CPU_HOST_INT(idxmap)); -} - -void tlb_flush_all_cpus(CPUState *src_cpu) -{ - tlb_flush_by_mmuidx_all_cpus(src_cpu, ALL_MMUIDX_BITS); -} - void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap) { const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; @@ -656,46 +641,6 @@ void tlb_flush_page(CPUState *cpu, vaddr addr) tlb_flush_page_by_mmuidx(cpu, addr, ALL_MMUIDX_BITS); } -void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, vaddr addr, - uint16_t idxmap) -{ - tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%"PRIx16"\n", addr, idxmap); - - /* This should already be page aligned */ - addr &= TARGET_PAGE_MASK; - - /* - * Allocate memory to hold addr+idxmap only when needed. - * See tlb_flush_page_by_mmuidx for details. - */ - if (idxmap < TARGET_PAGE_SIZE) { - flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1, - RUN_ON_CPU_TARGET_PTR(addr | idxmap)); - } else { - CPUState *dst_cpu; - - /* Allocate a separate data block for each destination cpu. */ - CPU_FOREACH(dst_cpu) { - if (dst_cpu != src_cpu) { - TLBFlushPageByMMUIdxData *d - = g_new(TLBFlushPageByMMUIdxData, 1); - - d->addr = addr; - d->idxmap = idxmap; - async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2, - RUN_ON_CPU_HOST_PTR(d)); - } - } - } - - tlb_flush_page_by_mmuidx_async_0(src_cpu, addr, idxmap); -} - -void tlb_flush_page_all_cpus(CPUState *src, vaddr addr) -{ - tlb_flush_page_by_mmuidx_all_cpus(src, addr, ALL_MMUIDX_BITS); -} - void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, vaddr addr, uint16_t idxmap) @@ -887,54 +832,6 @@ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, tlb_flush_range_by_mmuidx(cpu, addr, TARGET_PAGE_SIZE, idxmap, bits); } -void tlb_flush_range_by_mmuidx_all_cpus(CPUState *src_cpu, - vaddr addr, vaddr len, - uint16_t idxmap, unsigned bits) -{ - TLBFlushRangeData d; - CPUState *dst_cpu; - - /* - * If all bits are significant, and len is small, - * this devolves to tlb_flush_page. - */ - if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) { - tlb_flush_page_by_mmuidx_all_cpus(src_cpu, addr, idxmap); - return; - } - /* If no page bits are significant, this devolves to tlb_flush. */ - if (bits < TARGET_PAGE_BITS) { - tlb_flush_by_mmuidx_all_cpus(src_cpu, idxmap); - return; - } - - /* This should already be page aligned */ - d.addr = addr & TARGET_PAGE_MASK; - d.len = len; - d.idxmap = idxmap; - d.bits = bits; - - /* Allocate a separate data block for each destination cpu. */ - CPU_FOREACH(dst_cpu) { - if (dst_cpu != src_cpu) { - TLBFlushRangeData *p = g_memdup(&d, sizeof(d)); - async_run_on_cpu(dst_cpu, - tlb_flush_range_by_mmuidx_async_1, - RUN_ON_CPU_HOST_PTR(p)); - } - } - - tlb_flush_range_by_mmuidx_async_0(src_cpu, d); -} - -void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu, - vaddr addr, uint16_t idxmap, - unsigned bits) -{ - tlb_flush_range_by_mmuidx_all_cpus(src_cpu, addr, TARGET_PAGE_SIZE, - idxmap, bits); -} - void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, vaddr addr, vaddr len, diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst index 1420789fff..d706c27ea7 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.rst @@ -205,15 +205,10 @@ DESIGN REQUIREMENTS: (Current solution) -We have updated cputlb.c to defer operations when a cross-vCPU -operation with async_run_on_cpu() which ensures each vCPU sees a -coherent state when it next runs its work (in a few instructions -time). - -A new set up operations (tlb_flush_*_all_cpus) take an additional flag -which when set will force synchronisation by setting the source vCPUs -work as "safe work" and exiting the cpu run loop. This ensure by the -time execution restarts all flush operations have completed. +A new set of tlb flush operations (tlb_flush_*_all_cpus_synced) force +synchronisation by setting the source vCPUs work as "safe work" and +exiting the cpu run loop. This ensures that by the time execution +restarts all flush operations have completed. TLB flag updates are all done atomically and are also protected by the corresponding page lock. diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 2cd7b8f61b..b6b46ad13c 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -67,25 +67,16 @@ void tlb_destroy(CPUState *cpu); * MMU indexes. */ void tlb_flush_page(CPUState *cpu, vaddr addr); -/** - * tlb_flush_page_all_cpus: - * @cpu: src CPU of the flush - * @addr: virtual address of page to be flushed - * - * Flush one page from the TLB of the specified CPU, for all - * MMU indexes. - */ -void tlb_flush_page_all_cpus(CPUState *src, vaddr addr); /** * tlb_flush_page_all_cpus_synced: * @cpu: src CPU of the flush * @addr: virtual address of page to be flushed * - * Flush one page from the TLB of the specified CPU, for all MMU - * indexes like tlb_flush_page_all_cpus except the source vCPUs work - * is scheduled as safe work meaning all flushes will be complete once - * the source vCPUs safe work is complete. This will depend on when - * the guests translation ends the TB. + * Flush one page from the TLB of all CPUs, for all + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr); /** @@ -98,19 +89,14 @@ void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr); * use one of the other functions for efficiency. */ void tlb_flush(CPUState *cpu); -/** - * tlb_flush_all_cpus: - * @cpu: src CPU of the flush - */ -void tlb_flush_all_cpus(CPUState *src_cpu); /** * tlb_flush_all_cpus_synced: * @cpu: src CPU of the flush * - * Like tlb_flush_all_cpus except this except the source vCPUs work is - * scheduled as safe work meaning all flushes will be complete once - * the source vCPUs safe work is complete. This will depend on when - * the guests translation ends the TB. + * Flush the entire TLB for all CPUs, for all MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_all_cpus_synced(CPUState *src_cpu); /** @@ -125,27 +111,16 @@ void tlb_flush_all_cpus_synced(CPUState *src_cpu); void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap); /** - * tlb_flush_page_by_mmuidx_all_cpus: + * tlb_flush_page_by_mmuidx_all_cpus_synced: * @cpu: Originating CPU of the flush * @addr: virtual address of page to be flushed * @idxmap: bitmap of MMU indexes to flush * * Flush one page from the TLB of all CPUs, for the specified * MMU indexes. - */ -void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, - uint16_t idxmap); -/** - * tlb_flush_page_by_mmuidx_all_cpus_synced: - * @cpu: Originating CPU of the flush - * @addr: virtual address of page to be flushed - * @idxmap: bitmap of MMU indexes to flush * - * Flush one page from the TLB of all CPUs, for the specified MMU - * indexes like tlb_flush_page_by_mmuidx_all_cpus except the source - * vCPUs work is scheduled as safe work meaning all flushes will be - * complete once the source vCPUs safe work is complete. This will - * depend on when the guests translation ends the TB. + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, uint16_t idxmap); @@ -159,25 +134,16 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, * MMU indexes. */ void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap); -/** - * tlb_flush_by_mmuidx_all_cpus: - * @cpu: Originating CPU of the flush - * @idxmap: bitmap of MMU indexes to flush - * - * Flush all entries from all TLBs of all CPUs, for the specified - * MMU indexes. - */ -void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap); /** * tlb_flush_by_mmuidx_all_cpus_synced: * @cpu: Originating CPU of the flush * @idxmap: bitmap of MMU indexes to flush * - * Flush all entries from all TLBs of all CPUs, for the specified - * MMU indexes like tlb_flush_by_mmuidx_all_cpus except except the source - * vCPUs work is scheduled as safe work meaning all flushes will be - * complete once the source vCPUs safe work is complete. This will - * depend on when the guests translation ends the TB. + * Flush all entries from the TLB of all CPUs, for the specified + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap); @@ -194,8 +160,6 @@ void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits); /* Similarly, with broadcast and syncing. */ -void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, - uint16_t idxmap, unsigned bits); void tlb_flush_page_bits_by_mmuidx_all_cpus_synced (CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits); @@ -215,9 +179,6 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, unsigned bits); /* Similarly, with broadcast and syncing. */ -void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, vaddr addr, - vaddr len, uint16_t idxmap, - unsigned bits); void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, vaddr len, @@ -290,18 +251,12 @@ static inline void tlb_destroy(CPUState *cpu) static inline void tlb_flush_page(CPUState *cpu, vaddr addr) { } -static inline void tlb_flush_page_all_cpus(CPUState *src, vaddr addr) -{ -} static inline void tlb_flush_page_all_cpus_synced(CPUState *src, vaddr addr) { } static inline void tlb_flush(CPUState *cpu) { } -static inline void tlb_flush_all_cpus(CPUState *src_cpu) -{ -} static inline void tlb_flush_all_cpus_synced(CPUState *src_cpu) { } @@ -313,20 +268,11 @@ static inline void tlb_flush_page_by_mmuidx(CPUState *cpu, static inline void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) { } -static inline void tlb_flush_page_by_mmuidx_all_cpus(CPUState *cpu, - vaddr addr, - uint16_t idxmap) -{ -} static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, uint16_t idxmap) { } -static inline void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap) -{ -} - static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { @@ -337,12 +283,6 @@ static inline void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, unsigned bits) { } -static inline void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *cpu, - vaddr addr, - uint16_t idxmap, - unsigned bits) -{ -} static inline void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, uint16_t idxmap, unsigned bits) @@ -353,13 +293,6 @@ static inline void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, unsigned bits) { } -static inline void tlb_flush_range_by_mmuidx_all_cpus(CPUState *cpu, - vaddr addr, - vaddr len, - uint16_t idxmap, - unsigned bits) -{ -} static inline void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, vaddr addr, vaddr len, From 30933c4fb4f3df95ae44c4c3c86a5df049852c01 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 27 Mar 2024 00:18:14 +1000 Subject: [PATCH 05/72] tcg/cputlb: remove other-cpu capability from TLB flushing Some TLB flush operations can flush other CPUs. The problem with this is they used non-synced variants of flushes (i.e., that return before the destination has completed the flush). Since all TLB flush users need the _synced variants, and that last user (ppc) of the non-synced flush was buggy, this is a footgun waiting to go off. There do not seem to be any callers that flush other CPUs, so remove the capability. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- accel/tcg/cputlb.c | 42 +++++++++--------------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 45799869eb..117b516739 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -418,12 +418,9 @@ void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) { tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap); - if (cpu->created && !qemu_cpu_is_self(cpu)) { - async_run_on_cpu(cpu, tlb_flush_by_mmuidx_async_work, - RUN_ON_CPU_HOST_INT(idxmap)); - } else { - tlb_flush_by_mmuidx_async_work(cpu, RUN_ON_CPU_HOST_INT(idxmap)); - } + assert_cpu_is_self(cpu); + + tlb_flush_by_mmuidx_async_work(cpu, RUN_ON_CPU_HOST_INT(idxmap)); } void tlb_flush(CPUState *cpu) @@ -612,28 +609,12 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, vaddr addr, uint16_t idxmap) { tlb_debug("addr: %016" VADDR_PRIx " mmu_idx:%" PRIx16 "\n", addr, idxmap); + assert_cpu_is_self(cpu); + /* This should already be page aligned */ addr &= TARGET_PAGE_MASK; - if (qemu_cpu_is_self(cpu)) { - tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); - } else if (idxmap < TARGET_PAGE_SIZE) { - /* - * Most targets have only a few mmu_idx. In the case where - * we can stuff idxmap into the low TARGET_PAGE_BITS, avoid - * allocating memory for this operation. - */ - async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_1, - RUN_ON_CPU_TARGET_PTR(addr | idxmap)); - } else { - TLBFlushPageByMMUIdxData *d = g_new(TLBFlushPageByMMUIdxData, 1); - - /* Otherwise allocate a structure, freed by the worker. */ - d->addr = addr; - d->idxmap = idxmap; - async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_2, - RUN_ON_CPU_HOST_PTR(d)); - } + tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); } void tlb_flush_page(CPUState *cpu, vaddr addr) @@ -796,6 +777,8 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, { TLBFlushRangeData d; + assert_cpu_is_self(cpu); + /* * If all bits are significant, and len is small, * this devolves to tlb_flush_page. @@ -816,14 +799,7 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, d.idxmap = idxmap; d.bits = bits; - if (qemu_cpu_is_self(cpu)) { - tlb_flush_range_by_mmuidx_async_0(cpu, d); - } else { - /* Otherwise allocate a structure, freed by the worker. */ - TLBFlushRangeData *p = g_memdup(&d, sizeof(d)); - async_run_on_cpu(cpu, tlb_flush_range_by_mmuidx_async_1, - RUN_ON_CPU_HOST_PTR(p)); - } + tlb_flush_range_by_mmuidx_async_0(cpu, d); } void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, vaddr addr, From 13f50867837874892f33c32a4452843d9ce7144c Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 1 May 2024 23:04:32 +1000 Subject: [PATCH 06/72] target/ppc: Move sync instructions to decodetree This tries to faithfully reproduce the odd BookE logic. Note the e206 check in gen_msync_4xx() is always false, so not carried over. It does change the handling of non-zero reserved bits outside the defined fields from being illegal to being ignored, which the architecture specifies ot help with backward compatibility of new fields. The existing behaviour causes illegal instruction exceptions when using new POWER10 sync variants that add new fields, after this the instructions are accepted and are implemented as supersets of the new behaviour, as intended. Reviewed-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 7 ++ target/ppc/translate.c | 102 +-------------------- target/ppc/translate/misc-impl.c.inc | 130 +++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 100 deletions(-) create mode 100644 target/ppc/translate/misc-impl.c.inc diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index eada59f59f..6b89804b15 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -998,3 +998,10 @@ MSGSND 011111 ----- ----- ..... 0011001110 - @X_rb MSGCLRP 011111 ----- ----- ..... 0010101110 - @X_rb MSGSNDP 011111 ----- ----- ..... 0010001110 - @X_rb MSGSYNC 011111 ----- ----- ----- 1101110110 - + +# Memory Barrier Instructions + +&X_sync l +@X_sync ...... ... l:2 ..... ..... .......... . &X_sync +SYNC 011111 --- .. ----- ----- 1001010110 - @X_sync +EIEIO 011111 ----- ----- ----- 1101010110 - diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 24461c2d1b..a70c5ed951 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3422,59 +3422,6 @@ static void gen_stswx(DisasContext *ctx) gen_helper_stsw(tcg_env, t0, t1, t2); } -/*** Memory synchronisation ***/ -/* eieio */ -static void gen_eieio(DisasContext *ctx) -{ - TCGBar bar = TCG_MO_ALL; - - /* - * eieio has complex semanitcs. It provides memory ordering between - * operations in the set: - * - loads from CI memory. - * - stores to CI memory. - * - stores to WT memory. - * - * It separately also orders memory for operations in the set: - * - stores to cacheble memory. - * - * It also serializes instructions: - * - dcbt and dcbst. - * - * It separately serializes: - * - tlbie and tlbsync. - * - * And separately serializes: - * - slbieg, slbiag, and slbsync. - * - * The end result is that CI memory ordering requires TCG_MO_ALL - * and it is not possible to special-case more relaxed ordering for - * cacheable accesses. TCG_BAR_SC is required to provide this - * serialization. - */ - - /* - * POWER9 has a eieio instruction variant using bit 6 as a hint to - * tell the CPU it is a store-forwarding barrier. - */ - if (ctx->opcode & 0x2000000) { - /* - * ISA says that "Reserved fields in instructions are ignored - * by the processor". So ignore the bit 6 on non-POWER9 CPU but - * as this is not an instruction software should be using, - * complain to the user. - */ - if (!(ctx->insns_flags2 & PPC2_ISA300)) { - qemu_log_mask(LOG_GUEST_ERROR, "invalid eieio using bit 6 at @" - TARGET_FMT_lx "\n", ctx->cia); - } else { - bar = TCG_MO_ST_LD; - } - } - - tcg_gen_mb(bar | TCG_BAR_SC); -} - #if !defined(CONFIG_USER_ONLY) static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) { @@ -3883,31 +3830,6 @@ static void gen_stqcx_(DisasContext *ctx) } #endif /* defined(TARGET_PPC64) */ -/* sync */ -static void gen_sync(DisasContext *ctx) -{ - TCGBar bar = TCG_MO_ALL; - uint32_t l = (ctx->opcode >> 21) & 3; - - if ((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) { - bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; - } - - /* - * We may need to check for a pending TLB flush. - * - * We do this on ptesync (l == 2) on ppc64 and any sync pn ppc32. - * - * Additionally, this can only happen in kernel mode however so - * check MSR_PR as well. - */ - if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { - gen_check_tlb_flush(ctx, true); - } - - tcg_gen_mb(bar | TCG_BAR_SC); -} - /* wait */ static void gen_wait(DisasContext *ctx) { @@ -6016,23 +5938,6 @@ static void gen_dlmzb(DisasContext *ctx) cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); } -/* mbar replaces eieio on 440 */ -static void gen_mbar(DisasContext *ctx) -{ - /* interpreted as no-op */ -} - -/* msync replaces sync on 440 */ -static void gen_msync_4xx(DisasContext *ctx) -{ - /* Only e500 seems to treat reserved bits as invalid */ - if ((ctx->insns_flags2 & PPC2_BOOKE206) && - (ctx->opcode & 0x03FFF801)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - } - /* otherwise interpreted as no-op */ -} - /* icbt */ static void gen_icbt_440(DisasContext *ctx) { @@ -6370,6 +6275,8 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/storage-ctrl-impl.c.inc" +#include "translate/misc-impl.c.inc" + /* Handles lfdp */ static void gen_dform39(DisasContext *ctx) { @@ -6498,7 +6405,6 @@ GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_STRING), GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_STRING), GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING), GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING), -GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x01FFF801, PPC_MEM_EIEIO), GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM), GEN_HANDLER_E(lbarx, 0x1F, 0x14, 0x01, 0, PPC_NONE, PPC2_ATOMIC_ISA206), GEN_HANDLER_E(lharx, 0x1F, 0x14, 0x03, 0, PPC_NONE, PPC2_ATOMIC_ISA206), @@ -6516,7 +6422,6 @@ GEN_HANDLER_E(lqarx, 0x1F, 0x14, 0x08, 0, PPC_NONE, PPC2_LSQ_ISA207), GEN_HANDLER2(stdcx_, "stdcx.", 0x1F, 0x16, 0x06, 0x00000000, PPC_64B), GEN_HANDLER_E(stqcx_, 0x1F, 0x16, 0x05, 0, PPC_NONE, PPC2_LSQ_ISA207), #endif -GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x039FF801, PPC_MEM_SYNC), /* ISA v3.0 changed the extended opcode from 62 to 30 */ GEN_HANDLER(wait, 0x1F, 0x1E, 0x01, 0x039FF801, PPC_WAIT), GEN_HANDLER_E(wait, 0x1F, 0x1E, 0x00, 0x039CF801, PPC_NONE, PPC2_ISA300), @@ -6639,9 +6544,6 @@ GEN_HANDLER2_E(tlbilx_booke206, "tlbilx", 0x1F, 0x12, 0x00, 0x03800001, GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE), GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE), GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC), -GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, - PPC_BOOKE, PPC2_BOOKE206), -GEN_HANDLER(msync_4xx, 0x1F, 0x16, 0x12, 0x039FF801, PPC_BOOKE), GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, diff --git a/target/ppc/translate/misc-impl.c.inc b/target/ppc/translate/misc-impl.c.inc new file mode 100644 index 0000000000..cb1a2b707e --- /dev/null +++ b/target/ppc/translate/misc-impl.c.inc @@ -0,0 +1,130 @@ +/* + * Power ISA decode for misc instructions + * + * Copyright (c) 2024, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +/* + * Memory Barrier Instructions + */ + +static bool trans_SYNC(DisasContext *ctx, arg_X_sync *a) +{ + TCGBar bar = TCG_MO_ALL; + uint32_t l = a->l; + + /* + * BookE uses the msync mnemonic. This means hwsync, except in the + * 440, where it an execution serialisation point that requires all + * previous storage accesses to have been performed to memory (which + * doesn't matter for TCG). + */ + if (!(ctx->insns_flags & PPC_MEM_SYNC)) { + if (ctx->insns_flags & PPC_BOOKE) { + /* msync replaces sync on 440, interpreted as nop */ + /* XXX: this also catches e200 */ + return true; + } + + return false; + } + + if ((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) { + bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; + } + + /* + * We may need to check for a pending TLB flush. + * + * We do this on ptesync (l == 2) on ppc64 and any sync on ppc32. + * + * Additionally, this can only happen in kernel mode however so + * check MSR_PR as well. + */ + if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { + gen_check_tlb_flush(ctx, true); + } + + tcg_gen_mb(bar | TCG_BAR_SC); + + return true; +} + +static bool trans_EIEIO(DisasContext *ctx, arg_EIEIO *a) +{ + TCGBar bar = TCG_MO_ALL; + + /* + * BookE uses the mbar instruction instead of eieio, which is basically + * full hwsync memory barrier, but is not execution synchronising. For + * the purpose of TCG the distinction is not relevant. + */ + if (!(ctx->insns_flags & PPC_MEM_EIEIO)) { + if ((ctx->insns_flags & PPC_BOOKE) || + (ctx->insns_flags2 & PPC2_BOOKE206)) { + return true; + } + return false; + } + + /* + * eieio has complex semanitcs. It provides memory ordering between + * operations in the set: + * - loads from CI memory. + * - stores to CI memory. + * - stores to WT memory. + * + * It separately also orders memory for operations in the set: + * - stores to cacheble memory. + * + * It also serializes instructions: + * - dcbt and dcbst. + * + * It separately serializes: + * - tlbie and tlbsync. + * + * And separately serializes: + * - slbieg, slbiag, and slbsync. + * + * The end result is that CI memory ordering requires TCG_MO_ALL + * and it is not possible to special-case more relaxed ordering for + * cacheable accesses. TCG_BAR_SC is required to provide this + * serialization. + */ + + /* + * POWER9 has a eieio instruction variant using bit 6 as a hint to + * tell the CPU it is a store-forwarding barrier. + */ + if (ctx->opcode & 0x2000000) { + /* + * ISA says that "Reserved fields in instructions are ignored + * by the processor". So ignore the bit 6 on non-POWER9 CPU but + * as this is not an instruction software should be using, + * complain to the user. + */ + if (!(ctx->insns_flags2 & PPC2_ISA300)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid eieio using bit 6 at @" + TARGET_FMT_lx "\n", ctx->cia); + } else { + bar = TCG_MO_ST_LD; + } + } + + tcg_gen_mb(bar | TCG_BAR_SC); + + return true; +} From ab4f174baee4b28b454fc94a7de2978c13a423ac Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 1 May 2024 23:04:33 +1000 Subject: [PATCH 07/72] target/ppc: Fix embedded memory barriers Memory barriers are supposed to do something on BookE systems, these were probably just missed during MTTCG enablement, maybe no targets support SMP. Either way, add proper BookE implementations. Reviewed-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/translate/misc-impl.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/ppc/translate/misc-impl.c.inc b/target/ppc/translate/misc-impl.c.inc index cb1a2b707e..7574317600 100644 --- a/target/ppc/translate/misc-impl.c.inc +++ b/target/ppc/translate/misc-impl.c.inc @@ -34,8 +34,7 @@ static bool trans_SYNC(DisasContext *ctx, arg_X_sync *a) */ if (!(ctx->insns_flags & PPC_MEM_SYNC)) { if (ctx->insns_flags & PPC_BOOKE) { - /* msync replaces sync on 440, interpreted as nop */ - /* XXX: this also catches e200 */ + tcg_gen_mb(bar | TCG_BAR_SC); return true; } @@ -75,6 +74,7 @@ static bool trans_EIEIO(DisasContext *ctx, arg_EIEIO *a) if (!(ctx->insns_flags & PPC_MEM_EIEIO)) { if ((ctx->insns_flags & PPC_BOOKE) || (ctx->insns_flags2 & PPC2_BOOKE206)) { + tcg_gen_mb(bar | TCG_BAR_SC); return true; } return false; From b3cfa2dd2b4dc517b9423bf568a358ac5fdd2752 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 1 May 2024 23:04:34 +1000 Subject: [PATCH 08/72] target/ppc: Add ISA v3.1 variants of sync instruction POWER10 adds a new field to sync for store-store syncs, and some new variants of the existing syncs that include persistent memory. Implement the store-store syncs and plwsync/phwsync. Reviewed-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 6 ++-- target/ppc/translate/misc-impl.c.inc | 41 ++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 6b89804b15..a180380750 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1001,7 +1001,7 @@ MSGSYNC 011111 ----- ----- ----- 1101110110 - # Memory Barrier Instructions -&X_sync l -@X_sync ...... ... l:2 ..... ..... .......... . &X_sync -SYNC 011111 --- .. ----- ----- 1001010110 - @X_sync +&X_sync l sc +@X_sync ...... .. l:3 ... sc:2 ..... .......... . &X_sync +SYNC 011111 -- ... --- .. ----- 1001010110 - @X_sync EIEIO 011111 ----- ----- ----- 1101010110 - diff --git a/target/ppc/translate/misc-impl.c.inc b/target/ppc/translate/misc-impl.c.inc index 7574317600..c1661d2f43 100644 --- a/target/ppc/translate/misc-impl.c.inc +++ b/target/ppc/translate/misc-impl.c.inc @@ -25,6 +25,7 @@ static bool trans_SYNC(DisasContext *ctx, arg_X_sync *a) { TCGBar bar = TCG_MO_ALL; uint32_t l = a->l; + uint32_t sc = a->sc; /* * BookE uses the msync mnemonic. This means hwsync, except in the @@ -41,20 +42,36 @@ static bool trans_SYNC(DisasContext *ctx, arg_X_sync *a) return false; } - if ((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) { - bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; + /* + * In ISA v3.1, the L field grew one bit. Mask that out to ignore it in + * older processors. It also added the SC field, zero this to ignore + * it too. + */ + if (!(ctx->insns_flags2 & PPC2_ISA310)) { + l &= 0x3; + sc = 0; } - /* - * We may need to check for a pending TLB flush. - * - * We do this on ptesync (l == 2) on ppc64 and any sync on ppc32. - * - * Additionally, this can only happen in kernel mode however so - * check MSR_PR as well. - */ - if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { - gen_check_tlb_flush(ctx, true); + if (sc) { + /* Store syncs [stsync, stcisync, stncisync]. These ignore L. */ + bar = TCG_MO_ST_ST; + } else { + if (((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) || (l == 5)) { + /* lwsync, or plwsync on POWER10 and later */ + bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; + } + + /* + * We may need to check for a pending TLB flush. + * + * We do this on ptesync (l == 2) on ppc64 and any sync on ppc32. + * + * Additionally, this can only happen in kernel mode however so + * check MSR_PR as well. + */ + if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { + gen_check_tlb_flush(ctx, true); + } } tcg_gen_mb(bar | TCG_BAR_SC); From 5747926fec6c65b60f9f7aca6d4df525c79eae8e Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Fri, 15 Mar 2024 12:14:21 +0530 Subject: [PATCH 09/72] target/ppc: Merge various fpu helpers This patch merges the definitions of the following set of fpu helper methods, which are similar, using macros : 1. f{add, sub, mul, div}(s) 2. fre(s) 3. frsqrte(s) Reviewed-by: Nicholas Piggin Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/fpu_helper.c | 221 +++++++++++----------------------------- 1 file changed, 62 insertions(+), 159 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 4b3dcad5d1..8d0cbe27e7 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -490,54 +490,12 @@ static void float_invalid_op_addsub(CPUPPCState *env, int flags, } } -/* fadd - fadd. */ -float64 helper_fadd(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void addsub_flags_handler(CPUPPCState *env, int flags, + uintptr_t ra) { - float64 ret = float64_add(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); + float_invalid_op_addsub(env, flags, 1, ra); } - - return ret; -} - -/* fadds - fadds. */ -float64 helper_fadds(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_add(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); - } - return ret; -} - -/* fsub - fsub. */ -float64 helper_fsub(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64_sub(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); - } - - return ret; -} - -/* fsubs - fsubs. */ -float64 helper_fsubs(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_sub(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); - } - return ret; } static void float_invalid_op_mul(CPUPPCState *env, int flags, @@ -550,29 +508,11 @@ static void float_invalid_op_mul(CPUPPCState *env, int flags, } } -/* fmul - fmul. */ -float64 helper_fmul(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void mul_flags_handler(CPUPPCState *env, int flags, uintptr_t ra) { - float64 ret = float64_mul(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_mul(env, flags, 1, GETPC()); + float_invalid_op_mul(env, flags, 1, ra); } - - return ret; -} - -/* fmuls - fmuls. */ -float64 helper_fmuls(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_mul(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_mul(env, flags, 1, GETPC()); - } - return ret; } static void float_invalid_op_div(CPUPPCState *env, int flags, @@ -587,36 +527,14 @@ static void float_invalid_op_div(CPUPPCState *env, int flags, } } -/* fdiv - fdiv. */ -float64 helper_fdiv(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void div_flags_handler(CPUPPCState *env, int flags, uintptr_t ra) { - float64 ret = float64_div(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_div(env, flags, 1, GETPC()); + float_invalid_op_div(env, flags, 1, ra); } if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); + float_zero_divide_excp(env, ra); } - - return ret; -} - -/* fdivs - fdivs. */ -float64 helper_fdivs(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_div(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_div(env, flags, 1, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); - } - - return ret; } static uint64_t float_invalid_cvt(CPUPPCState *env, int flags, @@ -812,81 +730,66 @@ float64 helper_##name(CPUPPCState *env, float64 arg) \ FPU_FSQRT(FSQRT, float64_sqrt) FPU_FSQRT(FSQRTS, float64r32_sqrt) -/* fre - fre. */ -float64 helper_fre(CPUPPCState *env, float64 arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 ret = float64_div(float64_one, arg, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid_snan)) { - float_invalid_op_vxsnan(env, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); - /* For FPSCR.ZE == 0, the result is 1/2. */ - ret = float64_set_sign(float64_half, float64_is_neg(arg)); - } - - return ret; +#define FPU_FRE(name, op) \ +float64 helper_##name(CPUPPCState *env, float64 arg) \ +{ \ + /* "Estimate" the reciprocal with actual division. */ \ + float64 ret = op(float64_one, arg, &env->fp_status); \ + int flags = get_float_exception_flags(&env->fp_status); \ + \ + if (unlikely(flags & float_flag_invalid_snan)) { \ + float_invalid_op_vxsnan(env, GETPC()); \ + } \ + if (unlikely(flags & float_flag_divbyzero)) { \ + float_zero_divide_excp(env, GETPC()); \ + /* For FPSCR.ZE == 0, the result is 1/2. */ \ + ret = float64_set_sign(float64_half, float64_is_neg(arg)); \ + } \ + \ + return ret; \ } -/* fres - fres. */ -uint64_t helper_fres(CPUPPCState *env, uint64_t arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 ret = float64r32_div(float64_one, arg, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid_snan)) { - float_invalid_op_vxsnan(env, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); - /* For FPSCR.ZE == 0, the result is 1/2. */ - ret = float64_set_sign(float64_half, float64_is_neg(arg)); - } - - return ret; +#define FPU_FRSQRTE(name, op) \ +float64 helper_##name(CPUPPCState *env, float64 arg) \ +{ \ + /* "Estimate" the reciprocal with actual division. */ \ + float64 rets = float64_sqrt(arg, &env->fp_status); \ + float64 retd = op(float64_one, rets, &env->fp_status); \ + int flags = get_float_exception_flags(&env->fp_status); \ + \ + if (unlikely(flags & float_flag_invalid)) { \ + float_invalid_op_sqrt(env, flags, 1, GETPC()); \ + } \ + if (unlikely(flags & float_flag_divbyzero)) { \ + /* Reciprocal of (square root of) zero. */ \ + float_zero_divide_excp(env, GETPC()); \ + } \ + \ + return retd; \ } -/* frsqrte - frsqrte. */ -float64 helper_frsqrte(CPUPPCState *env, float64 arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 rets = float64_sqrt(arg, &env->fp_status); - float64 retd = float64_div(float64_one, rets, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_sqrt(env, flags, 1, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - /* Reciprocal of (square root of) zero. */ - float_zero_divide_excp(env, GETPC()); - } - - return retd; +#define FPU_HELPER(name, op, flags_handler) \ +float64 helper_##name(CPUPPCState *env, float64 arg1, float64 arg2) \ +{ \ + float64 ret = op(arg1, arg2, &env->fp_status); \ + int flags = get_float_exception_flags(&env->fp_status); \ + uintptr_t ra = GETPC(); \ + flags_handler(env, flags, ra); \ + return ret; \ } -/* frsqrtes - frsqrtes. */ -float64 helper_frsqrtes(CPUPPCState *env, float64 arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 rets = float64_sqrt(arg, &env->fp_status); - float64 retd = float64r32_div(float64_one, rets, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_sqrt(env, flags, 1, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - /* Reciprocal of (square root of) zero. */ - float_zero_divide_excp(env, GETPC()); - } - - return retd; -} +FPU_FRE(fre, float64_div) +FPU_FRE(fres, float64r32_div) +FPU_FRSQRTE(frsqrte, float64_div) +FPU_FRSQRTE(frsqrtes, float64r32_div) +FPU_HELPER(fadd, float64_add, addsub_flags_handler) +FPU_HELPER(fadds, float64r32_add, addsub_flags_handler) +FPU_HELPER(fsub, float64_sub, addsub_flags_handler) +FPU_HELPER(fsubs, float64r32_sub, addsub_flags_handler) +FPU_HELPER(fmul, float64_mul, mul_flags_handler) +FPU_HELPER(fmuls, float64r32_mul, mul_flags_handler) +FPU_HELPER(fdiv, float64_div, div_flags_handler) +FPU_HELPER(fdivs, float64r32_div, div_flags_handler) /* fsel - fsel. */ uint64_t helper_FSEL(uint64_t a, uint64_t b, uint64_t c) From 177fcc06dc579749ec4515174b62ba4cdb775474 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Fri, 15 Mar 2024 12:14:22 +0530 Subject: [PATCH 10/72] target/ppc: Move floating-point arithmetic instructions to decodetree. This patch moves the below instructions to decodetree specification : f{add, sub, mul, div, re, rsqrte, madd, msub, nmadd, nmsub}[s][.] : A-form ft{div, sqrt} : X-form With this patch, all the floating-point arithmetic instructions have been moved to decodetree. The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Nicholas Piggin Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/fpu_helper.c | 38 ++-- target/ppc/helper.h | 44 ++--- target/ppc/insn32.decode | 42 +++++ target/ppc/translate/fp-impl.c.inc | 285 +++++++++++------------------ target/ppc/translate/fp-ops.c.inc | 31 ---- 5 files changed, 192 insertions(+), 248 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 8d0cbe27e7..51bce99fd5 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -673,7 +673,7 @@ static uint64_t do_fmadds(CPUPPCState *env, float64 a, float64 b, uint64_t helper_##op(CPUPPCState *env, uint64_t arg1, \ uint64_t arg2, uint64_t arg3) \ { return do_fmadd(env, arg1, arg2, arg3, madd_flags, GETPC()); } \ - uint64_t helper_##op##s(CPUPPCState *env, uint64_t arg1, \ + uint64_t helper_##op##S(CPUPPCState *env, uint64_t arg1, \ uint64_t arg2, uint64_t arg3) \ { return do_fmadds(env, arg1, arg2, arg3, madd_flags, GETPC()); } @@ -682,10 +682,10 @@ static uint64_t do_fmadds(CPUPPCState *env, float64 a, float64 b, #define NMADD_FLGS float_muladd_negate_result #define NMSUB_FLGS (float_muladd_negate_c | float_muladd_negate_result) -FPU_FMADD(fmadd, MADD_FLGS) -FPU_FMADD(fnmadd, NMADD_FLGS) -FPU_FMADD(fmsub, MSUB_FLGS) -FPU_FMADD(fnmsub, NMSUB_FLGS) +FPU_FMADD(FMADD, MADD_FLGS) +FPU_FMADD(FNMADD, NMADD_FLGS) +FPU_FMADD(FMSUB, MSUB_FLGS) +FPU_FMADD(FNMSUB, NMSUB_FLGS) /* frsp - frsp. */ static uint64_t do_frsp(CPUPPCState *env, uint64_t arg, uintptr_t retaddr) @@ -778,18 +778,18 @@ float64 helper_##name(CPUPPCState *env, float64 arg1, float64 arg2) \ return ret; \ } -FPU_FRE(fre, float64_div) -FPU_FRE(fres, float64r32_div) -FPU_FRSQRTE(frsqrte, float64_div) -FPU_FRSQRTE(frsqrtes, float64r32_div) -FPU_HELPER(fadd, float64_add, addsub_flags_handler) -FPU_HELPER(fadds, float64r32_add, addsub_flags_handler) -FPU_HELPER(fsub, float64_sub, addsub_flags_handler) -FPU_HELPER(fsubs, float64r32_sub, addsub_flags_handler) -FPU_HELPER(fmul, float64_mul, mul_flags_handler) -FPU_HELPER(fmuls, float64r32_mul, mul_flags_handler) -FPU_HELPER(fdiv, float64_div, div_flags_handler) -FPU_HELPER(fdivs, float64r32_div, div_flags_handler) +FPU_FRE(FRE, float64_div) +FPU_FRE(FRES, float64r32_div) +FPU_FRSQRTE(FRSQRTE, float64_div) +FPU_FRSQRTE(FRSQRTES, float64r32_div) +FPU_HELPER(FADD, float64_add, addsub_flags_handler) +FPU_HELPER(FADDS, float64r32_add, addsub_flags_handler) +FPU_HELPER(FSUB, float64_sub, addsub_flags_handler) +FPU_HELPER(FSUBS, float64r32_sub, addsub_flags_handler) +FPU_HELPER(FMUL, float64_mul, mul_flags_handler) +FPU_HELPER(FMULS, float64r32_mul, mul_flags_handler) +FPU_HELPER(FDIV, float64_div, div_flags_handler) +FPU_HELPER(FDIVS, float64r32_div, div_flags_handler) /* fsel - fsel. */ uint64_t helper_FSEL(uint64_t a, uint64_t b, uint64_t c) @@ -806,7 +806,7 @@ uint64_t helper_FSEL(uint64_t a, uint64_t b, uint64_t c) } } -uint32_t helper_ftdiv(uint64_t fra, uint64_t frb) +uint32_t helper_FTDIV(uint64_t fra, uint64_t frb) { int fe_flag = 0; int fg_flag = 0; @@ -842,7 +842,7 @@ uint32_t helper_ftdiv(uint64_t fra, uint64_t frb) return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); } -uint32_t helper_ftsqrt(uint64_t frb) +uint32_t helper_FTSQRT(uint64_t frb) { int fe_flag = 0; int fg_flag = 0; diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 86f97ee1e7..f177d5b906 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -110,32 +110,32 @@ DEF_HELPER_2(friz, i64, env, i64) DEF_HELPER_2(frip, i64, env, i64) DEF_HELPER_2(frim, i64, env, i64) -DEF_HELPER_3(fadd, f64, env, f64, f64) -DEF_HELPER_3(fadds, f64, env, f64, f64) -DEF_HELPER_3(fsub, f64, env, f64, f64) -DEF_HELPER_3(fsubs, f64, env, f64, f64) -DEF_HELPER_3(fmul, f64, env, f64, f64) -DEF_HELPER_3(fmuls, f64, env, f64, f64) -DEF_HELPER_3(fdiv, f64, env, f64, f64) -DEF_HELPER_3(fdivs, f64, env, f64, f64) -DEF_HELPER_4(fmadd, i64, env, i64, i64, i64) -DEF_HELPER_4(fmsub, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64) -DEF_HELPER_4(fmadds, i64, env, i64, i64, i64) -DEF_HELPER_4(fmsubs, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmadds, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmsubs, i64, env, i64, i64, i64) +DEF_HELPER_3(FADD, f64, env, f64, f64) +DEF_HELPER_3(FADDS, f64, env, f64, f64) +DEF_HELPER_3(FSUB, f64, env, f64, f64) +DEF_HELPER_3(FSUBS, f64, env, f64, f64) +DEF_HELPER_3(FMUL, f64, env, f64, f64) +DEF_HELPER_3(FMULS, f64, env, f64, f64) +DEF_HELPER_3(FDIV, f64, env, f64, f64) +DEF_HELPER_3(FDIVS, f64, env, f64, f64) +DEF_HELPER_4(FMADD, i64, env, i64, i64, i64) +DEF_HELPER_4(FMSUB, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMADD, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMSUB, i64, env, i64, i64, i64) +DEF_HELPER_4(FMADDS, i64, env, i64, i64, i64) +DEF_HELPER_4(FMSUBS, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMADDS, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMSUBS, i64, env, i64, i64, i64) DEF_HELPER_2(FSQRT, f64, env, f64) DEF_HELPER_2(FSQRTS, f64, env, f64) -DEF_HELPER_2(fre, i64, env, i64) -DEF_HELPER_2(fres, i64, env, i64) -DEF_HELPER_2(frsqrte, i64, env, i64) -DEF_HELPER_2(frsqrtes, i64, env, i64) +DEF_HELPER_2(FRE, i64, env, i64) +DEF_HELPER_2(FRES, i64, env, i64) +DEF_HELPER_2(FRSQRTE, i64, env, i64) +DEF_HELPER_2(FRSQRTES, i64, env, i64) DEF_HELPER_FLAGS_3(FSEL, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) -DEF_HELPER_FLAGS_2(ftdiv, TCG_CALL_NO_RWG_SE, i32, i64, i64) -DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64) +DEF_HELPER_FLAGS_2(FTDIV, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_1(FTSQRT, TCG_CALL_NO_RWG_SE, i32, i64) #define dh_alias_avr ptr #define dh_ctype_avr ppc_avr_t * diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index a180380750..e9d6595168 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -20,6 +20,12 @@ &A frt fra frb frc rc:bool @A ...... frt:5 fra:5 frb:5 frc:5 ..... rc:1 &A +&A_tab frt fra frb rc:bool +@A_tab ...... frt:5 fra:5 frb:5 ..... ..... rc:1 &A_tab + +&A_tac frt fra frc rc:bool +@A_tac ...... frt:5 fra:5 ..... frc:5 ..... rc:1 &A_tac + &A_tb frt frb rc:bool @A_tb ...... frt:5 ..... frb:5 ..... ..... rc:1 &A_tb @@ -124,6 +130,9 @@ &X_bf bf ra rb @X_bf ...... bf:3 .. ra:5 rb:5 .......... . &X_bf +&X_bf_b bf rb +@X_bf_b ...... bf:3 .. ..... rb:5 .......... . &X_bf_b + @X_bf_ap_bp ...... bf:3 .. ....0 ....0 .......... . &X_bf ra=%x_frap rb=%x_frbp @X_bf_a_bp ...... bf:3 .. ra:5 ....0 .......... . &X_bf rb=%x_frbp @@ -400,9 +409,42 @@ STFDUX 011111 ..... ...... .... 1011110111 - @X ### Floating-Point Arithmetic Instructions +FADD 111111 ..... ..... ..... ----- 10101 . @A_tab +FADDS 111011 ..... ..... ..... ----- 10101 . @A_tab + +FSUB 111111 ..... ..... ..... ----- 10100 . @A_tab +FSUBS 111011 ..... ..... ..... ----- 10100 . @A_tab + +FMUL 111111 ..... ..... ----- ..... 11001 . @A_tac +FMULS 111011 ..... ..... ----- ..... 11001 . @A_tac + +FDIV 111111 ..... ..... ..... ----- 10010 . @A_tab +FDIVS 111011 ..... ..... ..... ----- 10010 . @A_tab + FSQRT 111111 ..... ----- ..... ----- 10110 . @A_tb FSQRTS 111011 ..... ----- ..... ----- 10110 . @A_tb +FRE 111111 ..... ----- ..... ----- 11000 . @A_tb +FRES 111011 ..... ----- ..... ----- 11000 . @A_tb + +FRSQRTE 111111 ..... ----- ..... ----- 11010 . @A_tb +FRSQRTES 111011 ..... ----- ..... ----- 11010 . @A_tb + +FTDIV 111111 ... -- ..... ..... 0010000000 - @X_bf +FTSQRT 111111 ... -- ----- ..... 0010100000 - @X_bf_b + +FMADD 111111 ..... ..... ..... ..... 11101 . @A +FMADDS 111011 ..... ..... ..... ..... 11101 . @A + +FMSUB 111111 ..... ..... ..... ..... 11100 . @A +FMSUBS 111011 ..... ..... ..... ..... 11100 . @A + +FNMADD 111111 ..... ..... ..... ..... 11111 . @A +FNMADDS 111011 ..... ..... ..... ..... 11111 . @A + +FNMSUB 111111 ..... ..... ..... ..... 11110 . @A +FNMSUBS 111011 ..... ..... ..... ..... 11110 . @A + ### Floating-Point Select Instruction FSEL 111111 ..... ..... ..... ..... 10111 . @A diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index 189cd8c979..a66b83398b 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -30,96 +30,73 @@ static void gen_set_cr1_from_fpscr(DisasContext *ctx) #endif /*** Floating-Point arithmetic ***/ -#define _GEN_FLOAT_ACB(name, op1, op2, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - TCGv_i64 t3; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - t3 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rA(ctx->opcode)); \ - get_fpr(t1, rC(ctx->opcode)); \ - get_fpr(t2, rB(ctx->opcode)); \ - gen_helper_f##name(t3, tcg_env, t0, t1, t2); \ - set_fpr(rD(ctx->opcode), t3); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t3); \ - } \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ +static bool do_helper_acb(DisasContext *ctx, arg_A *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64, + TCGv_i64, TCGv_i64)) +{ + TCGv_i64 t0, t1, t2, t3; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + t3 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->fra); + get_fpr(t1, a->frc); + get_fpr(t2, a->frb); + helper(t3, tcg_env, t0, t1, t2); + set_fpr(a->frt, t3); + gen_compute_fprf_float64(t3); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } -#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ -_GEN_FLOAT_ACB(name, 0x3F, op2, set_fprf, type); \ -_GEN_FLOAT_ACB(name##s, 0x3B, op2, set_fprf, type); - -#define _GEN_FLOAT_AB(name, op1, op2, inval, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rA(ctx->opcode)); \ - get_fpr(t1, rB(ctx->opcode)); \ - gen_helper_f##name(t2, tcg_env, t0, t1); \ - set_fpr(rD(ctx->opcode), t2); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t2); \ - } \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ +static bool do_helper_ab(DisasContext *ctx, arg_A_tab *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64, + TCGv_i64)) +{ + TCGv_i64 t0, t1, t2; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->fra); + get_fpr(t1, a->frb); + helper(t2, tcg_env, t0, t1); + set_fpr(a->frt, t2); + gen_compute_fprf_float64(t2); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } -#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AB(name, 0x3F, op2, inval, set_fprf, type); \ -_GEN_FLOAT_AB(name##s, 0x3B, op2, inval, set_fprf, type); -#define _GEN_FLOAT_AC(name, op1, op2, inval, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rA(ctx->opcode)); \ - get_fpr(t1, rC(ctx->opcode)); \ - gen_helper_f##name(t2, tcg_env, t0, t1); \ - set_fpr(rD(ctx->opcode), t2); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t2); \ - } \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ +static bool do_helper_ac(DisasContext *ctx, arg_A_tac *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64, + TCGv_i64)) +{ + TCGv_i64 t0, t1, t2; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->fra); + get_fpr(t1, a->frc); + helper(t2, tcg_env, t0, t1); + set_fpr(a->frt, t2); + gen_compute_fprf_float64(t2); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } -#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AC(name, 0x3F, op2, inval, set_fprf, type); \ -_GEN_FLOAT_AC(name##s, 0x3B, op2, inval, set_fprf, type); #define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ static void gen_f##name(DisasContext *ctx) \ @@ -145,64 +122,22 @@ static void gen_f##name(DisasContext *ctx) \ } \ } -#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rB(ctx->opcode)); \ - gen_helper_f##name(t1, tcg_env, t0); \ - set_fpr(rD(ctx->opcode), t1); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t1); \ - } \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_cr1_from_fpscr(ctx); \ - } \ -} - -/* fadd - fadds */ -GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT); -/* fdiv - fdivs */ -GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT); -/* fmul - fmuls */ -GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT); - -/* fre */ -GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT); - -/* fres */ -GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES); - -/* frsqrte */ -GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE); - -/* frsqrtes */ -static void gen_frsqrtes(DisasContext *ctx) +static bool do_helper_bs(DisasContext *ctx, arg_A_tb *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64)) { - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1; + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); - get_fpr(t0, rB(ctx->opcode)); - gen_helper_frsqrtes(t1, tcg_env, t0); - set_fpr(rD(ctx->opcode), t1); + get_fpr(t0, a->frb); + helper(t1, tcg_env, t0); + set_fpr(a->frt, t1); gen_compute_fprf_float64(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { + if (unlikely(a->rc)) { gen_set_cr1_from_fpscr(ctx); } + return true; } static bool trans_FSEL(DisasContext *ctx, arg_A *a) @@ -228,10 +163,6 @@ static bool trans_FSEL(DisasContext *ctx, arg_A *a) return true; } -/* fsub - fsubs */ -GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT); -/* Optional: */ - static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a, void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64)) { @@ -254,19 +185,33 @@ static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a, return true; } +TRANS(FADD, do_helper_ab, gen_helper_FADD); +TRANS(FADDS, do_helper_ab, gen_helper_FADDS); +TRANS(FSUB, do_helper_ab, gen_helper_FSUB); +TRANS(FSUBS, do_helper_ab, gen_helper_FSUBS); +TRANS(FDIV, do_helper_ab, gen_helper_FDIV); +TRANS(FDIVS, do_helper_ab, gen_helper_FDIVS); +TRANS(FMUL, do_helper_ac, gen_helper_FMUL); +TRANS(FMULS, do_helper_ac, gen_helper_FMULS); + +TRANS(FMADD, do_helper_acb, gen_helper_FMADD); +TRANS(FMADDS, do_helper_acb, gen_helper_FMADDS); +TRANS(FMSUB, do_helper_acb, gen_helper_FMSUB); +TRANS(FMSUBS, do_helper_acb, gen_helper_FMSUBS); + +TRANS(FNMADD, do_helper_acb, gen_helper_FNMADD); +TRANS(FNMADDS, do_helper_acb, gen_helper_FNMADDS); +TRANS(FNMSUB, do_helper_acb, gen_helper_FNMSUB); +TRANS(FNMSUBS, do_helper_acb, gen_helper_FNMSUBS); + +TRANS_FLAGS(FLOAT_EXT, FRE, do_helper_bs, gen_helper_FRE); +TRANS_FLAGS(FLOAT_FRES, FRES, do_helper_bs, gen_helper_FRES); +TRANS_FLAGS(FLOAT_FRSQRTE, FRSQRTE, do_helper_bs, gen_helper_FRSQRTE); +TRANS_FLAGS(FLOAT_FRSQRTES, FRSQRTES, do_helper_bs, gen_helper_FRSQRTES); + TRANS(FSQRT, do_helper_fsqrt, gen_helper_FSQRT); TRANS(FSQRTS, do_helper_fsqrt, gen_helper_FSQRTS); -/*** Floating-Point multiply-and-add ***/ -/* fmadd - fmadds */ -GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT); -/* fmsub - fmsubs */ -GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT); -/* fnmadd - fnmadds */ -GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT); -/* fnmsub - fnmsubs */ -GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT); - /*** Floating-Point round & convert ***/ /* fctiw */ GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT); @@ -304,35 +249,30 @@ GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT); /* frim */ GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT); -static void gen_ftdiv(DisasContext *ctx) +static bool trans_FTDIV(DisasContext *ctx, arg_X_bf *a) { - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1; + REQUIRE_INSNS_FLAGS2(ctx, FP_TST_ISA206); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); - get_fpr(t0, rA(ctx->opcode)); - get_fpr(t1, rB(ctx->opcode)); - gen_helper_ftdiv(cpu_crf[crfD(ctx->opcode)], t0, t1); + get_fpr(t0, a->ra); + get_fpr(t1, a->rb); + gen_helper_FTDIV(cpu_crf[a->bf], t0, t1); + return true; } -static void gen_ftsqrt(DisasContext *ctx) +static bool trans_FTSQRT(DisasContext *ctx, arg_X_bf_b *a) { TCGv_i64 t0; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + REQUIRE_INSNS_FLAGS2(ctx, FP_TST_ISA206); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); - get_fpr(t0, rB(ctx->opcode)); - gen_helper_ftsqrt(cpu_crf[crfD(ctx->opcode)], t0); + get_fpr(t0, a->rb); + gen_helper_FTSQRT(cpu_crf[a->bf], t0); + return true; } - - /*** Floating-Point compare ***/ /* fcmpo */ @@ -1111,14 +1051,7 @@ TRANS(STFDX, do_lsfp_X, false, true, false) TRANS(STFDUX, do_lsfp_X, true, true, false) TRANS(PSTFD, do_lsfp_PLS_D, false, true, false) -#undef _GEN_FLOAT_ACB -#undef GEN_FLOAT_ACB -#undef _GEN_FLOAT_AB -#undef GEN_FLOAT_AB -#undef _GEN_FLOAT_AC -#undef GEN_FLOAT_AC #undef GEN_FLOAT_B -#undef GEN_FLOAT_BS #undef GEN_LDF #undef GEN_LDUF diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index d4c6c4bed1..cef4b5dfcb 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -1,36 +1,6 @@ -#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, 0x00000000, type) -#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ -_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type), \ -_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type) -#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) -#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type), \ -_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type) -#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) -#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type), \ -_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type) #define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, type) -#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, 0x001F07C0, type) -GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT), -GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT), -GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT), -GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT), -GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES), -GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE), -GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT), -GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT), -GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT), -GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT), -GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT), -GEN_HANDLER_E(ftdiv, 0x3F, 0x00, 0x04, 1, PPC_NONE, PPC2_FP_TST_ISA206), -GEN_HANDLER_E(ftsqrt, 0x3F, 0x00, 0x05, 1, PPC_NONE, PPC2_FP_TST_ISA206), GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT), GEN_HANDLER_E(fctiwu, 0x3F, 0x0E, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT), @@ -61,7 +31,6 @@ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) GEN_HANDLER_E(stfdepx, 0x1F, 0x1F, 0x16, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), -GEN_HANDLER(frsqrtes, 0x3B, 0x1A, 0xFF, 0x001F07C0, PPC_FLOAT_FRSQRTES), GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT), GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT), GEN_HANDLER(fabs, 0x3F, 0x08, 0x08, 0x001F0000, PPC_FLOAT), From a1faff873ab1b808126a110aa6b3bc6050baa0f1 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:27 +0530 Subject: [PATCH 11/72] target/ppc: Move mul{li, lw, lwo, hw, hwu} instructions to decodetree. Moving the following instructions to decodetree specification : mulli : D-form mul{lw, lwo, hw, hwu}[.] : XO-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Also cleaned up code for mullw[o][.] as per review comments while keeping the logic of the tcg ops generated semantically same. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 9 +++ target/ppc/translate.c | 89 ---------------------- target/ppc/translate/fixedpoint-impl.c.inc | 66 ++++++++++++++++ 3 files changed, 75 insertions(+), 89 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index e9d6595168..bb0f1dbd75 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -202,6 +202,9 @@ &XO_ta rt ra oe:bool rc:bool @XO_ta ...... rt:5 ra:5 ..... oe:1 ......... rc:1 &XO_ta +&XO_tab_rc rt ra rb rc:bool +@XO_tab_rc ...... rt:5 ra:5 rb:5 . ......... rc:1 &XO_tab_rc + %xx_xt 0:1 21:5 %xx_xb 1:1 11:5 %xx_xa 2:1 16:5 @@ -362,6 +365,12 @@ SUBFE 011111 ..... ..... ..... . 010001000 . @XO SUBFME 011111 ..... ..... ----- . 011101000 . @XO_ta SUBFZE 011111 ..... ..... ----- . 011001000 . @XO_ta +MULLI 000111 ..... ..... ................ @D +MULLW 011111 ..... ..... ..... 0 011101011 . @XO_tab_rc +MULLWO 011111 ..... ..... ..... 1 011101011 . @XO_tab_rc +MULHW 011111 ..... ..... ..... - 001001011 . @XO_tab_rc +MULHWU 011111 ..... ..... ..... - 000001011 . @XO_tab_rc + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/translate.c b/target/ppc/translate.c index a70c5ed951..cb10e33ceb 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1947,90 +1947,6 @@ GEN_INT_ARITH_MODD(modud, 0x08, 0); GEN_INT_ARITH_MODD(modsd, 0x18, 1); #endif -/* mulhw mulhw. */ -static void gen_mulhw(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_muls2_i32(t0, t1, t0, t1); - tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulhwu mulhwu. */ -static void gen_mulhwu(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mulu2_i32(t0, t1, t0, t1); - tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mullw mullw. */ -static void gen_mullw(DisasContext *ctx) -{ -#if defined(TARGET_PPC64) - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mul_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); -#else - tcg_gen_mul_i32(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); -#endif - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mullwo mullwo. */ -static void gen_mullwo(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i32 t1 = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_muls2_i32(t0, t1, t0, t1); -#if defined(TARGET_PPC64) - tcg_gen_concat_i32_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); -#else - tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], t0); -#endif - - tcg_gen_sari_i32(t0, t0, 31); - tcg_gen_setcond_i32(TCG_COND_NE, t0, t0, t1); - tcg_gen_extu_i32_tl(cpu_ov, t0); - if (is_isa300(ctx)) { - tcg_gen_mov_tl(cpu_ov32, cpu_ov); - } - tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); - - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulli */ -static void gen_mulli(DisasContext *ctx) -{ - tcg_gen_muli_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - SIMM(ctx->opcode)); -} - #if defined(TARGET_PPC64) /* mulhd mulhd. */ static void gen_mulhd(DisasContext *ctx) @@ -6343,11 +6259,6 @@ GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(cmprb, 0x1F, 0x00, 0x06, 0x00400001, PPC_NONE, PPC2_ISA300), GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), -GEN_HANDLER(mulhw, 0x1F, 0x0B, 0x02, 0x00000400, PPC_INTEGER), -GEN_HANDLER(mulhwu, 0x1F, 0x0B, 0x00, 0x00000400, PPC_INTEGER), -GEN_HANDLER(mullw, 0x1F, 0x0B, 0x07, 0x00000000, PPC_INTEGER), -GEN_HANDLER(mullwo, 0x1F, 0x0B, 0x17, 0x00000000, PPC_INTEGER), -GEN_HANDLER(mulli, 0x07, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), #if defined(TARGET_PPC64) GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B), #endif diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 0c66465d96..1a2ad58929 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -395,6 +395,72 @@ TRANS(SUBFE, do_subf_XO, true, true) TRANS(SUBFME, do_subf_const_XO, tcg_constant_tl(-1LL), true, true) TRANS(SUBFZE, do_subf_const_XO, tcg_constant_tl(0), true, true) +static bool trans_MULLI(DisasContext *ctx, arg_MULLI *a) +{ + tcg_gen_muli_tl(cpu_gpr[a->rt], cpu_gpr[a->ra], a->si); + return true; +} + +static bool trans_MULLW(DisasContext *ctx, arg_MULLW *a) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + tcg_gen_ext32s_tl(t0, cpu_gpr[a->ra]); + tcg_gen_ext32s_tl(t1, cpu_gpr[a->rb]); + tcg_gen_mul_tl(cpu_gpr[a->rt], t0, t1); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +static bool trans_MULLWO(DisasContext *ctx, arg_MULLWO *a) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + +#if defined(TARGET_PPC64) + tcg_gen_ext32s_i64(t0, cpu_gpr[a->ra]); + tcg_gen_ext32s_i64(t1, cpu_gpr[a->rb]); + tcg_gen_mul_i64(cpu_gpr[a->rt], t0, t1); + tcg_gen_sextract_i64(t0, cpu_gpr[a->rt], 31, 1); + tcg_gen_sari_i64(t1, cpu_gpr[a->rt], 32); +#else + tcg_gen_muls2_i32(cpu_gpr[a->rt], t1, cpu_gpr[a->ra], cpu_gpr[a->rb]); + tcg_gen_sari_i32(t0, cpu_gpr[a->rt], 31); +#endif + tcg_gen_setcond_tl(TCG_COND_NE, cpu_ov, t0, t1); + if (is_isa300(ctx)) { + tcg_gen_mov_tl(cpu_ov32, cpu_ov); + } + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +static bool do_mulhw(DisasContext *ctx, arg_XO_tab_rc *a, + void (*helper)(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, + TCGv_i32 arg2)) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[a->ra]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[a->rb]); + helper(t0, t1, t0, t1); + tcg_gen_extu_i32_tl(cpu_gpr[a->rt], t1); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +TRANS(MULHW, do_mulhw, tcg_gen_muls2_i32) +TRANS(MULHWU, do_mulhw, tcg_gen_mulu2_i32) + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From 86e6202a57b1ea44e6bd1fdb0faa0ce5aa3d4aab Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:28 +0530 Subject: [PATCH 12/72] target/ppc: Make divw[u] handler method decodetree compatible. The handler methods for divw[u] instructions internally use Rc(ctx->opcode), for extraction of Rc field of instructions, which poses a problem if we move the above said instructions to decodetree, as the ctx->opcode field is not popluated in decodetree. Hence, making it decodetree compatible, so that the mentioned insns can be safely move to decodetree specs. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/translate.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index cb10e33ceb..277d96aa94 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1737,8 +1737,9 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, } } -static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, - TCGv arg2, int sign, int compute_ov) +static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, + TCGv arg1, TCGv arg2, bool sign, + bool compute_ov, bool compute_rc0) { TCGv_i32 t0 = tcg_temp_new_i32(); TCGv_i32 t1 = tcg_temp_new_i32(); @@ -1772,7 +1773,7 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); } - if (unlikely(Rc(ctx->opcode) != 0)) { + if (unlikely(compute_rc0)) { gen_set_Rc0(ctx, ret); } } @@ -1782,7 +1783,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ { \ gen_op_arith_divw(ctx, cpu_gpr[rD(ctx->opcode)], \ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign, compute_ov); \ + sign, compute_ov, Rc(ctx->opcode)); \ } /* divwu divwu. divwuo divwuo. */ GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0); From 2871921d857d3137e160dcb57ae0b48ddc98822f Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:29 +0530 Subject: [PATCH 13/72] target/ppc: Move divw[u, e, eu] instructions to decodetree. Moving the following instructions to decodetree specification : divw[u, e, eu][o][.] : XO-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 4 +-- target/ppc/insn32.decode | 5 ++++ target/ppc/int_helper.c | 4 +-- target/ppc/translate.c | 31 ---------------------- target/ppc/translate/fixedpoint-impl.c.inc | 24 +++++++++++++++++ 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index f177d5b906..6f5c0c20eb 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -55,8 +55,8 @@ DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) DEF_HELPER_4(divdeu, i64, env, i64, i64, i32) DEF_HELPER_4(divde, i64, env, i64, i64, i32) #endif -DEF_HELPER_4(divweu, tl, env, tl, tl, i32) -DEF_HELPER_4(divwe, tl, env, tl, tl, i32) +DEF_HELPER_4(DIVWEU, tl, env, tl, tl, i32) +DEF_HELPER_4(DIVWE, tl, env, tl, tl, i32) DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(cmpb, TCG_CALL_NO_RWG_SE, tl, tl, tl) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index bb0f1dbd75..c5291504dd 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -371,6 +371,11 @@ MULLWO 011111 ..... ..... ..... 1 011101011 . @XO_tab_rc MULHW 011111 ..... ..... ..... - 001001011 . @XO_tab_rc MULHWU 011111 ..... ..... ..... - 000001011 . @XO_tab_rc +DIVW 011111 ..... ..... ..... . 111101011 . @XO +DIVWU 011111 ..... ..... ..... . 111001011 . @XO +DIVWE 011111 ..... ..... ..... . 110101011 . @XO +DIVWEU 011111 ..... ..... ..... . 110001011 . @XO + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 0a5c3e78a4..dc1f72ff38 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -44,7 +44,7 @@ static inline void helper_update_ov_legacy(CPUPPCState *env, int ov) } } -target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, +target_ulong helper_DIVWEU(CPUPPCState *env, target_ulong ra, target_ulong rb, uint32_t oe) { uint64_t rt = 0; @@ -71,7 +71,7 @@ target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, return (target_ulong)rt; } -target_ulong helper_divwe(CPUPPCState *env, target_ulong ra, target_ulong rb, +target_ulong helper_DIVWE(CPUPPCState *env, target_ulong ra, target_ulong rb, uint32_t oe) { int64_t rt = 0; diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 277d96aa94..59248ae3fb 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1777,21 +1777,6 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, gen_set_Rc0(ctx, ret); } } -/* Div functions */ -#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_divw(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign, compute_ov, Rc(ctx->opcode)); \ -} -/* divwu divwu. divwuo divwuo. */ -GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0); -GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1); -/* divw divw. divwo divwo. */ -GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0); -GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1); - /* div[wd]eu[o][.] */ #define GEN_DIVE(name, hlpr, compute_ov) \ static void gen_##name(DisasContext *ctx) \ @@ -1804,11 +1789,6 @@ static void gen_##name(DisasContext *ctx) \ } \ } -GEN_DIVE(divweu, divweu, 0); -GEN_DIVE(divweuo, divweu, 1); -GEN_DIVE(divwe, divwe, 0); -GEN_DIVE(divweo, divwe, 1); - #if defined(TARGET_PPC64) static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign, int compute_ov) @@ -6470,17 +6450,6 @@ GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE, GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), #endif -#undef GEN_INT_ARITH_DIVW -#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \ -GEN_HANDLER(name, 0x1F, 0x0B, opc3, 0x00000000, PPC_INTEGER) -GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0), -GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1), -GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0), -GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1), -GEN_HANDLER_E(divwe, 0x1F, 0x0B, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divweo, 0x1F, 0x0B, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divweu, 0x1F, 0x0B, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divweuo, 0x1F, 0x0B, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), GEN_HANDLER_E(modsw, 0x1F, 0x0B, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(moduw, 0x1F, 0x0B, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 1a2ad58929..3265c77aa6 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -461,6 +461,30 @@ static bool do_mulhw(DisasContext *ctx, arg_XO_tab_rc *a, TRANS(MULHW, do_mulhw, tcg_gen_muls2_i32) TRANS(MULHWU, do_mulhw, tcg_gen_mulu2_i32) +static bool do_divw(DisasContext *ctx, arg_XO *a, int sign) +{ + gen_op_arith_divw(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign, a->oe, a->rc); + return true; +} + +static bool do_divwe(DisasContext *ctx, arg_XO *a, + void (*helper)(TCGv, TCGv_ptr, TCGv, TCGv, TCGv_i32)) +{ + REQUIRE_INSNS_FLAGS2(ctx, DIVE_ISA206); + helper(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->ra], cpu_gpr[a->rb], + tcg_constant_i32(a->oe)); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +TRANS(DIVW, do_divw, 1); +TRANS(DIVWU, do_divw, 0); +TRANS(DIVWE, do_divwe, gen_helper_DIVWE); +TRANS(DIVWEU, do_divwe, gen_helper_DIVWEU); + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From a81b5c186730fe5a92b645c84e538444a64b93f5 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:30 +0530 Subject: [PATCH 14/72] target/ppc: Move neg, darn, mod{sw, uw} to decodetree. Moving the below instructions to decodetree specification : neg[o][.] : XO-form mod{sw, uw}, darn : X-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 4 +- target/ppc/insn32.decode | 8 ++++ target/ppc/int_helper.c | 4 +- target/ppc/translate.c | 56 ---------------------- target/ppc/translate/fixedpoint-impl.c.inc | 48 +++++++++++++++++++ 5 files changed, 60 insertions(+), 60 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 6f5c0c20eb..5a97429f1e 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -71,8 +71,8 @@ DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(srad, tl, env, tl, tl) -DEF_HELPER_FLAGS_0(darn32, TCG_CALL_NO_RWG, tl) -DEF_HELPER_FLAGS_0(darn64, TCG_CALL_NO_RWG, tl) +DEF_HELPER_FLAGS_0(DARN32, TCG_CALL_NO_RWG, tl) +DEF_HELPER_FLAGS_0(DARN64, TCG_CALL_NO_RWG, tl) #endif DEF_HELPER_FLAGS_1(cntlsw32, TCG_CALL_NO_RWG_SE, i32, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index c5291504dd..3051792a56 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -196,6 +196,9 @@ &X_a ra @X_a ...... ra:3 .. ..... ..... .......... . &X_a +&X_tl rt l +@X_tl ...... rt:5 ... l:2 ..... .......... . &X_tl + &XO rt ra rb oe:bool rc:bool @XO ...... rt:5 ra:5 rb:5 oe:1 ......... rc:1 &XO @@ -376,6 +379,11 @@ DIVWU 011111 ..... ..... ..... . 111001011 . @XO DIVWE 011111 ..... ..... ..... . 110101011 . @XO DIVWEU 011111 ..... ..... ..... . 110001011 . @XO +MODSW 011111 ..... ..... ..... 1100001011 - @X +MODUW 011111 ..... ..... ..... 0100001011 - @X +DARN 011111 ..... --- .. ----- 1011110011 - @X_tl +NEG 011111 ..... ..... ----- . 001101000 . @XO_ta + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index dc1f72ff38..bc25d5b062 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -171,7 +171,7 @@ uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb) /* * Return a random number. */ -uint64_t helper_darn32(void) +uint64_t helper_DARN32(void) { Error *err = NULL; uint32_t ret; @@ -186,7 +186,7 @@ uint64_t helper_darn32(void) return ret; } -uint64_t helper_darn64(void) +uint64_t helper_DARN64(void) { Error *err = NULL; uint64_t ret; diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 59248ae3fb..3756b0c495 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1877,17 +1877,6 @@ static inline void gen_op_arith_modw(DisasContext *ctx, TCGv ret, TCGv arg1, } } -#define GEN_INT_ARITH_MODW(name, opc3, sign) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_modw(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign); \ -} - -GEN_INT_ARITH_MODW(moduw, 0x08, 0); -GEN_INT_ARITH_MODW(modsw, 0x18, 1); - #if defined(TARGET_PPC64) static inline void gen_op_arith_modd(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign) @@ -2054,27 +2043,6 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, } } -/* neg neg. nego nego. */ -static inline void gen_op_arith_neg(DisasContext *ctx, bool compute_ov) -{ - TCGv zero = tcg_constant_tl(0); - gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - zero, 0, 0, compute_ov, Rc(ctx->opcode)); -} - -static void gen_neg(DisasContext *ctx) -{ - tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode))) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -static void gen_nego(DisasContext *ctx) -{ - gen_op_arith_neg(ctx, 1); -} - /*** Integer logical ***/ #define GEN_LOGICAL2(name, tcg_op, opc, type) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -2400,24 +2368,6 @@ static void gen_cnttzd(DisasContext *ctx) gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); } } - -/* darn */ -static void gen_darn(DisasContext *ctx) -{ - int l = L(ctx->opcode); - - if (l > 2) { - tcg_gen_movi_i64(cpu_gpr[rD(ctx->opcode)], -1); - } else { - translator_io_start(&ctx->base); - if (l == 0) { - gen_helper_darn32(cpu_gpr[rD(ctx->opcode)]); - } else { - /* Return 64-bit random for both CRN and RRN */ - gen_helper_darn64(cpu_gpr[rD(ctx->opcode)]); - } - } -} #endif /*** Integer rotate ***/ @@ -6243,8 +6193,6 @@ GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), #if defined(TARGET_PPC64) GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B), #endif -GEN_HANDLER(neg, 0x1F, 0x08, 0x03, 0x0000F800, PPC_INTEGER), -GEN_HANDLER(nego, 0x1F, 0x08, 0x13, 0x0000F800, PPC_INTEGER), GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), @@ -6265,7 +6213,6 @@ GEN_HANDLER_E(prtyw, 0x1F, 0x1A, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA205), GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD), GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B), GEN_HANDLER_E(cnttzd, 0x1F, 0x1A, 0x11, 0x00000000, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(darn, 0x1F, 0x13, 0x17, 0x001CF801, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(prtyd, 0x1F, 0x1A, 0x05, 0x0000F801, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(bpermd, 0x1F, 0x1C, 0x07, 0x00000001, PPC_NONE, PPC2_PERM_ISA206), #endif @@ -6450,9 +6397,6 @@ GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE, GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), #endif -GEN_HANDLER_E(modsw, 0x1F, 0x0B, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(moduw, 0x1F, 0x0B, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), - #if defined(TARGET_PPC64) #undef GEN_INT_ARITH_DIVD #define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 3265c77aa6..584cc4bfb2 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -485,6 +485,54 @@ TRANS(DIVWU, do_divw, 0); TRANS(DIVWE, do_divwe, gen_helper_DIVWE); TRANS(DIVWEU, do_divwe, gen_helper_DIVWEU); +static bool do_modw(DisasContext *ctx, arg_X *a, bool sign) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + gen_op_arith_modw(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign); + return true; +} + +TRANS(MODUW, do_modw, false); +TRANS(MODSW, do_modw, true); + +static bool trans_NEG(DisasContext *ctx, arg_NEG *a) +{ + if (a->oe) { + TCGv zero = tcg_constant_tl(0); + gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], zero, + false, false, true, a->rc); + } else { + tcg_gen_neg_tl(cpu_gpr[a->rt], cpu_gpr[a->ra]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + } + return true; +} + +static bool trans_DARN(DisasContext *ctx, arg_DARN *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + if (a->l > 2) { + tcg_gen_movi_i64(cpu_gpr[a->rt], -1); + } else { + translator_io_start(&ctx->base); + if (a->l == 0) { + gen_helper_DARN32(cpu_gpr[a->rt]); + } else { + /* Return 64-bit random for both CRN and RRN */ + gen_helper_DARN64(cpu_gpr[a->rt]); + } + } +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From 703e88f72325c46daa1a47c28469d814dd850d4c Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:31 +0530 Subject: [PATCH 15/72] target/ppc: Move multiply fixed-point insns (64-bit operands) to decodetree. Moving the following instructions to decodetree : mul{ld, ldo, hd, hdu}[.] : XO-form madd{hd, hdu, ld} : VA-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 9 ++ target/ppc/translate.c | 101 -------------------- target/ppc/translate/fixedpoint-impl.c.inc | 105 +++++++++++++++++++++ 3 files changed, 114 insertions(+), 101 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 3051792a56..46199488f7 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -384,6 +384,15 @@ MODUW 011111 ..... ..... ..... 0100001011 - @X DARN 011111 ..... --- .. ----- 1011110011 - @X_tl NEG 011111 ..... ..... ----- . 001101000 . @XO_ta +MULLD 011111 ..... ..... ..... 0 011101001 . @XO_tab_rc +MULLDO 011111 ..... ..... ..... 1 011101001 . @XO_tab_rc +MULHD 011111 ..... ..... ..... - 001001001 . @XO_tab_rc +MULHDU 011111 ..... ..... ..... - 000001001 . @XO_tab_rc + +MADDLD 000100 ..... ..... ..... ..... 110011 @VA +MADDHD 000100 ..... ..... ..... ..... 110000 @VA +MADDHDU 000100 ..... ..... ..... ..... 110001 @VA + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 3756b0c495..c76c4c9054 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1917,62 +1917,6 @@ GEN_INT_ARITH_MODD(modud, 0x08, 0); GEN_INT_ARITH_MODD(modsd, 0x18, 1); #endif -#if defined(TARGET_PPC64) -/* mulhd mulhd. */ -static void gen_mulhd(DisasContext *ctx) -{ - TCGv lo = tcg_temp_new(); - tcg_gen_muls2_tl(lo, cpu_gpr[rD(ctx->opcode)], - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulhdu mulhdu. */ -static void gen_mulhdu(DisasContext *ctx) -{ - TCGv lo = tcg_temp_new(); - tcg_gen_mulu2_tl(lo, cpu_gpr[rD(ctx->opcode)], - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulld mulld. */ -static void gen_mulld(DisasContext *ctx) -{ - tcg_gen_mul_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulldo mulldo. */ -static void gen_mulldo(DisasContext *ctx) -{ - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - - tcg_gen_muls2_i64(t0, t1, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mov_i64(cpu_gpr[rD(ctx->opcode)], t0); - - tcg_gen_sari_i64(t0, t0, 63); - tcg_gen_setcond_i64(TCG_COND_NE, cpu_ov, t0, t1); - if (is_isa300(ctx)) { - tcg_gen_mov_tl(cpu_ov32, cpu_ov); - } - tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); - - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} -#endif - /* Common subf function */ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, bool add_ca, bool compute_ca, @@ -5795,36 +5739,6 @@ static void gen_icbt_440(DisasContext *ctx) */ } -#if defined(TARGET_PPC64) -static void gen_maddld(DisasContext *ctx) -{ - TCGv_i64 t1 = tcg_temp_new_i64(); - - tcg_gen_mul_i64(t1, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - tcg_gen_add_i64(cpu_gpr[rD(ctx->opcode)], t1, cpu_gpr[rC(ctx->opcode)]); -} - -/* maddhd maddhdu */ -static void gen_maddhd_maddhdu(DisasContext *ctx) -{ - TCGv_i64 lo = tcg_temp_new_i64(); - TCGv_i64 hi = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - - if (Rc(ctx->opcode)) { - tcg_gen_mulu2_i64(lo, hi, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_movi_i64(t1, 0); - } else { - tcg_gen_muls2_i64(lo, hi, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_sari_i64(t1, cpu_gpr[rC(ctx->opcode)], 63); - } - tcg_gen_add2_i64(t1, cpu_gpr[rD(ctx->opcode)], lo, hi, - cpu_gpr[rC(ctx->opcode)], t1); -} -#endif /* defined(TARGET_PPC64) */ - static void gen_tbegin(DisasContext *ctx) { if (unlikely(!ctx->tm_enabled)) { @@ -6190,9 +6104,6 @@ GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(cmprb, 0x1F, 0x00, 0x06, 0x00400001, PPC_NONE, PPC2_ISA300), GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), -#if defined(TARGET_PPC64) -GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B), -#endif GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), @@ -6391,11 +6302,6 @@ GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE, - PPC2_ISA300), -GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300), -#endif #if defined(TARGET_PPC64) #undef GEN_INT_ARITH_DIVD @@ -6412,13 +6318,6 @@ GEN_HANDLER_E(divde, 0x1F, 0x09, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), GEN_HANDLER_E(divdeo, 0x1F, 0x09, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), GEN_HANDLER_E(modsd, 0x1F, 0x09, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(modud, 0x1F, 0x09, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), - -#undef GEN_INT_ARITH_MUL_HELPER -#define GEN_INT_ARITH_MUL_HELPER(name, opc3) \ -GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) -GEN_INT_ARITH_MUL_HELPER(mulhdu, 0x00), -GEN_INT_ARITH_MUL_HELPER(mulhd, 0x02), -GEN_INT_ARITH_MUL_HELPER(mulldo, 0x17), #endif #undef GEN_LOGICAL1 diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 584cc4bfb2..077c938b30 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -533,6 +533,111 @@ static bool trans_DARN(DisasContext *ctx, arg_DARN *a) return true; } +static bool trans_MULLD(DisasContext *ctx, arg_MULLD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + tcg_gen_mul_tl(cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MULLDO(DisasContext *ctx, arg_MULLD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_muls2_i64(t0, t1, cpu_gpr[a->ra], cpu_gpr[a->rb]); + tcg_gen_mov_i64(cpu_gpr[a->rt], t0); + + tcg_gen_sari_i64(t0, t0, 63); + tcg_gen_setcond_i64(TCG_COND_NE, cpu_ov, t0, t1); + if (is_isa300(ctx)) { + tcg_gen_mov_tl(cpu_ov32, cpu_ov); + } + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_mulhd(DisasContext *ctx, arg_XO_tab_rc *a, + void (*helper)(TCGv, TCGv, TCGv, TCGv)) +{ + TCGv lo = tcg_temp_new(); + helper(lo, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +TRANS64(MULHD, do_mulhd, tcg_gen_muls2_tl); +TRANS64(MULHDU, do_mulhd, tcg_gen_mulu2_tl); + +static bool trans_MADDLD(DisasContext *ctx, arg_MADDLD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t1, cpu_gpr[a->vra], cpu_gpr[a->vrb]); + tcg_gen_add_i64(cpu_gpr[a->vrt], t1, cpu_gpr[a->rc]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MADDHD(DisasContext *ctx, arg_MADDHD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + TCGv_i64 lo = tcg_temp_new_i64(); + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_muls2_i64(lo, hi, cpu_gpr[a->vra], cpu_gpr[a->vrb]); + tcg_gen_sari_i64(t1, cpu_gpr[a->rc], 63); + tcg_gen_add2_i64(t1, cpu_gpr[a->vrt], lo, hi, cpu_gpr[a->rc], t1); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MADDHDU(DisasContext *ctx, arg_MADDHDU *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + TCGv_i64 lo = tcg_temp_new_i64(); + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_mulu2_i64(lo, hi, cpu_gpr[a->vra], cpu_gpr[a->vrb]); + tcg_gen_add2_i64(t1, cpu_gpr[a->vrt], lo, hi, cpu_gpr[a->rc], + tcg_constant_i64(0)); +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From f424bc10ebf2a935a2c20400996d665434ec9e17 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:32 +0530 Subject: [PATCH 16/72] target/ppc: Move div/mod fixed-point insns (64 bits operands) to decodetree. Moving the below instructions to decodetree specification : divd[u, e, eu][o][.] : XO-form mod{sd, ud} : X-form With this patch, all the fixed-point arithmetic instructions have been moved to decodetree. The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured using the '-d in_asm,op' flag. Also, remaned do_divwe method in fixedpoint-impl.c.inc to do_dive because it is now used to divide doubleword operands as well, and not just words. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 4 +- target/ppc/insn32.decode | 8 +++ target/ppc/int_helper.c | 4 +- target/ppc/translate.c | 63 ++-------------------- target/ppc/translate/fixedpoint-impl.c.inc | 58 ++++++++++++++++++-- 5 files changed, 70 insertions(+), 67 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 5a97429f1e..b5a76f1365 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -52,8 +52,8 @@ DEF_HELPER_FLAGS_2(icbiep, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) -DEF_HELPER_4(divdeu, i64, env, i64, i64, i32) -DEF_HELPER_4(divde, i64, env, i64, i64, i32) +DEF_HELPER_4(DIVDEU, i64, env, i64, i64, i32) +DEF_HELPER_4(DIVDE, i64, env, i64, i64, i32) #endif DEF_HELPER_4(DIVWEU, tl, env, tl, tl, i32) DEF_HELPER_4(DIVWE, tl, env, tl, tl, i32) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 46199488f7..c7cb6e7f37 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -393,6 +393,14 @@ MADDLD 000100 ..... ..... ..... ..... 110011 @VA MADDHD 000100 ..... ..... ..... ..... 110000 @VA MADDHDU 000100 ..... ..... ..... ..... 110001 @VA +DIVD 011111 ..... ..... ..... . 111101001 . @XO +DIVDU 011111 ..... ..... ..... . 111001001 . @XO +DIVDE 011111 ..... ..... ..... . 110101001 . @XO +DIVDEU 011111 ..... ..... ..... . 110001001 . @XO + +MODSD 011111 ..... ..... ..... 1100001001 - @X +MODUD 011111 ..... ..... ..... 0100001001 - @X + ## Fixed-Point Logical Instructions CFUGED 011111 ..... ..... ..... 0011011100 - @X diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index bc25d5b062..585c2b65d3 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -101,7 +101,7 @@ target_ulong helper_DIVWE(CPUPPCState *env, target_ulong ra, target_ulong rb, #if defined(TARGET_PPC64) -uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) +uint64_t helper_DIVDEU(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) { uint64_t rt = 0; int overflow = 0; @@ -120,7 +120,7 @@ uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) return rt; } -uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) +uint64_t helper_DIVDE(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) { uint64_t rt = 0; int64_t ra = (int64_t)rau; diff --git a/target/ppc/translate.c b/target/ppc/translate.c index c76c4c9054..ba7c1fdf43 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1777,21 +1777,11 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, gen_set_Rc0(ctx, ret); } } -/* div[wd]eu[o][.] */ -#define GEN_DIVE(name, hlpr, compute_ov) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_i32 t0 = tcg_constant_i32(compute_ov); \ - gen_helper_##hlpr(cpu_gpr[rD(ctx->opcode)], tcg_env, \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); \ - if (unlikely(Rc(ctx->opcode) != 0)) { \ - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); \ - } \ -} #if defined(TARGET_PPC64) -static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, - TCGv arg2, int sign, int compute_ov) +static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, + TCGv arg1, TCGv arg2, bool sign, + bool compute_ov, bool compute_rc0) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -1827,25 +1817,6 @@ static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, gen_set_Rc0(ctx, ret); } } - -#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_divd(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign, compute_ov); \ -} -/* divdu divdu. divduo divduo. */ -GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0); -GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1); -/* divd divd. divdo divdo. */ -GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0); -GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1); - -GEN_DIVE(divdeu, divdeu, 0); -GEN_DIVE(divdeuo, divdeu, 1); -GEN_DIVE(divde, divde, 0); -GEN_DIVE(divdeo, divde, 1); #endif static inline void gen_op_arith_modw(DisasContext *ctx, TCGv ret, TCGv arg1, @@ -1904,17 +1875,6 @@ static inline void gen_op_arith_modd(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_remu_i64(ret, t0, t1); } } - -#define GEN_INT_ARITH_MODD(name, opc3, sign) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_modd(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign); \ -} - -GEN_INT_ARITH_MODD(modud, 0x08, 0); -GEN_INT_ARITH_MODD(modsd, 0x18, 1); #endif /* Common subf function */ @@ -6303,23 +6263,6 @@ GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), -#if defined(TARGET_PPC64) -#undef GEN_INT_ARITH_DIVD -#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ -GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) -GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0), -GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1), -GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0), -GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1), - -GEN_HANDLER_E(divdeu, 0x1F, 0x09, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divdeuo, 0x1F, 0x09, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divde, 0x1F, 0x09, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divdeo, 0x1F, 0x09, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(modsd, 0x1F, 0x09, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(modud, 0x1F, 0x09, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), -#endif - #undef GEN_LOGICAL1 #undef GEN_LOGICAL2 #define GEN_LOGICAL2(name, tcg_op, opc, type) \ diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 077c938b30..2ada7473ea 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -468,7 +468,7 @@ static bool do_divw(DisasContext *ctx, arg_XO *a, int sign) return true; } -static bool do_divwe(DisasContext *ctx, arg_XO *a, +static bool do_dive(DisasContext *ctx, arg_XO *a, void (*helper)(TCGv, TCGv_ptr, TCGv, TCGv, TCGv_i32)) { REQUIRE_INSNS_FLAGS2(ctx, DIVE_ISA206); @@ -482,8 +482,8 @@ static bool do_divwe(DisasContext *ctx, arg_XO *a, TRANS(DIVW, do_divw, 1); TRANS(DIVWU, do_divw, 0); -TRANS(DIVWE, do_divwe, gen_helper_DIVWE); -TRANS(DIVWEU, do_divwe, gen_helper_DIVWEU); +TRANS(DIVWE, do_dive, gen_helper_DIVWE); +TRANS(DIVWEU, do_dive, gen_helper_DIVWEU); static bool do_modw(DisasContext *ctx, arg_X *a, bool sign) { @@ -638,6 +638,58 @@ static bool trans_MADDHDU(DisasContext *ctx, arg_MADDHDU *a) return true; } +static bool do_divd(DisasContext *ctx, arg_XO *a, bool sign) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + gen_op_arith_divd(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign, a->oe, a->rc); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_modd(DisasContext *ctx, arg_X *a, bool sign) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + gen_op_arith_modd(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign); +#else + qemu_build_not_reached(); +#endif + return true; +} + +TRANS64(DIVD, do_divd, true); +TRANS64(DIVDU, do_divd, false); + +static bool trans_DIVDE(DisasContext *ctx, arg_DIVDE *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + return do_dive(ctx, a, gen_helper_DIVDE); +#else + qemu_build_not_reached(); +#endif +} + +static bool trans_DIVDEU(DisasContext *ctx, arg_DIVDEU *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + return do_dive(ctx, a, gen_helper_DIVDEU); +#else + qemu_build_not_reached(); +#endif + return true; +} + +TRANS64(MODSD, do_modd, true); +TRANS64(MODUD, do_modd, false); + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From ae556c6a49d47208e4cbc70efab3dfd5bb2ac309 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:33 +0530 Subject: [PATCH 17/72] target/ppc: Move cmp{rb, eqb}, tw[i], td[i], isel instructions to decodetree. Moving the following instructions to decodetree specification : cmp{rb, eqb}, t{w, d} : X-form t{w, d}i : D-form isel : A-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured using the '-d in_asm,op' flag. Also for CMPRB, following review comments : Replaced repetition of arithmetic right shifting (tcg_gen_shri_i32) followed by extraction of last 8 bits (tcg_gen_ext8u_i32) with extraction of the required bits using offsets (tcg_gen_extract_i32). Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 4 +- target/ppc/helper.h | 6 +- target/ppc/insn32.decode | 16 +++ target/ppc/int_helper.c | 2 +- target/ppc/translate.c | 133 +-------------------- target/ppc/translate/fixedpoint-impl.c.inc | 132 ++++++++++++++++++++ 6 files changed, 157 insertions(+), 136 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 9df17f93bf..2e3f36a3ef 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2742,7 +2742,7 @@ void helper_rfmci(CPUPPCState *env) } #endif /* !CONFIG_USER_ONLY */ -void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, +void helper_TW(CPUPPCState *env, target_ulong arg1, target_ulong arg2, uint32_t flags) { if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || @@ -2756,7 +2756,7 @@ void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, } #ifdef TARGET_PPC64 -void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, +void helper_TD(CPUPPCState *env, target_ulong arg1, target_ulong arg2, uint32_t flags) { if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || diff --git a/target/ppc/helper.h b/target/ppc/helper.h index b5a76f1365..b8af2cf878 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -1,8 +1,8 @@ DEF_HELPER_FLAGS_3(raise_exception_err, TCG_CALL_NO_WG, noreturn, env, i32, i32) DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) -DEF_HELPER_FLAGS_4(tw, TCG_CALL_NO_WG, void, env, tl, tl, i32) +DEF_HELPER_FLAGS_4(TW, TCG_CALL_NO_WG, void, env, tl, tl, i32) #if defined(TARGET_PPC64) -DEF_HELPER_FLAGS_4(td, TCG_CALL_NO_WG, void, env, tl, tl, i32) +DEF_HELPER_FLAGS_4(TD, TCG_CALL_NO_WG, void, env, tl, tl, i32) #endif DEF_HELPER_4(HASHST, void, env, tl, tl, tl) DEF_HELPER_4(HASHCHK, void, env, tl, tl, tl) @@ -67,7 +67,7 @@ DEF_HELPER_FLAGS_2(PEXTD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_1(CDTBCD, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(CBCDTD, TCG_CALL_NO_RWG_SE, tl, tl) #if defined(TARGET_PPC64) -DEF_HELPER_FLAGS_2(cmpeqb, TCG_CALL_NO_RWG_SE, i32, tl, tl) +DEF_HELPER_FLAGS_2(CMPEQB, TCG_CALL_NO_RWG_SE, i32, tl, tl) DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(srad, tl, env, tl, tl) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index c7cb6e7f37..cb1e4bd307 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -29,6 +29,9 @@ &A_tb frt frb rc:bool @A_tb ...... frt:5 ..... frb:5 ..... ..... rc:1 &A_tb +&A_tab_bc rt ra rb bc +@A_tab_bc ...... rt:5 ra:5 rb:5 bc:5 ..... . &A_tab_bc + &D rt ra si:int64_t @D ...... rt:5 ra:5 si:s16 &D @@ -340,6 +343,19 @@ CMP 011111 ... - . ..... ..... 0000000000 - @X_bfl CMPL 011111 ... - . ..... ..... 0000100000 - @X_bfl CMPI 001011 ... - . ..... ................ @D_bfs CMPLI 001010 ... - . ..... ................ @D_bfu +CMPRB 011111 ... - . ..... ..... 0011000000 - @X_bfl +CMPEQB 011111 ... -- ..... ..... 0011100000 - @X_bf + +### Fixed-Point Trap Instructions + +TW 011111 ..... ..... ..... 0000000100 - @X +TD 011111 ..... ..... ..... 0001000100 - @X +TWI 000011 ..... ..... ................ @D +TDI 000010 ..... ..... ................ @D + +### Fixed-Point Select Instruction + +ISEL 011111 ..... ..... ..... ..... 01111 - @A_tab_bc ### Fixed-Point Arithmetic Instructions diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 585c2b65d3..d12dcc28e1 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -159,7 +159,7 @@ uint64_t helper_DIVDE(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) /* When you XOR the pattern and there is a match, that byte will be zero */ #define hasvalue(x, n) (haszero((x) ^ pattern(n))) -uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb) +uint32_t helper_CMPEQB(target_ulong ra, target_ulong rb) { return hasvalue(rb, ra) ? CRF_GT : 0; } diff --git a/target/ppc/translate.c b/target/ppc/translate.c index ba7c1fdf43..e1ccb82f10 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1563,66 +1563,6 @@ static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg) } } -/* cmprb - range comparison: isupper, isaplha, islower*/ -static void gen_cmprb(DisasContext *ctx) -{ - TCGv_i32 src1 = tcg_temp_new_i32(); - TCGv_i32 src2 = tcg_temp_new_i32(); - TCGv_i32 src2lo = tcg_temp_new_i32(); - TCGv_i32 src2hi = tcg_temp_new_i32(); - TCGv_i32 crf = cpu_crf[crfD(ctx->opcode)]; - - tcg_gen_trunc_tl_i32(src1, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_trunc_tl_i32(src2, cpu_gpr[rB(ctx->opcode)]); - - tcg_gen_andi_i32(src1, src1, 0xFF); - tcg_gen_ext8u_i32(src2lo, src2); - tcg_gen_shri_i32(src2, src2, 8); - tcg_gen_ext8u_i32(src2hi, src2); - - tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); - tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); - tcg_gen_and_i32(crf, src2lo, src2hi); - - if (ctx->opcode & 0x00200000) { - tcg_gen_shri_i32(src2, src2, 8); - tcg_gen_ext8u_i32(src2lo, src2); - tcg_gen_shri_i32(src2, src2, 8); - tcg_gen_ext8u_i32(src2hi, src2); - tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); - tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); - tcg_gen_and_i32(src2lo, src2lo, src2hi); - tcg_gen_or_i32(crf, crf, src2lo); - } - tcg_gen_shli_i32(crf, crf, CRF_GT_BIT); -} - -#if defined(TARGET_PPC64) -/* cmpeqb */ -static void gen_cmpeqb(DisasContext *ctx) -{ - gen_helper_cmpeqb(cpu_crf[crfD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); -} -#endif - -/* isel (PowerPC 2.03 specification) */ -static void gen_isel(DisasContext *ctx) -{ - uint32_t bi = rC(ctx->opcode); - uint32_t mask = 0x08 >> (bi & 0x03); - TCGv t0 = tcg_temp_new(); - TCGv zr; - - tcg_gen_extu_i32_tl(t0, cpu_crf[bi >> 2]); - tcg_gen_andi_tl(t0, t0, mask); - - zr = tcg_constant_tl(0); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rD(ctx->opcode)], t0, zr, - rA(ctx->opcode) ? cpu_gpr[rA(ctx->opcode)] : zr, - cpu_gpr[rB(ctx->opcode)]); -} - /* cmpb: PowerPC 2.05 specification */ static void gen_cmpb(DisasContext *ctx) { @@ -4187,76 +4127,20 @@ static void gen_scv(DisasContext *ctx) /*** Trap ***/ /* Check for unconditional traps (always or never) */ -static bool check_unconditional_trap(DisasContext *ctx) +static bool check_unconditional_trap(DisasContext *ctx, int to) { /* Trap never */ - if (TO(ctx->opcode) == 0) { + if (to == 0) { return true; } /* Trap always */ - if (TO(ctx->opcode) == 31) { + if (to == 31) { gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP); return true; } return false; } -/* tw */ -static void gen_tw(DisasContext *ctx) -{ - TCGv_i32 t0; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_tw(tcg_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], - t0); -} - -/* twi */ -static void gen_twi(DisasContext *ctx) -{ - TCGv t0; - TCGv_i32 t1; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_tl(SIMM(ctx->opcode)); - t1 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_tw(tcg_env, cpu_gpr[rA(ctx->opcode)], t0, t1); -} - -#if defined(TARGET_PPC64) -/* td */ -static void gen_td(DisasContext *ctx) -{ - TCGv_i32 t0; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_td(tcg_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], - t0); -} - -/* tdi */ -static void gen_tdi(DisasContext *ctx) -{ - TCGv t0; - TCGv_i32 t1; - - if (check_unconditional_trap(ctx)) { - return; - } - t0 = tcg_constant_tl(SIMM(ctx->opcode)); - t1 = tcg_constant_i32(TO(ctx->opcode)); - gen_helper_td(tcg_env, cpu_gpr[rA(ctx->opcode)], t0, t1); -} -#endif - /*** Processor control ***/ /* mcrxr */ @@ -6058,12 +5942,7 @@ GEN_HANDLER_E(brw, 0x1F, 0x1B, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA310), GEN_HANDLER_E(brh, 0x1F, 0x1B, 0x06, 0x0000F801, PPC_NONE, PPC2_ISA310), #endif GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300), -#endif GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), -GEN_HANDLER_E(cmprb, 0x1F, 0x00, 0x06, 0x00400001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL), GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), @@ -6160,12 +6039,6 @@ GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H), /* Top bit of opc2 corresponds with low bit of LEV, so use two handlers */ GEN_HANDLER(sc, 0x11, 0x11, 0xFF, 0x03FFF01D, PPC_FLOW), GEN_HANDLER(sc, 0x11, 0x01, 0xFF, 0x03FFF01D, PPC_FLOW), -GEN_HANDLER(tw, 0x1F, 0x04, 0x00, 0x00000001, PPC_FLOW), -GEN_HANDLER(twi, 0x03, 0xFF, 0xFF, 0x00000000, PPC_FLOW), -#if defined(TARGET_PPC64) -GEN_HANDLER(td, 0x1F, 0x04, 0x02, 0x00000001, PPC_64B), -GEN_HANDLER(tdi, 0x02, 0xFF, 0xFF, 0x00000000, PPC_64B), -#endif GEN_HANDLER(mcrxr, 0x1F, 0x00, 0x10, 0x007FF801, PPC_MISC), GEN_HANDLER(mfcr, 0x1F, 0x13, 0x00, 0x00000801, PPC_MISC), GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC), diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 2ada7473ea..872fed664d 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -289,6 +289,50 @@ TRANS(CMPL, do_cmp_X, false); TRANS(CMPI, do_cmp_D, true); TRANS(CMPLI, do_cmp_D, false); +static bool trans_CMPRB(DisasContext *ctx, arg_CMPRB *a) +{ + TCGv_i32 src1 = tcg_temp_new_i32(); + TCGv_i32 src2 = tcg_temp_new_i32(); + TCGv_i32 src2lo = tcg_temp_new_i32(); + TCGv_i32 src2hi = tcg_temp_new_i32(); + TCGv_i32 crf = cpu_crf[a->bf]; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + tcg_gen_trunc_tl_i32(src1, cpu_gpr[a->ra]); + tcg_gen_trunc_tl_i32(src2, cpu_gpr[a->rb]); + + tcg_gen_andi_i32(src1, src1, 0xFF); + tcg_gen_ext8u_i32(src2lo, src2); + tcg_gen_extract_i32(src2hi, src2, 8, 8); + + tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); + tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); + tcg_gen_and_i32(crf, src2lo, src2hi); + + if (a->l) { + tcg_gen_extract_i32(src2lo, src2, 16, 8); + tcg_gen_extract_i32(src2hi, src2, 24, 8); + tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); + tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); + tcg_gen_and_i32(src2lo, src2lo, src2hi); + tcg_gen_or_i32(crf, crf, src2lo); + } + tcg_gen_shli_i32(crf, crf, CRF_GT_BIT); + return true; +} + +static bool trans_CMPEQB(DisasContext *ctx, arg_CMPEQB *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + gen_helper_CMPEQB(cpu_crf[a->bf], cpu_gpr[a->ra], cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + /* * Fixed-Point Arithmetic Instructions */ @@ -690,6 +734,94 @@ static bool trans_DIVDEU(DisasContext *ctx, arg_DIVDEU *a) TRANS64(MODSD, do_modd, true); TRANS64(MODUD, do_modd, false); +/* + * Fixed-Point Select Instructions + */ + +static bool trans_ISEL(DisasContext *ctx, arg_ISEL *a) +{ + REQUIRE_INSNS_FLAGS(ctx, ISEL); + uint32_t bi = a->bc; + uint32_t mask = 0x08 >> (bi & 0x03); + TCGv t0 = tcg_temp_new(); + TCGv zr; + + tcg_gen_extu_i32_tl(t0, cpu_crf[bi >> 2]); + tcg_gen_andi_tl(t0, t0, mask); + + zr = tcg_constant_tl(0); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[a->rt], t0, zr, + a->ra ? cpu_gpr[a->ra] : zr, + cpu_gpr[a->rb]); + return true; +} + +/* + * Fixed-Point Trap Instructions + */ + +static bool trans_TW(DisasContext *ctx, arg_TW *a) +{ + TCGv_i32 t0; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_i32(a->rt); + gen_helper_TW(tcg_env, cpu_gpr[a->ra], cpu_gpr[a->rb], t0); + return true; +} + +static bool trans_TWI(DisasContext *ctx, arg_TWI *a) +{ + TCGv t0; + TCGv_i32 t1; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_tl(a->si); + t1 = tcg_constant_i32(a->rt); + gen_helper_TW(tcg_env, cpu_gpr[a->ra], t0, t1); + return true; +} + +static bool trans_TD(DisasContext *ctx, arg_TD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + TCGv_i32 t0; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_i32(a->rt); + gen_helper_TD(tcg_env, cpu_gpr[a->ra], cpu_gpr[a->rb], t0); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_TDI(DisasContext *ctx, arg_TDI *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + TCGv t0; + TCGv_i32 t1; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_tl(a->si); + t1 = tcg_constant_i32(a->rt); + gen_helper_TD(tcg_env, cpu_gpr[a->ra], t0, t1); +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); From 948e257c48cf8e387e12b81f4b96195d52b68455 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 23 Apr 2024 12:02:34 +0530 Subject: [PATCH 18/72] target/ppc: Move logical fixed-point instructions to decodetree. Moving the below instructions to decodetree specification : andi[s]., {ori, xori}[s] : D-form {and, andc, nand, or, orc, nor, xor, eqv}[.], exts{b, h, w}[.], cnt{l, t}z{w, d}[.], popcnt{b, w, d}, prty{w, d}, cmp, bpermd : X-form With this patch, all the fixed-point logical instructions have been moved to decodetree. The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath [np: 32-bit compile fix] Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 8 +- target/ppc/insn32.decode | 38 +++ target/ppc/int_helper.c | 10 +- target/ppc/translate.c | 359 --------------------- target/ppc/translate/fixedpoint-impl.c.inc | 300 +++++++++++++++++ 5 files changed, 347 insertions(+), 368 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index b8af2cf878..4267917615 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -58,8 +58,8 @@ DEF_HELPER_4(DIVDE, i64, env, i64, i64, i32) DEF_HELPER_4(DIVWEU, tl, env, tl, tl, i32) DEF_HELPER_4(DIVWE, tl, env, tl, tl, i32) -DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_2(cmpb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_1(POPCNTB, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(CMPB, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_3(sraw, tl, env, tl, tl) DEF_HELPER_FLAGS_2(CFUGED, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(PDEPD, TCG_CALL_NO_RWG_SE, i64, i64, i64) @@ -68,8 +68,8 @@ DEF_HELPER_FLAGS_1(CDTBCD, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(CBCDTD, TCG_CALL_NO_RWG_SE, tl, tl) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_2(CMPEQB, TCG_CALL_NO_RWG_SE, i32, tl, tl) -DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_1(POPCNTW, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(BPERMD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(srad, tl, env, tl, tl) DEF_HELPER_FLAGS_0(DARN32, TCG_CALL_NO_RWG, tl) DEF_HELPER_FLAGS_0(DARN64, TCG_CALL_NO_RWG, tl) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index cb1e4bd307..dc62bc90aa 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -35,6 +35,9 @@ &D rt ra si:int64_t @D ...... rt:5 ra:5 si:s16 &D +&D_ui rt ra ui:uint64_t +@D_ui ...... rt:5 ra:5 ui:16 &D_ui + &D_bf bf l:bool ra imm @D_bfs ...... bf:3 . l:1 ra:5 imm:s16 &D_bf @D_bfu ...... bf:3 . l:1 ra:5 imm:16 &D_bf @@ -102,6 +105,9 @@ &X_sa rs ra @X_sa ...... rs:5 ra:5 ..... .......... . &X_sa +&X_sa_rc rs ra rc +@X_sa_rc ...... rs:5 ra:5 ..... .......... rc:1 &X_sa_rc + %x_frtp 22:4 !function=times_2 %x_frap 17:4 !function=times_2 %x_frbp 12:4 !function=times_2 @@ -419,6 +425,38 @@ MODUD 011111 ..... ..... ..... 0100001001 - @X ## Fixed-Point Logical Instructions +ANDI_ 011100 ..... ..... ................ @D_ui +ANDIS_ 011101 ..... ..... ................ @D_ui +ORI 011000 ..... ..... ................ @D_ui +ORIS 011001 ..... ..... ................ @D_ui +XORI 011010 ..... ..... ................ @D_ui +XORIS 011011 ..... ..... ................ @D_ui + +AND 011111 ..... ..... ..... 0000011100 . @X_rc +ANDC 011111 ..... ..... ..... 0000111100 . @X_rc +NAND 011111 ..... ..... ..... 0111011100 . @X_rc +OR 011111 ..... ..... ..... 0110111100 . @X_rc +ORC 011111 ..... ..... ..... 0110011100 . @X_rc +NOR 011111 ..... ..... ..... 0001111100 . @X_rc +XOR 011111 ..... ..... ..... 0100111100 . @X_rc +EQV 011111 ..... ..... ..... 0100011100 . @X_rc +CMPB 011111 ..... ..... ..... 0111111100 . @X_rc + +EXTSB 011111 ..... ..... ----- 1110111010 . @X_sa_rc +EXTSH 011111 ..... ..... ----- 1110011010 . @X_sa_rc +EXTSW 011111 ..... ..... ----- 1111011010 . @X_sa_rc +CNTLZW 011111 ..... ..... ----- 0000011010 . @X_sa_rc +CNTTZW 011111 ..... ..... ----- 1000011010 . @X_sa_rc +CNTLZD 011111 ..... ..... ----- 0000111010 . @X_sa_rc +CNTTZD 011111 ..... ..... ----- 1000111010 . @X_sa_rc +POPCNTB 011111 ..... ..... ----- 0001111010 . @X_sa_rc + +POPCNTW 011111 ..... ..... ----- 0101111010 - @X_sa +POPCNTD 011111 ..... ..... ----- 0111111010 - @X_sa +PRTYW 011111 ..... ..... ----- 0010011010 - @X_sa +PRTYD 011111 ..... ..... ----- 0010111010 - @X_sa + +BPERMD 011111 ..... ..... ..... 0011111100 - @X CFUGED 011111 ..... ..... ..... 0011011100 - @X CNTLZDM 011111 ..... ..... ..... 0000111011 - @X CNTTZDM 011111 ..... ..... ..... 1000111011 - @X diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index d12dcc28e1..2c6b633d65 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -201,7 +201,7 @@ uint64_t helper_DARN64(void) return ret; } -uint64_t helper_bpermd(uint64_t rs, uint64_t rb) +uint64_t helper_BPERMD(uint64_t rs, uint64_t rb) { int i; uint64_t ra = 0; @@ -219,7 +219,7 @@ uint64_t helper_bpermd(uint64_t rs, uint64_t rb) #endif -target_ulong helper_cmpb(target_ulong rs, target_ulong rb) +target_ulong helper_CMPB(target_ulong rs, target_ulong rb) { target_ulong mask = 0xff; target_ulong ra = 0; @@ -288,7 +288,7 @@ target_ulong helper_srad(CPUPPCState *env, target_ulong value, #endif #if defined(TARGET_PPC64) -target_ulong helper_popcntb(target_ulong val) +target_ulong helper_POPCNTB(target_ulong val) { /* Note that we don't fold past bytes */ val = (val & 0x5555555555555555ULL) + ((val >> 1) & @@ -300,7 +300,7 @@ target_ulong helper_popcntb(target_ulong val) return val; } -target_ulong helper_popcntw(target_ulong val) +target_ulong helper_POPCNTW(target_ulong val) { /* Note that we don't fold past words. */ val = (val & 0x5555555555555555ULL) + ((val >> 1) & @@ -316,7 +316,7 @@ target_ulong helper_popcntw(target_ulong val) return val; } #else -target_ulong helper_popcntb(target_ulong val) +target_ulong helper_POPCNTB(target_ulong val) { /* Note that we don't fold past bytes */ val = (val & 0x55555555) + ((val >> 1) & 0x55555555); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index e1ccb82f10..2cfa7d37ee 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1563,13 +1563,6 @@ static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg) } } -/* cmpb: PowerPC 2.05 specification */ -static void gen_cmpb(DisasContext *ctx) -{ - gen_helper_cmpb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); -} - /*** Integer arithmetic ***/ static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0, @@ -1888,82 +1881,6 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, } /*** Integer logical ***/ -#define GEN_LOGICAL2(name, tcg_op, opc, type) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - tcg_op(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], \ - cpu_gpr[rB(ctx->opcode)]); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); \ -} - -#define GEN_LOGICAL1(name, tcg_op, opc, type) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - tcg_op(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); \ -} - -/* and & and. */ -GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER); -/* andc & andc. */ -GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER); - -/* andi. */ -static void gen_andi_(DisasContext *ctx) -{ - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - UIMM(ctx->opcode)); - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); -} - -/* andis. */ -static void gen_andis_(DisasContext *ctx) -{ - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - UIMM(ctx->opcode) << 16); - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); -} - -/* cntlzw */ -static void gen_cntlzw(DisasContext *ctx) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_clzi_i32(t, t, 32); - tcg_gen_extu_i32_tl(cpu_gpr[rA(ctx->opcode)], t); - - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* cnttzw */ -static void gen_cnttzw(DisasContext *ctx) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_ctzi_i32(t, t, 32); - tcg_gen_extu_i32_tl(cpu_gpr[rA(ctx->opcode)], t); - - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* eqv & eqv. */ -GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER); -/* extsb & extsb. */ -GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER); -/* extsh & extsh. */ -GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER); -/* nand & nand. */ -GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER); -/* nor & nor. */ -GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER); #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) static void gen_pause(DisasContext *ctx) @@ -1977,243 +1894,6 @@ static void gen_pause(DisasContext *ctx) } #endif /* defined(TARGET_PPC64) */ -/* or & or. */ -static void gen_or(DisasContext *ctx) -{ - int rs, ra, rb; - - rs = rS(ctx->opcode); - ra = rA(ctx->opcode); - rb = rB(ctx->opcode); - /* Optimisation for mr. ri case */ - if (rs != ra || rs != rb) { - if (rs != rb) { - tcg_gen_or_tl(cpu_gpr[ra], cpu_gpr[rs], cpu_gpr[rb]); - } else { - tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rs]); - } - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[ra]); - } - } else if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rs]); -#if defined(TARGET_PPC64) - } else if (rs != 0) { /* 0 is nop */ - int prio = 0; - - switch (rs) { - case 1: - /* Set process priority to low */ - prio = 2; - break; - case 6: - /* Set process priority to medium-low */ - prio = 3; - break; - case 2: - /* Set process priority to normal */ - prio = 4; - break; -#if !defined(CONFIG_USER_ONLY) - case 31: - if (!ctx->pr) { - /* Set process priority to very low */ - prio = 1; - } - break; - case 5: - if (!ctx->pr) { - /* Set process priority to medium-hight */ - prio = 5; - } - break; - case 3: - if (!ctx->pr) { - /* Set process priority to high */ - prio = 6; - } - break; - case 7: - if (ctx->hv && !ctx->pr) { - /* Set process priority to very high */ - prio = 7; - } - break; -#endif - default: - break; - } - if (prio) { - TCGv t0 = tcg_temp_new(); - gen_load_spr(t0, SPR_PPR); - tcg_gen_andi_tl(t0, t0, ~0x001C000000000000ULL); - tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50); - gen_store_spr(SPR_PPR, t0); - } -#if !defined(CONFIG_USER_ONLY) - /* - * Pause out of TCG otherwise spin loops with smt_low eat too - * much CPU and the kernel hangs. This applies to all - * encodings other than no-op, e.g., miso(rs=26), yield(27), - * mdoio(29), mdoom(30), and all currently undefined. - */ - gen_pause(ctx); -#endif -#endif - } -} -/* orc & orc. */ -GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER); - -/* xor & xor. */ -static void gen_xor(DisasContext *ctx) -{ - /* Optimisation for "set to zero" case */ - if (rS(ctx->opcode) != rB(ctx->opcode)) { - tcg_gen_xor_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - } else { - tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0); - } - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* ori */ -static void gen_ori(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - return; - } - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); -} - -/* oris */ -static void gen_oris(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - return; - } - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - uimm << 16); -} - -/* xori */ -static void gen_xori(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - return; - } - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); -} - -/* xoris */ -static void gen_xoris(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - return; - } - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - uimm << 16); -} - -/* popcntb : PowerPC 2.03 specification */ -static void gen_popcntb(DisasContext *ctx) -{ - gen_helper_popcntb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -} - -static void gen_popcntw(DisasContext *ctx) -{ -#if defined(TARGET_PPC64) - gen_helper_popcntw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -#else - tcg_gen_ctpop_i32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -#endif -} - -#if defined(TARGET_PPC64) -/* popcntd: PowerPC 2.06 specification */ -static void gen_popcntd(DisasContext *ctx) -{ - tcg_gen_ctpop_i64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -} -#endif - -/* prtyw: PowerPC 2.05 specification */ -static void gen_prtyw(DisasContext *ctx) -{ - TCGv ra = cpu_gpr[rA(ctx->opcode)]; - TCGv rs = cpu_gpr[rS(ctx->opcode)]; - TCGv t0 = tcg_temp_new(); - tcg_gen_shri_tl(t0, rs, 16); - tcg_gen_xor_tl(ra, rs, t0); - tcg_gen_shri_tl(t0, ra, 8); - tcg_gen_xor_tl(ra, ra, t0); - tcg_gen_andi_tl(ra, ra, (target_ulong)0x100000001ULL); -} - -#if defined(TARGET_PPC64) -/* prtyd: PowerPC 2.05 specification */ -static void gen_prtyd(DisasContext *ctx) -{ - TCGv ra = cpu_gpr[rA(ctx->opcode)]; - TCGv rs = cpu_gpr[rS(ctx->opcode)]; - TCGv t0 = tcg_temp_new(); - tcg_gen_shri_tl(t0, rs, 32); - tcg_gen_xor_tl(ra, rs, t0); - tcg_gen_shri_tl(t0, ra, 16); - tcg_gen_xor_tl(ra, ra, t0); - tcg_gen_shri_tl(t0, ra, 8); - tcg_gen_xor_tl(ra, ra, t0); - tcg_gen_andi_tl(ra, ra, 1); -} -#endif - -#if defined(TARGET_PPC64) -/* bpermd */ -static void gen_bpermd(DisasContext *ctx) -{ - gen_helper_bpermd(cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); -} -#endif - -#if defined(TARGET_PPC64) -/* extsw & extsw. */ -GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B); - -/* cntlzd */ -static void gen_cntlzd(DisasContext *ctx) -{ - tcg_gen_clzi_i64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], 64); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* cnttzd */ -static void gen_cnttzd(DisasContext *ctx) -{ - tcg_gen_ctzi_i64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], 64); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} -#endif - /*** Integer rotate ***/ /* rlwimi & rlwimi. */ @@ -5942,30 +5622,9 @@ GEN_HANDLER_E(brw, 0x1F, 0x1B, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA310), GEN_HANDLER_E(brh, 0x1F, 0x1B, 0x06, 0x0000F801, PPC_NONE, PPC2_ISA310), #endif GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE), -GEN_HANDLER_E(cmpb, 0x1F, 0x1C, 0x0F, 0x00000001, PPC_NONE, PPC2_ISA205), -GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER), -GEN_HANDLER_E(cnttzw, 0x1F, 0x1A, 0x10, 0x00000000, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(copy, 0x1F, 0x06, 0x18, 0x03C00001, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(cp_abort, 0x1F, 0x06, 0x1A, 0x03FFF801, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(paste, 0x1F, 0x06, 0x1C, 0x03C00000, PPC_NONE, PPC2_ISA300), -GEN_HANDLER(or, 0x1F, 0x1C, 0x0D, 0x00000000, PPC_INTEGER), -GEN_HANDLER(xor, 0x1F, 0x1C, 0x09, 0x00000000, PPC_INTEGER), -GEN_HANDLER(ori, 0x18, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(oris, 0x19, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(xori, 0x1A, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(xoris, 0x1B, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(popcntb, 0x1F, 0x1A, 0x03, 0x0000F801, PPC_POPCNTB), -GEN_HANDLER(popcntw, 0x1F, 0x1A, 0x0b, 0x0000F801, PPC_POPCNTWD), -GEN_HANDLER_E(prtyw, 0x1F, 0x1A, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA205), -#if defined(TARGET_PPC64) -GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD), -GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B), -GEN_HANDLER_E(cnttzd, 0x1F, 0x1A, 0x11, 0x00000000, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(prtyd, 0x1F, 0x1A, 0x05, 0x0000F801, PPC_NONE, PPC2_ISA205), -GEN_HANDLER_E(bpermd, 0x1F, 0x1C, 0x07, 0x00000001, PPC_NONE, PPC2_PERM_ISA206), -#endif GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(rlwinm, 0x15, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(rlwnm, 0x17, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), @@ -6136,24 +5795,6 @@ GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), -#undef GEN_LOGICAL1 -#undef GEN_LOGICAL2 -#define GEN_LOGICAL2(name, tcg_op, opc, type) \ -GEN_HANDLER(name, 0x1F, 0x1C, opc, 0x00000000, type) -#define GEN_LOGICAL1(name, tcg_op, opc, type) \ -GEN_HANDLER(name, 0x1F, 0x1A, opc, 0x00000000, type) -GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER), -GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER), -GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER), -GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER), -GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER), -GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER), -GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER), -GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER), -#if defined(TARGET_PPC64) -GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B), -#endif - #if defined(TARGET_PPC64) #undef GEN_PPC64_R2 #undef GEN_PPC64_R4 diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc index 872fed664d..fa0191e866 100644 --- a/target/ppc/translate/fixedpoint-impl.c.inc +++ b/target/ppc/translate/fixedpoint-impl.c.inc @@ -856,6 +856,285 @@ TRANS(SETBCR, do_set_bool_cond, false, true) TRANS(SETNBC, do_set_bool_cond, true, false) TRANS(SETNBCR, do_set_bool_cond, true, true) +/* + * Fixed-Point Logical Instructions + */ + +static bool do_addi_(DisasContext *ctx, arg_D_ui *a, bool shift) +{ + tcg_gen_andi_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], shift ? a->ui << 16 : a->ui); + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + return true; +} + +static bool do_ori(DisasContext *ctx, arg_D_ui *a, bool shift) +{ + if (a->rt == a->ra && a->ui == 0) { + /* NOP */ + return true; + } + tcg_gen_ori_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], shift ? a->ui << 16 : a->ui); + return true; +} + +static bool do_xori(DisasContext *ctx, arg_D_ui *a, bool shift) +{ + if (a->rt == a->ra && a->ui == 0) { + /* NOP */ + return true; + } + tcg_gen_xori_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], shift ? a->ui << 16 : a->ui); + return true; +} + +static bool do_logical1(DisasContext *ctx, arg_X_sa_rc *a, + void (*helper)(TCGv, TCGv)) +{ + helper(cpu_gpr[a->ra], cpu_gpr[a->rs]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +static bool do_logical2(DisasContext *ctx, arg_X_rc *a, + void (*helper)(TCGv, TCGv, TCGv)) +{ + helper(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +static bool trans_OR(DisasContext *ctx, arg_OR *a) +{ + /* Optimisation for mr. ri case */ + if (a->rt != a->ra || a->rt != a->rb) { + if (a->rt != a->rb) { + tcg_gen_or_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + } else { + tcg_gen_mov_tl(cpu_gpr[a->ra], cpu_gpr[a->rt]); + } + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + } else if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); +#if defined(TARGET_PPC64) + } else if (a->rt != 0) { /* 0 is nop */ + int prio = 0; + + switch (a->rt) { + case 1: + /* Set process priority to low */ + prio = 2; + break; + case 6: + /* Set process priority to medium-low */ + prio = 3; + break; + case 2: + /* Set process priority to normal */ + prio = 4; + break; +#if !defined(CONFIG_USER_ONLY) + case 31: + if (!ctx->pr) { + /* Set process priority to very low */ + prio = 1; + } + break; + case 5: + if (!ctx->pr) { + /* Set process priority to medium-hight */ + prio = 5; + } + break; + case 3: + if (!ctx->pr) { + /* Set process priority to high */ + prio = 6; + } + break; + case 7: + if (ctx->hv && !ctx->pr) { + /* Set process priority to very high */ + prio = 7; + } + break; +#endif + default: + break; + } + if (prio) { + TCGv t0 = tcg_temp_new(); + gen_load_spr(t0, SPR_PPR); + tcg_gen_andi_tl(t0, t0, ~0x001C000000000000ULL); + tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50); + gen_store_spr(SPR_PPR, t0); + } +#if !defined(CONFIG_USER_ONLY) + /* + * Pause out of TCG otherwise spin loops with smt_low eat too + * much CPU and the kernel hangs. This applies to all + * encodings other than no-op, e.g., miso(rs=26), yield(27), + * mdoio(29), mdoom(30), and all currently undefined. + */ + gen_pause(ctx); +#endif +#endif + } + + return true; +} + +static bool trans_XOR(DisasContext *ctx, arg_XOR *a) +{ + /* Optimisation for "set to zero" case */ + if (a->rt != a->rb) { + tcg_gen_xor_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + } else { + tcg_gen_movi_tl(cpu_gpr[a->ra], 0); + } + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +static bool trans_CMPB(DisasContext *ctx, arg_CMPB *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + gen_helper_CMPB(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + return true; +} + +static bool do_cntzw(DisasContext *ctx, arg_X_sa_rc *a, + void (*helper)(TCGv_i32, TCGv_i32, uint32_t)) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t, cpu_gpr[a->rs]); + helper(t, t, 32); + tcg_gen_extu_i32_tl(cpu_gpr[a->ra], t); + + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +#if defined(TARGET_PPC64) +static bool do_cntzd(DisasContext *ctx, arg_X_sa_rc *a, + void (*helper)(TCGv_i64, TCGv_i64, uint64_t)) +{ + helper(cpu_gpr[a->ra], cpu_gpr[a->rs], 64); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} +#endif + +static bool trans_CNTLZD(DisasContext *ctx, arg_CNTLZD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + do_cntzd(ctx, a, tcg_gen_clzi_i64); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_CNTTZD(DisasContext *ctx, arg_CNTTZD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + do_cntzd(ctx, a, tcg_gen_ctzi_i64); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_POPCNTB(DisasContext *ctx, arg_POPCNTB *a) +{ + REQUIRE_INSNS_FLAGS(ctx, POPCNTB); + gen_helper_POPCNTB(cpu_gpr[a->ra], cpu_gpr[a->rs]); + return true; +} + +static bool trans_POPCNTW(DisasContext *ctx, arg_POPCNTW *a) +{ + REQUIRE_INSNS_FLAGS(ctx, POPCNTWD); +#if defined(TARGET_PPC64) + gen_helper_POPCNTW(cpu_gpr[a->ra], cpu_gpr[a->rs]); +#else + tcg_gen_ctpop_i32(cpu_gpr[a->ra], cpu_gpr[a->rs]); +#endif + return true; +} + +static bool trans_POPCNTD(DisasContext *ctx, arg_POPCNTD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, POPCNTWD); +#if defined(TARGET_PPC64) + tcg_gen_ctpop_i64(cpu_gpr[a->ra], cpu_gpr[a->rs]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_PRTYW(DisasContext *ctx, arg_PRTYW *a) +{ + TCGv ra = cpu_gpr[a->ra]; + TCGv rs = cpu_gpr[a->rs]; + TCGv t0 = tcg_temp_new(); + + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + tcg_gen_shri_tl(t0, rs, 16); + tcg_gen_xor_tl(ra, rs, t0); + tcg_gen_shri_tl(t0, ra, 8); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_andi_tl(ra, ra, (target_ulong)0x100000001ULL); + return true; +} + +static bool trans_PRTYD(DisasContext *ctx, arg_PRTYD *a) +{ + TCGv ra = cpu_gpr[a->ra]; + TCGv rs = cpu_gpr[a->rs]; + TCGv t0 = tcg_temp_new(); + + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + tcg_gen_shri_tl(t0, rs, 32); + tcg_gen_xor_tl(ra, rs, t0); + tcg_gen_shri_tl(t0, ra, 16); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_shri_tl(t0, ra, 8); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_andi_tl(ra, ra, 1); + return true; +} + +static bool trans_BPERMD(DisasContext *ctx, arg_BPERMD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, PERM_ISA206); +#if defined(TARGET_PPC64) + gen_helper_BPERMD(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_CFUGED(DisasContext *ctx, arg_X *a) { REQUIRE_64BIT(ctx); @@ -944,6 +1223,27 @@ static bool trans_PEXTD(DisasContext *ctx, arg_X *a) return true; } +TRANS(ANDI_, do_addi_, false); +TRANS(ANDIS_, do_addi_, true); +TRANS(ORI, do_ori, false); +TRANS(ORIS, do_ori, true); +TRANS(XORI, do_xori, false); +TRANS(XORIS, do_xori, true); + +TRANS(AND, do_logical2, tcg_gen_and_tl); +TRANS(ANDC, do_logical2, tcg_gen_andc_tl); +TRANS(NAND, do_logical2, tcg_gen_nand_tl); +TRANS(ORC, do_logical2, tcg_gen_orc_tl); +TRANS(NOR, do_logical2, tcg_gen_nor_tl); +TRANS(EQV, do_logical2, tcg_gen_eqv_tl); +TRANS(EXTSB, do_logical1, tcg_gen_ext8s_tl); +TRANS(EXTSH, do_logical1, tcg_gen_ext16s_tl); + +TRANS(CNTLZW, do_cntzw, tcg_gen_clzi_i32); +TRANS_FLAGS2(ISA300, CNTTZW, do_cntzw, tcg_gen_ctzi_i32); + +TRANS64(EXTSW, do_logical1, tcg_gen_ext32s_tl); + static bool trans_ADDG6S(DisasContext *ctx, arg_X *a) { const target_ulong carry_bits = (target_ulong)-1 / 0xf; From 21b5f5464f97f68f025c86330146d038d2ee79ad Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Mon, 29 Apr 2024 10:43:15 +0530 Subject: [PATCH 19/72] target/ppc: Move VMX storage access instructions to decodetree Moving the following instructions to decodetree specification : {l,st}ve{b,h,w}x, {l,st}v{x,xl}, lvs{l,r} : X-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured using the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 12 +- target/ppc/insn32.decode | 17 +++ target/ppc/mem_helper.c | 12 +- target/ppc/translate.c | 2 - target/ppc/translate/vmx-impl.c.inc | 219 ++++++++++++---------------- target/ppc/translate/vmx-ops.c.inc | 19 --- 6 files changed, 119 insertions(+), 162 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 4267917615..6d6f31366c 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -267,12 +267,12 @@ DEF_HELPER_5(VMSUMSHS, void, env, avr, avr, avr, avr) DEF_HELPER_FLAGS_5(VMLADDUHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_FLAGS_2(mtvscr, TCG_CALL_NO_RWG, void, env, i32) DEF_HELPER_FLAGS_1(mfvscr, TCG_CALL_NO_RWG, i32, env) -DEF_HELPER_3(lvebx, void, env, avr, tl) -DEF_HELPER_3(lvehx, void, env, avr, tl) -DEF_HELPER_3(lvewx, void, env, avr, tl) -DEF_HELPER_3(stvebx, void, env, avr, tl) -DEF_HELPER_3(stvehx, void, env, avr, tl) -DEF_HELPER_3(stvewx, void, env, avr, tl) +DEF_HELPER_3(LVEBX, void, env, avr, tl) +DEF_HELPER_3(LVEHX, void, env, avr, tl) +DEF_HELPER_3(LVEWX, void, env, avr, tl) +DEF_HELPER_3(STVEBX, void, env, avr, tl) +DEF_HELPER_3(STVEHX, void, env, avr, tl) +DEF_HELPER_3(STVEWX, void, env, avr, tl) #if defined(TARGET_PPC64) DEF_HELPER_4(lxvl, void, env, tl, vsr, tl) DEF_HELPER_4(lxvll, void, env, tl, vsr, tl) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index dc62bc90aa..11be21d230 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -661,6 +661,23 @@ DSCRIQ 111111 ..... ..... ...... 001100010 . @Z22_tap_sh_rc VPMSUMD 000100 ..... ..... ..... 10011001000 @VX +## Vector Load/Store Instructions + +LVEBX 011111 ..... ..... ..... 0000000111 - @X +LVEHX 011111 ..... ..... ..... 0000100111 - @X +LVEWX 011111 ..... ..... ..... 0001000111 - @X +LVX 011111 ..... ..... ..... 0001100111 - @X +LVXL 011111 ..... ..... ..... 0101100111 - @X + +STVEBX 011111 ..... ..... ..... 0010000111 - @X +STVEHX 011111 ..... ..... ..... 0010100111 - @X +STVEWX 011111 ..... ..... ..... 0011000111 - @X +STVX 011111 ..... ..... ..... 0011100111 - @X +STVXL 011111 ..... ..... ..... 0111100111 - @X + +LVSL 011111 ..... ..... ..... 0000000110 - @X +LVSR 011111 ..... ..... ..... 0000100110 - @X + ## Vector Integer Instructions VCMPEQUB 000100 ..... ..... ..... . 0000000110 @VC diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index ea7e8443a8..f88155ad45 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -404,9 +404,9 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, } \ } #define I(x) (x) -LVE(lvebx, cpu_ldub_data_ra, I, u8) -LVE(lvehx, cpu_lduw_data_ra, bswap16, u16) -LVE(lvewx, cpu_ldl_data_ra, bswap32, u32) +LVE(LVEBX, cpu_ldub_data_ra, I, u8) +LVE(LVEHX, cpu_lduw_data_ra, bswap16, u16) +LVE(LVEWX, cpu_ldl_data_ra, bswap32, u32) #undef I #undef LVE @@ -432,9 +432,9 @@ LVE(lvewx, cpu_ldl_data_ra, bswap32, u32) } \ } #define I(x) (x) -STVE(stvebx, cpu_stb_data_ra, I, u8) -STVE(stvehx, cpu_stw_data_ra, bswap16, u16) -STVE(stvewx, cpu_stl_data_ra, bswap32, u32) +STVE(STVEBX, cpu_stb_data_ra, I, u8) +STVE(STVEHX, cpu_stw_data_ra, bswap16, u16) +STVE(STVEWX, cpu_stl_data_ra, bswap32, u32) #undef I #undef LVE diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 2cfa7d37ee..2c39605273 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -5790,8 +5790,6 @@ GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_440_SPEC), -GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), -GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC), diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index b56e615c24..4d5e743cfe 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -14,125 +14,88 @@ static inline TCGv_ptr gen_avr_ptr(int reg) return r; } -#define GEN_VR_LDX(name, opc2, opc3) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 avr; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - avr = tcg_temp_new_i64(); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - tcg_gen_andi_tl(EA, EA, ~0xf); \ - /* \ - * We only need to swap high and low halves. gen_qemu_ld64_i64 \ - * does necessary 64-bit byteswap already. \ - */ \ - if (ctx->le_mode) { \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, false); \ - tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, true); \ - } else { \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, true); \ - tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, false); \ - } \ +static bool trans_LVX(DisasContext *ctx, arg_X *a) +{ + TCGv EA; + TCGv_i64 avr; + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + gen_set_access_type(ctx, ACCESS_INT); + avr = tcg_temp_new_i64(); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + tcg_gen_andi_tl(EA, EA, ~0xf); + /* + * We only need to swap high and low halves. gen_qemu_ld64_i64 + * does necessary 64-bit byteswap already. + */ + gen_qemu_ld64_i64(ctx, avr, EA); + set_avr64(a->rt, avr, !ctx->le_mode); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_ld64_i64(ctx, avr, EA); + set_avr64(a->rt, avr, ctx->le_mode); + return true; } -#define GEN_VR_STX(name, opc2, opc3) \ -static void gen_st##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 avr; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - avr = tcg_temp_new_i64(); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - tcg_gen_andi_tl(EA, EA, ~0xf); \ - /* \ - * We only need to swap high and low halves. gen_qemu_st64_i64 \ - * does necessary 64-bit byteswap already. \ - */ \ - if (ctx->le_mode) { \ - get_avr64(avr, rD(ctx->opcode), false); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - tcg_gen_addi_tl(EA, EA, 8); \ - get_avr64(avr, rD(ctx->opcode), true); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - } else { \ - get_avr64(avr, rD(ctx->opcode), true); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - tcg_gen_addi_tl(EA, EA, 8); \ - get_avr64(avr, rD(ctx->opcode), false); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - } \ -} - -#define GEN_VR_LVE(name, opc2, opc3, size) \ -static void gen_lve##name(DisasContext *ctx) \ - { \ - TCGv EA; \ - TCGv_ptr rs; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - if (size > 1) { \ - tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ - } \ - rs = gen_avr_ptr(rS(ctx->opcode)); \ - gen_helper_lve##name(tcg_env, rs, EA); \ - } - -#define GEN_VR_STVE(name, opc2, opc3, size) \ -static void gen_stve##name(DisasContext *ctx) \ - { \ - TCGv EA; \ - TCGv_ptr rs; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - if (size > 1) { \ - tcg_gen_andi_tl(EA, EA, ~(size - 1)); \ - } \ - rs = gen_avr_ptr(rS(ctx->opcode)); \ - gen_helper_stve##name(tcg_env, rs, EA); \ - } - -GEN_VR_LDX(lvx, 0x07, 0x03); /* As we don't emulate the cache, lvxl is strictly equivalent to lvx */ -GEN_VR_LDX(lvxl, 0x07, 0x0B); +QEMU_FLATTEN +static bool trans_LVXL(DisasContext *ctx, arg_LVXL *a) +{ + return trans_LVX(ctx, a); +} -GEN_VR_LVE(bx, 0x07, 0x00, 1); -GEN_VR_LVE(hx, 0x07, 0x01, 2); -GEN_VR_LVE(wx, 0x07, 0x02, 4); +static bool trans_STVX(DisasContext *ctx, arg_STVX *a) +{ + TCGv EA; + TCGv_i64 avr; + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + gen_set_access_type(ctx, ACCESS_INT); + avr = tcg_temp_new_i64(); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + tcg_gen_andi_tl(EA, EA, ~0xf); + /* + * We only need to swap high and low halves. gen_qemu_st64_i64 + * does necessary 64-bit byteswap already. + */ + get_avr64(avr, a->rt, !ctx->le_mode); + gen_qemu_st64_i64(ctx, avr, EA); + tcg_gen_addi_tl(EA, EA, 8); + get_avr64(avr, a->rt, ctx->le_mode); + gen_qemu_st64_i64(ctx, avr, EA); + return true; +} -GEN_VR_STX(svx, 0x07, 0x07); /* As we don't emulate the cache, stvxl is strictly equivalent to stvx */ -GEN_VR_STX(svxl, 0x07, 0x0F); +QEMU_FLATTEN +static bool trans_STVXL(DisasContext *ctx, arg_STVXL *a) +{ + return trans_STVX(ctx, a); +} -GEN_VR_STVE(bx, 0x07, 0x04, 1); -GEN_VR_STVE(hx, 0x07, 0x05, 2); -GEN_VR_STVE(wx, 0x07, 0x06, 4); +static bool do_ldst_ve_X(DisasContext *ctx, arg_X *a, int size, + void (*helper)(TCGv_env, TCGv_ptr, TCGv)) +{ + TCGv EA; + TCGv_ptr vrt; + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + if (size > 1) { + tcg_gen_andi_tl(EA, EA, ~(size - 1)); + } + vrt = gen_avr_ptr(a->rt); + helper(tcg_env, vrt, EA); + return true; +} + +TRANS(LVEBX, do_ldst_ve_X, 1, gen_helper_LVEBX); +TRANS(LVEHX, do_ldst_ve_X, 2, gen_helper_LVEHX); +TRANS(LVEWX, do_ldst_ve_X, 4, gen_helper_LVEWX); + +TRANS(STVEBX, do_ldst_ve_X, 1, gen_helper_STVEBX); +TRANS(STVEHX, do_ldst_ve_X, 2, gen_helper_STVEHX); +TRANS(STVEWX, do_ldst_ve_X, 4, gen_helper_STVEWX); static void gen_mfvscr(DisasContext *ctx) { @@ -460,15 +423,17 @@ static void trans_vmrgow(DisasContext *ctx) * Let X be the 32-byte value 0x00 || 0x01 || 0x02 || ... || 0x1E || 0x1F. * Bytes sh:sh+15 of X are placed into vD. */ -static void trans_lvsl(DisasContext *ctx) +static bool trans_LVSL(DisasContext *ctx, arg_LVSL *a) { - int VT = rD(ctx->opcode); TCGv_i64 result = tcg_temp_new_i64(); TCGv_i64 sh = tcg_temp_new_i64(); TCGv EA = tcg_temp_new(); + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + /* Get sh(from description) by anding EA with 0xf. */ - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_extu_tl_i64(sh, EA); tcg_gen_andi_i64(sh, sh, 0xfULL); @@ -478,13 +443,14 @@ static void trans_lvsl(DisasContext *ctx) */ tcg_gen_muli_i64(sh, sh, 0x0101010101010101ULL); tcg_gen_addi_i64(result, sh, 0x0001020304050607ull); - set_avr64(VT, result, true); + set_avr64(a->rt, result, true); /* * Create bytes sh+8:sh+15 of X(from description) and place them in * lower doubleword of vD. */ tcg_gen_addi_i64(result, sh, 0x08090a0b0c0d0e0fULL); - set_avr64(VT, result, false); + set_avr64(a->rt, result, false); + return true; } /* @@ -494,16 +460,17 @@ static void trans_lvsl(DisasContext *ctx) * Let X be the 32-byte value 0x00 || 0x01 || 0x02 || ... || 0x1E || 0x1F. * Bytes (16-sh):(31-sh) of X are placed into vD. */ -static void trans_lvsr(DisasContext *ctx) +static bool trans_LVSR(DisasContext *ctx, arg_LVSR *a) { - int VT = rD(ctx->opcode); TCGv_i64 result = tcg_temp_new_i64(); TCGv_i64 sh = tcg_temp_new_i64(); TCGv EA = tcg_temp_new(); + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); /* Get sh(from description) by anding EA with 0xf. */ - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_extu_tl_i64(sh, EA); tcg_gen_andi_i64(sh, sh, 0xfULL); @@ -513,13 +480,14 @@ static void trans_lvsr(DisasContext *ctx) */ tcg_gen_muli_i64(sh, sh, 0x0101010101010101ULL); tcg_gen_subfi_i64(result, 0x1011121314151617ULL, sh); - set_avr64(VT, result, true); + set_avr64(a->rt, result, true); /* * Create bytes (24-sh):(32-sh) of X(from description) and place them in * lower doubleword of vD. */ tcg_gen_subfi_i64(result, 0x18191a1b1c1d1e1fULL, sh); - set_avr64(VT, result, false); + set_avr64(a->rt, result, false); + return true; } /* @@ -1158,8 +1126,6 @@ GEN_VXFORM_TRANS_DUAL(vmrgow, PPC_NONE, PPC2_ALTIVEC_207, GEN_VXFORM_HETRO(vextubrx, 6, 28) GEN_VXFORM_HETRO(vextuhrx, 6, 29) GEN_VXFORM_HETRO(vextuwrx, 6, 30) -GEN_VXFORM_TRANS(lvsl, 6, 31) -GEN_VXFORM_TRANS(lvsr, 6, 32) GEN_VXFORM_TRANS_DUAL(vmrgew, PPC_NONE, PPC2_ALTIVEC_207, vextuwrx, PPC_NONE, PPC2_ISA300) @@ -3365,11 +3331,6 @@ TRANS_FLAGS2(ISA310, VMODUQ, do_vx_helper, gen_helper_VMODUQ) #undef DIVS64 #undef DIVU64 -#undef GEN_VR_LDX -#undef GEN_VR_STX -#undef GEN_VR_LVE -#undef GEN_VR_STVE - #undef GEN_VX_LOGICAL #undef GEN_VX_LOGICAL_207 #undef GEN_VXFORM diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 33fec8aca4..672fba3796 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -1,22 +1,3 @@ -#define GEN_VR_LDX(name, opc2, opc3) \ -GEN_HANDLER(name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -#define GEN_VR_STX(name, opc2, opc3) \ -GEN_HANDLER(st##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -#define GEN_VR_LVE(name, opc2, opc3) \ - GEN_HANDLER(lve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -#define GEN_VR_STVE(name, opc2, opc3) \ - GEN_HANDLER(stve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -GEN_VR_LDX(lvx, 0x07, 0x03), -GEN_VR_LDX(lvxl, 0x07, 0x0B), -GEN_VR_LVE(bx, 0x07, 0x00), -GEN_VR_LVE(hx, 0x07, 0x01), -GEN_VR_LVE(wx, 0x07, 0x02), -GEN_VR_STX(svx, 0x07, 0x07), -GEN_VR_STX(svxl, 0x07, 0x0F), -GEN_VR_STVE(bx, 0x07, 0x04), -GEN_VR_STVE(hx, 0x07, 0x05), -GEN_VR_STVE(wx, 0x07, 0x06), - #define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) From 664eb39ec94d6fc6e8a96949b0a27deb0c32f50d Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Mon, 29 Apr 2024 10:43:16 +0530 Subject: [PATCH 20/72] target/ppc: Move VMX integer logical instructions to decodetree. Moving the following instructions to decodetree specification: v{and, andc, nand, or, orc, nor, xor, eqv} : VX-form The changes were verified by validating that the tcp ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 11 +++++++++++ target/ppc/translate/vmx-impl.c.inc | 22 ++++++++++------------ target/ppc/translate/vmx-ops.c.inc | 15 --------------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 11be21d230..16f3711073 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -709,6 +709,17 @@ VCMPNEZW 000100 ..... ..... ..... . 0110000111 @VC VCMPSQ 000100 ... -- ..... ..... 00101000001 @VX_bf VCMPUQ 000100 ... -- ..... ..... 00100000001 @VX_bf +## Vector Integer Logical Instructions + +VAND 000100 ..... ..... ..... 10000000100 @VX +VANDC 000100 ..... ..... ..... 10001000100 @VX +VNAND 000100 ..... ..... ..... 10110000100 @VX +VOR 000100 ..... ..... ..... 10010000100 @VX +VORC 000100 ..... ..... ..... 10101000100 @VX +VNOR 000100 ..... ..... ..... 10100000100 @VX +VXOR 000100 ..... ..... ..... 10011000100 @VX +VEQV 000100 ..... ..... ..... 11010000100 @VX + ## Vector Integer Average Instructions VAVGSB 000100 ..... ..... ..... 10100000010 @VX diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 4d5e743cfe..cefe04127c 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -205,16 +205,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ 16, 16); \ } -/* Logical operations */ -GEN_VXFORM_V(vand, MO_64, tcg_gen_gvec_and, 2, 16); -GEN_VXFORM_V(vandc, MO_64, tcg_gen_gvec_andc, 2, 17); -GEN_VXFORM_V(vor, MO_64, tcg_gen_gvec_or, 2, 18); -GEN_VXFORM_V(vxor, MO_64, tcg_gen_gvec_xor, 2, 19); -GEN_VXFORM_V(vnor, MO_64, tcg_gen_gvec_nor, 2, 20); -GEN_VXFORM_V(veqv, MO_64, tcg_gen_gvec_eqv, 2, 26); -GEN_VXFORM_V(vnand, MO_64, tcg_gen_gvec_nand, 2, 22); -GEN_VXFORM_V(vorc, MO_64, tcg_gen_gvec_orc, 2, 21); - #define GEN_VXFORM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ @@ -727,6 +717,16 @@ TRANS_FLAGS(ALTIVEC, VRLH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_rotlv) TRANS_FLAGS(ALTIVEC, VRLW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_rotlv) TRANS_FLAGS2(ALTIVEC_207, VRLD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_rotlv) +/* Logical operations */ +TRANS_FLAGS(ALTIVEC, VAND, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_and); +TRANS_FLAGS(ALTIVEC, VANDC, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_andc); +TRANS_FLAGS(ALTIVEC, VOR, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_or); +TRANS_FLAGS(ALTIVEC, VXOR, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_xor); +TRANS_FLAGS(ALTIVEC, VNOR, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_nor); +TRANS_FLAGS2(ALTIVEC_207, VEQV, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_eqv); +TRANS_FLAGS2(ALTIVEC_207, VNAND, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_nand); +TRANS_FLAGS2(ALTIVEC_207, VORC, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_orc); + static TCGv_vec do_vrl_mask_vec(unsigned vece, TCGv_vec vrb) { TCGv_vec t0 = tcg_temp_new_vec_matching(vrb), @@ -3331,8 +3331,6 @@ TRANS_FLAGS2(ISA310, VMODUQ, do_vx_helper, gen_helper_VMODUQ) #undef DIVS64 #undef DIVU64 -#undef GEN_VX_LOGICAL -#undef GEN_VX_LOGICAL_207 #undef GEN_VXFORM #undef GEN_VXFORM_207 #undef GEN_VXFORM_DUAL diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 672fba3796..80c5217749 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -1,18 +1,3 @@ -#define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ -GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) - -#define GEN_VX_LOGICAL_207(name, tcg_op, opc2, opc3) \ -GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ALTIVEC_207) - -GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16), -GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17), -GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18), -GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19), -GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20), -GEN_VX_LOGICAL_207(veqv, tcg_gen_eqv_i64, 2, 26), -GEN_VX_LOGICAL_207(vnand, tcg_gen_nand_i64, 2, 22), -GEN_VX_LOGICAL_207(vorc, tcg_gen_orc_i64, 2, 21), - #define GEN_VXFORM(name, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) From 687a30ad3c0f219bb372d806575eb47fae0cd27c Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Mon, 29 Apr 2024 10:43:17 +0530 Subject: [PATCH 21/72] target/ppc: Move VMX integer max/min instructions to decodetree. Moving the following instructions to decodetree specification : v{max, min}{u, s}{b, h, w, d} : VX-form The changes were verified by validating that the tcg ops generated by those instructions remain the same, which were captured with the '-d in_asm,op' flag. Reviewed-by: Richard Henderson Signed-off-by: Chinmay Rath Signed-off-by: Nicholas Piggin --- target/ppc/insn32.decode | 22 +++++++++++++++++ target/ppc/translate/vmx-impl.c.inc | 37 ++++++++++++++++------------- target/ppc/translate/vmx-ops.c.inc | 16 ------------- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 16f3711073..05c1d8c12d 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -852,6 +852,28 @@ VEXTSD2Q 000100 ..... 11011 ..... 11000000010 @VX_tb VNEGD 000100 ..... 00111 ..... 11000000010 @VX_tb VNEGW 000100 ..... 00110 ..... 11000000010 @VX_tb +## Vector Integer Maximum/Minimum Instructions + +VMAXUB 000100 ..... ..... ..... 00000000010 @VX +VMAXUH 000100 ..... ..... ..... 00001000010 @VX +VMAXUW 000100 ..... ..... ..... 00010000010 @VX +VMAXUD 000100 ..... ..... ..... 00011000010 @VX + +VMAXSB 000100 ..... ..... ..... 00100000010 @VX +VMAXSH 000100 ..... ..... ..... 00101000010 @VX +VMAXSW 000100 ..... ..... ..... 00110000010 @VX +VMAXSD 000100 ..... ..... ..... 00111000010 @VX + +VMINUB 000100 ..... ..... ..... 01000000010 @VX +VMINUH 000100 ..... ..... ..... 01001000010 @VX +VMINUW 000100 ..... ..... ..... 01010000010 @VX +VMINUD 000100 ..... ..... ..... 01011000010 @VX + +VMINSB 000100 ..... ..... ..... 01100000010 @VX +VMINSH 000100 ..... ..... ..... 01101000010 @VX +VMINSW 000100 ..... ..... ..... 01110000010 @VX +VMINSD 000100 ..... ..... ..... 01111000010 @VX + ## Vector Mask Manipulation Instructions MTVSRBM 000100 ..... 10000 ..... 11001000010 @VX_tb diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index cefe04127c..8084af75cc 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -342,22 +342,6 @@ GEN_VXFORM_V(vsububm, MO_8, tcg_gen_gvec_sub, 0, 16); GEN_VXFORM_V(vsubuhm, MO_16, tcg_gen_gvec_sub, 0, 17); GEN_VXFORM_V(vsubuwm, MO_32, tcg_gen_gvec_sub, 0, 18); GEN_VXFORM_V(vsubudm, MO_64, tcg_gen_gvec_sub, 0, 19); -GEN_VXFORM_V(vmaxub, MO_8, tcg_gen_gvec_umax, 1, 0); -GEN_VXFORM_V(vmaxuh, MO_16, tcg_gen_gvec_umax, 1, 1); -GEN_VXFORM_V(vmaxuw, MO_32, tcg_gen_gvec_umax, 1, 2); -GEN_VXFORM_V(vmaxud, MO_64, tcg_gen_gvec_umax, 1, 3); -GEN_VXFORM_V(vmaxsb, MO_8, tcg_gen_gvec_smax, 1, 4); -GEN_VXFORM_V(vmaxsh, MO_16, tcg_gen_gvec_smax, 1, 5); -GEN_VXFORM_V(vmaxsw, MO_32, tcg_gen_gvec_smax, 1, 6); -GEN_VXFORM_V(vmaxsd, MO_64, tcg_gen_gvec_smax, 1, 7); -GEN_VXFORM_V(vminub, MO_8, tcg_gen_gvec_umin, 1, 8); -GEN_VXFORM_V(vminuh, MO_16, tcg_gen_gvec_umin, 1, 9); -GEN_VXFORM_V(vminuw, MO_32, tcg_gen_gvec_umin, 1, 10); -GEN_VXFORM_V(vminud, MO_64, tcg_gen_gvec_umin, 1, 11); -GEN_VXFORM_V(vminsb, MO_8, tcg_gen_gvec_smin, 1, 12); -GEN_VXFORM_V(vminsh, MO_16, tcg_gen_gvec_smin, 1, 13); -GEN_VXFORM_V(vminsw, MO_32, tcg_gen_gvec_smin, 1, 14); -GEN_VXFORM_V(vminsd, MO_64, tcg_gen_gvec_smin, 1, 15); GEN_VXFORM(vmrghb, 6, 0); GEN_VXFORM(vmrghh, 6, 1); GEN_VXFORM(vmrghw, 6, 2); @@ -727,6 +711,27 @@ TRANS_FLAGS2(ALTIVEC_207, VEQV, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_eqv); TRANS_FLAGS2(ALTIVEC_207, VNAND, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_nand); TRANS_FLAGS2(ALTIVEC_207, VORC, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_orc); +/* Integer Max/Min operations */ +TRANS_FLAGS(ALTIVEC, VMAXUB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_umax); +TRANS_FLAGS(ALTIVEC, VMAXUH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_umax); +TRANS_FLAGS(ALTIVEC, VMAXUW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_umax); +TRANS_FLAGS2(ALTIVEC_207, VMAXUD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_umax); + +TRANS_FLAGS(ALTIVEC, VMAXSB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_smax); +TRANS_FLAGS(ALTIVEC, VMAXSH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_smax); +TRANS_FLAGS(ALTIVEC, VMAXSW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_smax); +TRANS_FLAGS2(ALTIVEC_207, VMAXSD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_smax); + +TRANS_FLAGS(ALTIVEC, VMINUB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_umin); +TRANS_FLAGS(ALTIVEC, VMINUH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_umin); +TRANS_FLAGS(ALTIVEC, VMINUW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_umin); +TRANS_FLAGS2(ALTIVEC_207, VMINUD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_umin); + +TRANS_FLAGS(ALTIVEC, VMINSB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_smin); +TRANS_FLAGS(ALTIVEC, VMINSH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_smin); +TRANS_FLAGS(ALTIVEC, VMINSW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_smin); +TRANS_FLAGS2(ALTIVEC_207, VMINSD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_smin); + static TCGv_vec do_vrl_mask_vec(unsigned vece, TCGv_vec vrb) { TCGv_vec t0 = tcg_temp_new_vec_matching(vrb), diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 80c5217749..7bb11b0549 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -33,22 +33,6 @@ GEN_VXFORM_DUAL(vsubuhm, bcdsub, 0, 17, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM_DUAL(vsubuwm, bcdus, 0, 18, PPC_ALTIVEC, PPC2_ISA300), GEN_VXFORM_DUAL(vsubudm, bcds, 0, 19, PPC2_ALTIVEC_207, PPC2_ISA300), GEN_VXFORM_300(bcds, 0, 27), -GEN_VXFORM(vmaxub, 1, 0), -GEN_VXFORM(vmaxuh, 1, 1), -GEN_VXFORM(vmaxuw, 1, 2), -GEN_VXFORM_207(vmaxud, 1, 3), -GEN_VXFORM(vmaxsb, 1, 4), -GEN_VXFORM(vmaxsh, 1, 5), -GEN_VXFORM(vmaxsw, 1, 6), -GEN_VXFORM_207(vmaxsd, 1, 7), -GEN_VXFORM(vminub, 1, 8), -GEN_VXFORM(vminuh, 1, 9), -GEN_VXFORM(vminuw, 1, 10), -GEN_VXFORM_207(vminud, 1, 11), -GEN_VXFORM(vminsb, 1, 12), -GEN_VXFORM(vminsh, 1, 13), -GEN_VXFORM(vminsw, 1, 14), -GEN_VXFORM_207(vminsd, 1, 15), GEN_VXFORM(vmrghb, 6, 0), GEN_VXFORM(vmrghh, 6, 1), GEN_VXFORM(vmrghw, 6, 2), From a7138e28a242680ae25b52ed44842cde235103f0 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 28 Mar 2024 20:41:29 +1000 Subject: [PATCH 22/72] target/ppc: Add new hflags to support BHRB This commit is preparatory to the addition of Branch History Rolling Buffer (BHRB) functionality, which is being provided today starting with the P8 processor. BHRB uses several SPR register fields to control whether or not a branch instruction's address (and sometimes target address) should be recorded. Checking each of these fields with each branch instruction using jitted code would lead to a significant decrease in performance. Therefore, it was decided that BHRB configuration bits that are not expected to change frequently should have their state summarized in an hflag so that the amount of checking done by jitted code can be reduced. This commit contains the changes for summarizing the state of the following register fields in the HFLAGS_BHRB_ENABLE hflag: MMCR0[FCP] - Determines if BHRB recording is frozen in the problem state MMCR0[FCPC] - A modifier for MMCR0[FCP] MMCRA[BHRBRD] - Disables all BHRB recording for a thread Reviewed-by: Nicholas Piggin Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 5 +++++ target/ppc/cpu_init.c | 4 ++-- target/ppc/helper.h | 1 + target/ppc/helper_regs.c | 37 ++++++++++++++++++++++++++++++++ target/ppc/machine.c | 2 +- target/ppc/power8-pmu-regs.c.inc | 5 +++++ target/ppc/power8-pmu.c | 15 +++++++++---- target/ppc/power8-pmu.h | 4 ++-- target/ppc/spr_common.h | 1 + target/ppc/translate.c | 2 ++ 10 files changed, 67 insertions(+), 9 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index a5f46d0b10..195d4be2b7 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -533,6 +533,8 @@ FIELD(MSR, LE, MSR_LE, 1) #define MMCR0_FC56 PPC_BIT(59) /* PMC Freeze Counters 5-6 bit */ #define MMCR0_PMC1CE PPC_BIT(48) /* MMCR0 PMC1 Condition Enabled */ #define MMCR0_PMCjCE PPC_BIT(49) /* MMCR0 PMCj Condition Enabled */ +#define MMCR0_FCP PPC_BIT(34) /* Freeze Counters/BHRB if PR=1 */ +#define MMCR0_FCPC PPC_BIT(51) /* Condition for FCP bit */ /* MMCR0 userspace r/w mask */ #define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE) /* MMCR2 userspace r/w mask */ @@ -545,6 +547,8 @@ FIELD(MSR, LE, MSR_LE, 1) #define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \ MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0) +#define MMCRA_BHRBRD PPC_BIT(26) /* BHRB Recording Disable */ + #define MMCR1_EVT_SIZE 8 /* extract64() does a right shift before extracting */ #define MMCR1_PMC1SEL_START 32 @@ -797,6 +801,7 @@ enum { HFLAGS_PMCJCE = 17, /* MMCR0 PMCjCE bit */ HFLAGS_PMC_OTHER = 18, /* PMC other than PMC5-6 is enabled */ HFLAGS_INSN_CNT = 19, /* PMU instruction count enabled */ + HFLAGS_BHRB_ENABLE = 20, /* Summary flag for enabling BHRB */ HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */ HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 914c6e0f18..366ea8568b 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5152,7 +5152,7 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env) KVM_REG_PPC_MMCR1, 0x00000000); spr_register_kvm(env, SPR_POWER_MMCRA, "MMCRA", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_MMCRA, KVM_REG_PPC_MMCRA, 0x00000000); spr_register_kvm(env, SPR_POWER_PMC1, "PMC1", SPR_NOACCESS, SPR_NOACCESS, @@ -7196,7 +7196,7 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) if (env->mmu_model != POWERPC_MMU_REAL) { ppc_tlb_invalidate_all(env); } - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); } /* clean any pending stop state */ diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 6d6f31366c..7b07cd1a49 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -30,6 +30,7 @@ DEF_HELPER_2(store_dawr0, void, env, tl) DEF_HELPER_2(store_dawrx0, void, env, tl) DEF_HELPER_2(store_mmcr0, void, env, tl) DEF_HELPER_2(store_mmcr1, void, env, tl) +DEF_HELPER_2(store_mmcrA, void, env, tl) DEF_HELPER_3(store_pmc, void, env, i32, i64) DEF_HELPER_2(read_pmc, tl, env, i32) DEF_HELPER_2(insns_inc, void, env, i32) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 9094ae5004..16b43702d5 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -47,6 +47,39 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env) env->tgpr[3] = tmp; } +#if defined(TARGET_PPC64) +static bool hreg_check_bhrb_enable(CPUPPCState *env) +{ + bool pr = !!(env->msr & (1 << MSR_PR)); + target_long mmcr0; + bool fcp; + bool hv; + + /* ISA 3.1 adds the PMCRA[BRHBRD] and problem state checks */ + if ((env->insns_flags2 & PPC2_ISA310) && + ((env->spr[SPR_POWER_MMCRA] & MMCRA_BHRBRD) || !pr)) { + return false; + } + + /* Check for BHRB "frozen" conditions */ + mmcr0 = env->spr[SPR_POWER_MMCR0]; + fcp = !!(mmcr0 & MMCR0_FCP); + if (mmcr0 & MMCR0_FCPC) { + hv = !!(env->msr & (1ull << MSR_HV)); + if (fcp) { + if (hv && pr) { + return false; + } + } else if (!hv && pr) { + return false; + } + } else if (fcp && pr) { + return false; + } + return true; +} +#endif + static uint32_t hreg_compute_pmu_hflags_value(CPUPPCState *env) { uint32_t hflags = 0; @@ -61,6 +94,9 @@ static uint32_t hreg_compute_pmu_hflags_value(CPUPPCState *env) if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { hflags |= 1 << HFLAGS_PMCJCE; } + if (hreg_check_bhrb_enable(env)) { + hflags |= 1 << HFLAGS_BHRB_ENABLE; + } #ifndef CONFIG_USER_ONLY if (env->pmc_ins_cnt) { @@ -85,6 +121,7 @@ static uint32_t hreg_compute_pmu_hflags_mask(CPUPPCState *env) hflags_mask |= 1 << HFLAGS_PMCJCE; hflags_mask |= 1 << HFLAGS_INSN_CNT; hflags_mask |= 1 << HFLAGS_PMC_OTHER; + hflags_mask |= 1 << HFLAGS_BHRB_ENABLE; #endif return hflags_mask; } diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 203fe28e01..6b6c31d903 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -333,7 +333,7 @@ static int cpu_post_load(void *opaque, int version_id) * triggered types (including HDEC) would need to carry more state. */ cpu_ppc_store_decr(env, env->spr[SPR_DECR]); - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); } return 0; diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc index 4956a8b350..652cf20704 100644 --- a/target/ppc/power8-pmu-regs.c.inc +++ b/target/ppc/power8-pmu-regs.c.inc @@ -175,6 +175,11 @@ void spr_write_MMCR2_ureg(DisasContext *ctx, int sprn, int gprn) gen_store_spr(SPR_POWER_MMCR2, masked_gprn); } +void spr_write_MMCRA(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_mmcrA(tcg_env, cpu_gpr[gprn]); +} + void spr_read_PMC(DisasContext *ctx, int gprn, int sprn) { TCGv_i32 t_sprn = tcg_constant_i32(sprn); diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index cbc5889d91..6f5d4e1256 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -82,7 +82,7 @@ static void pmu_update_summaries(CPUPPCState *env) env->pmc_cyc_cnt = cyc_cnt; } -void pmu_mmcr01_updated(CPUPPCState *env) +void pmu_mmcr01a_updated(CPUPPCState *env) { PowerPCCPU *cpu = env_archcpu(env); @@ -260,7 +260,7 @@ void helper_store_mmcr0(CPUPPCState *env, target_ulong value) env->spr[SPR_POWER_MMCR0] = value; - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); /* Update cycle overflow timers with the current MMCR0 state */ pmu_update_overflow_timers(env); @@ -272,7 +272,14 @@ void helper_store_mmcr1(CPUPPCState *env, uint64_t value) env->spr[SPR_POWER_MMCR1] = value; - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); +} + +void helper_store_mmcrA(CPUPPCState *env, uint64_t value) +{ + env->spr[SPR_POWER_MMCRA] = value; + + pmu_mmcr01a_updated(env); } target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn) @@ -301,7 +308,7 @@ static void perfm_alert(PowerPCCPU *cpu) env->spr[SPR_POWER_MMCR0] |= MMCR0_FC; /* Changing MMCR0_FC requires summaries and hflags update */ - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); /* * Delete all pending timers if we need to freeze diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h index 775e640053..87fa8c9334 100644 --- a/target/ppc/power8-pmu.h +++ b/target/ppc/power8-pmu.h @@ -18,10 +18,10 @@ #define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL void cpu_ppc_pmu_init(CPUPPCState *env); -void pmu_mmcr01_updated(CPUPPCState *env); +void pmu_mmcr01a_updated(CPUPPCState *env); #else static inline void cpu_ppc_pmu_init(CPUPPCState *env) { } -static inline void pmu_mmcr01_updated(CPUPPCState *env) { } +static inline void pmu_mmcr01a_updated(CPUPPCState *env) { } #endif #endif diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index 8a9d6cd994..eb2561f593 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -85,6 +85,7 @@ void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn); +void spr_write_MMCRA(DisasContext *ctx, int sprn, int gprn); void spr_write_PMC(DisasContext *ctx, int sprn, int gprn); void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn); void spr_read_xer(DisasContext *ctx, int gprn, int sprn); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 2c39605273..a85f596d65 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -193,6 +193,7 @@ struct DisasContext { bool mmcr0_pmcjce; bool pmc_other; bool pmu_insn_cnt; + bool bhrb_enable; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; uint32_t flags; @@ -6345,6 +6346,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->mmcr0_pmcjce = (hflags >> HFLAGS_PMCJCE) & 1; ctx->pmc_other = (hflags >> HFLAGS_PMC_OTHER) & 1; ctx->pmu_insn_cnt = (hflags >> HFLAGS_INSN_CNT) & 1; + ctx->bhrb_enable = (hflags >> HFLAGS_BHRB_ENABLE) & 1; ctx->singlestep_enabled = 0; if ((hflags >> HFLAGS_SE) & 1) { From 4de4a4705f234861176b32292374021ee96e004e Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 28 Mar 2024 20:41:33 +1000 Subject: [PATCH 23/72] target/ppc: Add recording of taken branches to BHRB This commit continues adding support for the Branch History Rolling Buffer (BHRB) as is provided starting with the P8 processor and continuing with its successors. This commit is limited to the recording and filtering of taken branches. The following changes were made: - Enabled functionality on P10 processors only due to performance impact seen with P8 and P9 where it is not disabled for non problem state branches. - Added a BHRB buffer for storing branch instruction and target addresses for taken branches - Renamed gen_update_cfar to gen_update_branch_history and added a 'target' parameter to hold the branch target address and 'inst_type' parameter to use for filtering - Added TCG code to gen_update_branch_history that stores data to the BHRB and updates the BHRB offset. - Added BHRB resource initialization and reset functions Reviewed-by: Nicholas Piggin Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 17 +++++ target/ppc/cpu_init.c | 37 +++++++++- target/ppc/power8-pmu.c | 33 +++++++++ target/ppc/power8-pmu.h | 7 ++ target/ppc/translate.c | 97 ++++++++++++++++++++++++-- target/ppc/translate/branch-impl.c.inc | 2 +- 6 files changed, 185 insertions(+), 8 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 195d4be2b7..2f91d7dc33 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -548,6 +548,8 @@ FIELD(MSR, LE, MSR_LE, 1) MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0) #define MMCRA_BHRBRD PPC_BIT(26) /* BHRB Recording Disable */ +#define MMCRA_IFM_MASK PPC_BITMASK(32, 33) /* BHRB Instruction Filtering */ +#define MMCRA_IFM_SHIFT PPC_BIT_NR(33) #define MMCR1_EVT_SIZE 8 /* extract64() does a right shift before extracting */ @@ -774,6 +776,8 @@ enum { POWERPC_FLAG_SMT = 0x00400000, /* Using "LPAR per core" mode (as opposed to per-thread) */ POWERPC_FLAG_SMT_1LPAR = 0x00800000, + /* Has BHRB */ + POWERPC_FLAG_BHRB = 0x01000000, }; /* @@ -1215,6 +1219,9 @@ struct pnv_tod_tbst { #define PPC_CPU_OPCODES_LEN 0x40 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 +#define BHRB_MAX_NUM_ENTRIES_LOG2 (5) +#define BHRB_MAX_NUM_ENTRIES (1 << BHRB_MAX_NUM_ENTRIES_LOG2) + struct CPUArchState { /* Most commonly used resources during translated code execution first */ target_ulong gpr[32]; /* general purpose registers */ @@ -1311,6 +1318,16 @@ struct CPUArchState { int dcache_line_size; int icache_line_size; +#ifdef TARGET_PPC64 + /* Branch History Rolling Buffer (BHRB) resources */ + target_ulong bhrb_num_entries; + intptr_t bhrb_base; + target_ulong bhrb_filter; + target_ulong bhrb_offset; + target_ulong bhrb_offset_mask; + uint64_t bhrb[BHRB_MAX_NUM_ENTRIES]; +#endif + /* These resources are used during exception processing */ /* CPU model definition */ target_ulong msr_mask; diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 366ea8568b..1ec84b5ddc 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6142,6 +6142,28 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; } +static void bhrb_init_state(CPUPPCState *env, target_long num_entries_log2) +{ + if (env->flags & POWERPC_FLAG_BHRB) { + if (num_entries_log2 > BHRB_MAX_NUM_ENTRIES_LOG2) { + num_entries_log2 = BHRB_MAX_NUM_ENTRIES_LOG2; + } + env->bhrb_num_entries = 1 << num_entries_log2; + env->bhrb_base = (intptr_t)&env->bhrb[0]; + env->bhrb_offset_mask = (env->bhrb_num_entries * sizeof(uint64_t)) - 1; + } +} + +static void bhrb_reset_state(CPUPPCState *env) +{ + if (env->flags & POWERPC_FLAG_BHRB) { + env->bhrb_offset = 0; + env->bhrb_filter = 0; + memset(env->bhrb, 0, sizeof(env->bhrb)); + } +} + +#define POWER8_BHRB_ENTRIES_LOG2 5 static void init_proc_POWER8(CPUPPCState *env) { /* Common Registers */ @@ -6183,6 +6205,8 @@ static void init_proc_POWER8(CPUPPCState *env) env->dcache_line_size = 128; env->icache_line_size = 128; + bhrb_init_state(env, POWER8_BHRB_ENTRIES_LOG2); + /* Allocate hardware IRQ controller */ init_excp_POWER8(env); ppcPOWER7_irq_init(env_archcpu(env)); @@ -6307,6 +6331,7 @@ static struct ppc_radix_page_info POWER9_radix_page_info = { }; #endif /* CONFIG_USER_ONLY */ +#define POWER9_BHRB_ENTRIES_LOG2 5 static void init_proc_POWER9(CPUPPCState *env) { /* Common Registers */ @@ -6357,6 +6382,8 @@ static void init_proc_POWER9(CPUPPCState *env) env->dcache_line_size = 128; env->icache_line_size = 128; + bhrb_init_state(env, POWER9_BHRB_ENTRIES_LOG2); + /* Allocate hardware IRQ controller */ init_excp_POWER9(env); ppcPOWER9_irq_init(env_archcpu(env)); @@ -6497,6 +6524,7 @@ static struct ppc_radix_page_info POWER10_radix_page_info = { }; #endif /* !CONFIG_USER_ONLY */ +#define POWER10_BHRB_ENTRIES_LOG2 5 static void init_proc_POWER10(CPUPPCState *env) { /* Common Registers */ @@ -6546,6 +6574,8 @@ static void init_proc_POWER10(CPUPPCState *env) env->dcache_line_size = 128; env->icache_line_size = 128; + bhrb_init_state(env, POWER10_BHRB_ENTRIES_LOG2); + /* Allocate hardware IRQ controller */ init_excp_POWER10(env); ppcPOWER9_irq_init(env_archcpu(env)); @@ -6650,7 +6680,8 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | POWERPC_FLAG_BE | POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | - POWERPC_FLAG_VSX | POWERPC_FLAG_SCV; + POWERPC_FLAG_VSX | POWERPC_FLAG_SCV | + POWERPC_FLAG_BHRB; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; } @@ -7222,6 +7253,10 @@ static void ppc_cpu_reset_hold(Object *obj, ResetType type) } env->spr[i] = spr->default_value; } + +#if defined(TARGET_PPC64) + bhrb_reset_state(env); +#endif } #ifndef CONFIG_USER_ONLY diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index 6f5d4e1256..db9ee8e96b 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -82,6 +82,37 @@ static void pmu_update_summaries(CPUPPCState *env) env->pmc_cyc_cnt = cyc_cnt; } +static void hreg_bhrb_filter_update(CPUPPCState *env) +{ + target_long ifm; + + if (!(env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE)) { + /* disable recording to BHRB */ + env->bhrb_filter = BHRB_TYPE_NORECORD; + return; + } + + ifm = (env->spr[SPR_POWER_MMCRA] & MMCRA_IFM_MASK) >> MMCRA_IFM_SHIFT; + switch (ifm) { + case 0: + /* record all branches */ + env->bhrb_filter = -1; + break; + case 1: + /* only record calls (LK = 1) */ + env->bhrb_filter = BHRB_TYPE_CALL; + break; + case 2: + /* only record indirect branches */ + env->bhrb_filter = BHRB_TYPE_INDIRECT; + break; + case 3: + /* only record conditional branches */ + env->bhrb_filter = BHRB_TYPE_COND; + break; + } +} + void pmu_mmcr01a_updated(CPUPPCState *env) { PowerPCCPU *cpu = env_archcpu(env); @@ -95,6 +126,8 @@ void pmu_mmcr01a_updated(CPUPPCState *env) ppc_set_irq(cpu, PPC_INTERRUPT_PERFM, 0); } + hreg_bhrb_filter_update(env); + /* * Should this update overflow timers (if mmcr0 is updated) so they * get set in cpu_post_load? diff --git a/target/ppc/power8-pmu.h b/target/ppc/power8-pmu.h index 87fa8c9334..3f79cfc45b 100644 --- a/target/ppc/power8-pmu.h +++ b/target/ppc/power8-pmu.h @@ -13,6 +13,13 @@ #ifndef POWER8_PMU_H #define POWER8_PMU_H +#define BHRB_TYPE_NORECORD 0x00 +#define BHRB_TYPE_CALL 0x01 +#define BHRB_TYPE_INDIRECT 0x02 +#define BHRB_TYPE_COND 0x04 +#define BHRB_TYPE_OTHER 0x08 +#define BHRB_TYPE_XL_FORM 0x10 + #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) #define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL diff --git a/target/ppc/translate.c b/target/ppc/translate.c index a85f596d65..8aa2439700 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -180,6 +180,7 @@ struct DisasContext { #if defined(TARGET_PPC64) bool sf_mode; bool has_cfar; + bool has_bhrb; #endif bool fpu_enabled; bool altivec_enabled; @@ -3371,14 +3372,85 @@ static void gen_rvwinkle(DisasContext *ctx) gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ } + +static inline TCGv gen_write_bhrb(TCGv_ptr base, TCGv offset, TCGv mask, TCGv value) +{ + TCGv_ptr tmp = tcg_temp_new_ptr(); + + /* add base and offset to get address of bhrb entry */ + tcg_gen_add_ptr(tmp, base, (TCGv_ptr)offset); + + /* store value into bhrb at bhrb_offset */ + tcg_gen_st_i64(value, tmp, 0); + + /* add 8 to current bhrb_offset */ + tcg_gen_addi_tl(offset, offset, 8); + + /* apply offset mask */ + tcg_gen_and_tl(offset, offset, mask); + + return offset; +} #endif /* #if defined(TARGET_PPC64) */ -static inline void gen_update_cfar(DisasContext *ctx, target_ulong nip) +static inline void gen_update_branch_history(DisasContext *ctx, + target_ulong nip, + TCGv target, + target_long inst_type) { #if defined(TARGET_PPC64) + TCGv_ptr base; + TCGv tmp; + TCGv offset; + TCGv mask; + TCGLabel *no_update; + if (ctx->has_cfar) { tcg_gen_movi_tl(cpu_cfar, nip); } + + if (!ctx->has_bhrb || + !ctx->bhrb_enable || + inst_type == BHRB_TYPE_NORECORD) { + return; + } + + tmp = tcg_temp_new(); + no_update = gen_new_label(); + + /* check for bhrb filtering */ + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPUPPCState, bhrb_filter)); + tcg_gen_andi_tl(tmp, tmp, inst_type); + tcg_gen_brcondi_tl(TCG_COND_EQ, tmp, 0, no_update); + + base = tcg_temp_new_ptr(); + offset = tcg_temp_new(); + mask = tcg_temp_new(); + + /* load bhrb base address */ + tcg_gen_ld_ptr(base, tcg_env, offsetof(CPUPPCState, bhrb_base)); + + /* load current bhrb_offset */ + tcg_gen_ld_tl(offset, tcg_env, offsetof(CPUPPCState, bhrb_offset)); + + /* load a BHRB offset mask */ + tcg_gen_ld_tl(mask, tcg_env, offsetof(CPUPPCState, bhrb_offset_mask)); + + offset = gen_write_bhrb(base, offset, mask, tcg_constant_i64(nip)); + + /* Also record the target address for XL-Form branches */ + if (inst_type & BHRB_TYPE_XL_FORM) { + + /* Set the 'T' bit for target entries */ + tcg_gen_ori_tl(tmp, target, 0x2); + + offset = gen_write_bhrb(base, offset, mask, tmp); + } + + /* save updated bhrb_offset for next time */ + tcg_gen_st_tl(offset, tcg_env, offsetof(CPUPPCState, bhrb_offset)); + + gen_set_label(no_update); #endif } @@ -3508,8 +3580,10 @@ static void gen_b(DisasContext *ctx) } if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->base.pc_next); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_CALL); + } else { + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_OTHER); } - gen_update_cfar(ctx, ctx->cia); gen_goto_tb(ctx, 0, target); ctx->base.is_jmp = DISAS_NORETURN; } @@ -3524,6 +3598,7 @@ static void gen_bcond(DisasContext *ctx, int type) uint32_t bo = BO(ctx->opcode); TCGLabel *l1; TCGv target; + target_long bhrb_type = BHRB_TYPE_OTHER; if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { target = tcg_temp_new(); @@ -3534,11 +3609,16 @@ static void gen_bcond(DisasContext *ctx, int type) } else { tcg_gen_mov_tl(target, cpu_lr); } + if (!LK(ctx->opcode)) { + bhrb_type |= BHRB_TYPE_INDIRECT; + } + bhrb_type |= BHRB_TYPE_XL_FORM; } else { target = NULL; } if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->base.pc_next); + bhrb_type |= BHRB_TYPE_CALL; } l1 = gen_new_label(); if ((bo & 0x4) == 0) { @@ -3589,6 +3669,7 @@ static void gen_bcond(DisasContext *ctx, int type) tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1); } } + bhrb_type |= BHRB_TYPE_COND; } if ((bo & 0x10) == 0) { /* Test CR */ @@ -3603,8 +3684,11 @@ static void gen_bcond(DisasContext *ctx, int type) tcg_gen_andi_i32(temp, cpu_crf[bi >> 2], mask); tcg_gen_brcondi_i32(TCG_COND_NE, temp, 0, l1); } + bhrb_type |= BHRB_TYPE_COND; } - gen_update_cfar(ctx, ctx->cia); + + gen_update_branch_history(ctx, ctx->cia, target, bhrb_type); + if (type == BCOND_IM) { target_ulong li = (target_long)((int16_t)(BD(ctx->opcode))); if (likely(AA(ctx->opcode) == 0)) { @@ -3720,7 +3804,7 @@ static void gen_rfi(DisasContext *ctx) /* Restore CPU state */ CHK_SV(ctx); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); gen_helper_rfi(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif @@ -3735,7 +3819,7 @@ static void gen_rfid(DisasContext *ctx) /* Restore CPU state */ CHK_SV(ctx); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); gen_helper_rfid(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif @@ -3750,7 +3834,7 @@ static void gen_rfscv(DisasContext *ctx) /* Restore CPU state */ CHK_SV(ctx); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); gen_helper_rfscv(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif @@ -6330,6 +6414,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #if defined(TARGET_PPC64) ctx->sf_mode = (hflags >> HFLAGS_64) & 1; ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); + ctx->has_bhrb = !!(env->flags & POWERPC_FLAG_BHRB); #endif ctx->lazy_tlb_flush = env->mmu_model == POWERPC_MMU_32B || env->mmu_model & POWERPC_MMU_64; diff --git a/target/ppc/translate/branch-impl.c.inc b/target/ppc/translate/branch-impl.c.inc index fb0fcf30cc..9ade0c659a 100644 --- a/target/ppc/translate/branch-impl.c.inc +++ b/target/ppc/translate/branch-impl.c.inc @@ -17,7 +17,7 @@ static bool trans_RFEBB(DisasContext *ctx, arg_XL_s *arg) REQUIRE_INSNS_FLAGS2(ctx, ISA207S); translator_io_start(&ctx->base); - gen_update_cfar(ctx, ctx->cia); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); gen_helper_rfebb(tcg_env, cpu_gpr[arg->s]); ctx->base.is_jmp = DISAS_CHAIN; From 6bfcf1dc239dba752307f0312e8a320470c26655 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 28 Mar 2024 20:41:35 +1000 Subject: [PATCH 24/72] target/ppc: Add clrbhrb and mfbhrbe instructions Add support for the clrbhrb and mfbhrbe instructions. Since neither instruction is believed to be critical to performance, both instructions were implemented using helper functions. Access to both instructions is controlled by bits in the HFSCR (for privileged state) and MMCR0 (for problem state). A new function, helper_mmcr0_facility_check, was added for checking MMCR0[BHRBA] and raising a facility_unavailable exception if required. NOTE: For P8 and P9, due to a performance issue, branch history will not be kept, but the instructions will be allowed to execute as normal with the exception that the mfbhrbe instruction will always return a zero value. Reviewed-by: Nicholas Piggin Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 2 ++ target/ppc/helper.h | 7 ++++ target/ppc/insn32.decode | 8 +++++ target/ppc/misc_helper.c | 50 ++++++++++++++++++++++++++++ target/ppc/translate.c | 2 ++ target/ppc/translate/bhrb-impl.c.inc | 43 ++++++++++++++++++++++++ 6 files changed, 112 insertions(+) create mode 100644 target/ppc/translate/bhrb-impl.c.inc diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2f91d7dc33..c358927211 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -535,6 +535,7 @@ FIELD(MSR, LE, MSR_LE, 1) #define MMCR0_PMCjCE PPC_BIT(49) /* MMCR0 PMCj Condition Enabled */ #define MMCR0_FCP PPC_BIT(34) /* Freeze Counters/BHRB if PR=1 */ #define MMCR0_FCPC PPC_BIT(51) /* Condition for FCP bit */ +#define MMCR0_BHRBA_NR PPC_BIT_NR(42) /* BHRB Available */ /* MMCR0 userspace r/w mask */ #define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE) /* MMCR2 userspace r/w mask */ @@ -634,6 +635,7 @@ FIELD(MSR, LE, MSR_LE, 1) /* HFSCR bits */ #define HFSCR_MSGP PPC_BIT(53) /* Privileged Message Send Facilities */ +#define HFSCR_BHRB PPC_BIT(59) /* BHRB Instructions */ #define HFSCR_IC_MSGP 0xA #define DBCR0_ICMP (1 << 27) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 7b07cd1a49..55293e20a9 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -820,3 +820,10 @@ DEF_HELPER_4(DSCLIQ, void, env, fprp, fprp, i32) DEF_HELPER_1(tbegin, void, env) DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) + +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +DEF_HELPER_1(clrbhrb, void, env) +DEF_HELPER_FLAGS_2(mfbhrbe, TCG_CALL_NO_WG, i64, env, i32) +#endif +#endif diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index 05c1d8c12d..d4dd022df4 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1190,3 +1190,11 @@ MSGSYNC 011111 ----- ----- ----- 1101110110 - @X_sync ...... .. l:3 ... sc:2 ..... .......... . &X_sync SYNC 011111 -- ... --- .. ----- 1001010110 - @X_sync EIEIO 011111 ----- ----- ----- 1101010110 - + +# Branch History Rolling Buffer (BHRB) Instructions + +&XFX_bhrbe rt bhrbe +@XFX_bhrbe ...... rt:5 bhrbe:10 .......... - &XFX_bhrbe + +MFBHRBE 011111 ..... ..... ..... 0100101110 - @XFX_bhrbe +CLRBHRB 011111 ----- ----- ----- 0110101110 - diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 58e808dc96..6f419c9346 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -150,6 +150,17 @@ void helper_msr_facility_check(CPUPPCState *env, uint32_t bit, #if !defined(CONFIG_USER_ONLY) +#ifdef TARGET_PPC64 +static void helper_mmcr0_facility_check(CPUPPCState *env, uint32_t bit, + uint32_t sprn, uint32_t cause) +{ + if (FIELD_EX64(env->msr, MSR, PR) && + !(env->spr[SPR_POWER_MMCR0] & (1ULL << bit))) { + raise_fu_exception(env, bit, sprn, cause, GETPC()); + } +} +#endif + void helper_store_sdr1(CPUPPCState *env, target_ulong val) { if (env->spr[SPR_SDR1] != val) { @@ -363,3 +374,42 @@ void helper_fixup_thrm(CPUPPCState *env) env->spr[i] = v; } } + +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +void helper_clrbhrb(CPUPPCState *env) +{ + helper_hfscr_facility_check(env, HFSCR_BHRB, "clrbhrb", FSCR_IC_BHRB); + + helper_mmcr0_facility_check(env, MMCR0_BHRBA_NR, 0, FSCR_IC_BHRB); + + if (env->flags & POWERPC_FLAG_BHRB) { + memset(env->bhrb, 0, sizeof(env->bhrb)); + } +} + +uint64_t helper_mfbhrbe(CPUPPCState *env, uint32_t bhrbe) +{ + unsigned int index; + + helper_hfscr_facility_check(env, HFSCR_BHRB, "mfbhrbe", FSCR_IC_BHRB); + + helper_mmcr0_facility_check(env, MMCR0_BHRBA_NR, 0, FSCR_IC_BHRB); + + if (!(env->flags & POWERPC_FLAG_BHRB) || + (bhrbe >= env->bhrb_num_entries) || + (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE)) { + return 0; + } + + /* + * Note: bhrb_offset is the byte offset for writing the + * next entry (over the oldest entry), which is why we + * must offset bhrbe by 1 to get to the 0th entry. + */ + index = ((env->bhrb_offset / sizeof(uint64_t)) - (bhrbe + 1)) % + env->bhrb_num_entries; + return env->bhrb[index]; +} +#endif +#endif diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 8aa2439700..546f1754de 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -5647,6 +5647,8 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/misc-impl.c.inc" +#include "translate/bhrb-impl.c.inc" + /* Handles lfdp */ static void gen_dform39(DisasContext *ctx) { diff --git a/target/ppc/translate/bhrb-impl.c.inc b/target/ppc/translate/bhrb-impl.c.inc new file mode 100644 index 0000000000..3a19bc4555 --- /dev/null +++ b/target/ppc/translate/bhrb-impl.c.inc @@ -0,0 +1,43 @@ +/* + * Power ISA Decode For BHRB Instructions + * + * Copyright IBM Corp. 2023 + * + * Authors: + * Glenn Miles + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + +static bool trans_MFBHRBE(DisasContext *ctx, arg_XFX_bhrbe *arg) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA207S); + TCGv_i32 bhrbe = tcg_constant_i32(arg->bhrbe); + gen_helper_mfbhrbe(cpu_gpr[arg->rt], tcg_env, bhrbe); + return true; +} + +static bool trans_CLRBHRB(DisasContext *ctx, arg_CLRBHRB *arg) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA207S); + gen_helper_clrbhrb(tcg_env); + return true; +} + +#else + +static bool trans_MFBHRBE(DisasContext *ctx, arg_XFX_bhrbe *arg) +{ + gen_invalid(ctx); + return true; +} + +static bool trans_CLRBHRB(DisasContext *ctx, arg_CLRBHRB *arg) +{ + gen_invalid(ctx); + return true; +} +#endif From dabd6d3c3ae1ba52662d2056a8d59b01d9ee8e24 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Thu, 28 Mar 2024 20:41:37 +1000 Subject: [PATCH 25/72] target/ppc: Add migration support for BHRB Adds migration support for Branch History Rolling Buffer (BHRB) internal state. Reviewed-by: Nicholas Piggin Signed-off-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/machine.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 6b6c31d903..731dd8df35 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -711,6 +711,26 @@ static const VMStateDescription vmstate_reservation = { } }; +#ifdef TARGET_PPC64 +static bool bhrb_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + return (cpu->env.flags & POWERPC_FLAG_BHRB) != 0; +} + +static const VMStateDescription vmstate_bhrb = { + .name = "cpu/bhrb", + .version_id = 1, + .minimum_version_id = 1, + .needed = bhrb_needed, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(env.bhrb_offset, PowerPCCPU), + VMSTATE_UINT64_ARRAY(env.bhrb, PowerPCCPU, BHRB_MAX_NUM_ENTRIES), + VMSTATE_END_OF_LIST() + } +}; +#endif + const VMStateDescription vmstate_ppc_cpu = { .name = "cpu", .version_id = 5, @@ -756,6 +776,7 @@ const VMStateDescription vmstate_ppc_cpu = { #ifdef TARGET_PPC64 &vmstate_tm, &vmstate_slb, + &vmstate_bhrb, #endif /* TARGET_PPC64 */ &vmstate_tlb6xx, &vmstate_tlbemb, From 21cfc36a6c2190932ea55edbce241e6b6b7b9c8f Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 10:56:50 +1000 Subject: [PATCH 26/72] target/ppc: larx/stcx generation need only apply DEF_MEMOP() once Use DEF_MEMOP() consistently in larx and stcx. generation, and apply it once when it's used rather than where the macros are expanded, to reduce typing. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- target/ppc/translate.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 546f1754de..6c103dafe0 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2849,7 +2849,7 @@ static void gen_load_locked(DisasContext *ctx, MemOp memop) gen_set_access_type(ctx, ACCESS_RES); gen_addr_reg_index(ctx, t0); - tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop | MO_ALIGN); + tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, DEF_MEMOP(memop) | MO_ALIGN); tcg_gen_mov_tl(cpu_reserve, t0); tcg_gen_movi_tl(cpu_reserve_length, memop_size(memop)); tcg_gen_mov_tl(cpu_reserve_val, gpr); @@ -2862,9 +2862,9 @@ static void gen_##name(DisasContext *ctx) \ } /* lwarx */ -LARX(lbarx, DEF_MEMOP(MO_UB)) -LARX(lharx, DEF_MEMOP(MO_UW)) -LARX(lwarx, DEF_MEMOP(MO_UL)) +LARX(lbarx, MO_UB) +LARX(lharx, MO_UW) +LARX(lwarx, MO_UL) static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, TCGv EA, TCGCond cond, int addend) @@ -3109,15 +3109,15 @@ static void gen_##name(DisasContext *ctx) \ gen_conditional_store(ctx, memop); \ } -STCX(stbcx_, DEF_MEMOP(MO_UB)) -STCX(sthcx_, DEF_MEMOP(MO_UW)) -STCX(stwcx_, DEF_MEMOP(MO_UL)) +STCX(stbcx_, MO_UB) +STCX(sthcx_, MO_UW) +STCX(stwcx_, MO_UL) #if defined(TARGET_PPC64) /* ldarx */ -LARX(ldarx, DEF_MEMOP(MO_UQ)) +LARX(ldarx, MO_UQ) /* stdcx. */ -STCX(stdcx_, DEF_MEMOP(MO_UQ)) +STCX(stdcx_, MO_UQ) /* lqarx */ static void gen_lqarx(DisasContext *ctx) From c10c6ce032959b8b4ada8b8d755a10cad7b5f1a7 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 10:58:51 +1000 Subject: [PATCH 27/72] target/ppc: Remove redundant MEMOP_GET_SIZE macro There is a memop_size() function for this. Reviewed-by: BALATON Zoltan Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- target/ppc/translate.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 6c103dafe0..cf42dfcc9d 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2840,8 +2840,6 @@ static void gen_isync(DisasContext *ctx) ctx->base.is_jmp = DISAS_EXIT_UPDATE; } -#define MEMOP_GET_SIZE(x) (1 << ((x) & MO_SIZE)) - static void gen_load_locked(DisasContext *ctx, MemOp memop) { TCGv gpr = cpu_gpr[rD(ctx->opcode)]; @@ -2874,7 +2872,7 @@ static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, TCGv u = tcg_temp_new(); tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); - tcg_gen_addi_tl(t2, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_addi_tl(t2, EA, memop_size(memop)); tcg_gen_qemu_ld_tl(t2, t2, ctx->mem_idx, memop); tcg_gen_addi_tl(u, t, addend); @@ -2884,7 +2882,7 @@ static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, tcg_gen_qemu_st_tl(u, EA, ctx->mem_idx, memop); /* RT = (t != t2 ? t : u = 1<<(s*8-1)) */ - tcg_gen_movi_tl(u, 1 << (MEMOP_GET_SIZE(memop) * 8 - 1)); + tcg_gen_movi_tl(u, 1 << (memop_size(memop) * 8 - 1)); tcg_gen_movcond_tl(cond, cpu_gpr[rD(ctx->opcode)], t, t2, t, u); } @@ -3046,7 +3044,7 @@ static void gen_st_atomic(DisasContext *ctx, MemOp memop) TCGv ea_plus_s = tcg_temp_new(); tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); - tcg_gen_addi_tl(ea_plus_s, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_addi_tl(ea_plus_s, EA, memop_size(memop)); tcg_gen_qemu_ld_tl(t2, ea_plus_s, ctx->mem_idx, memop); tcg_gen_movcond_tl(TCG_COND_EQ, s, t, t2, src, t); tcg_gen_movcond_tl(TCG_COND_EQ, s2, t, t2, src, t2); From cce7aee8dd934dca3040eb8ea1ad1973701d7c96 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 Jun 2023 03:02:15 +1000 Subject: [PATCH 28/72] target/ppc: Make checkstop actually stop the system checkstop state does not halt the system, interrupts continue to be serviced, and other CPUs run. Make it stop the machine with qemu_system_guest_panicked. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 2e3f36a3ef..fd00c044b5 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -19,6 +19,8 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/log.h" +#include "sysemu/sysemu.h" +#include "sysemu/runstate.h" #include "cpu.h" #include "exec/exec-all.h" #include "internal.h" @@ -425,6 +427,8 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, static void powerpc_mcheck_checkstop(CPUPPCState *env) { + /* KVM guests always have MSR[ME] enabled */ +#ifdef CONFIG_TCG CPUState *cs = env_cpu(env); if (FIELD_EX64(env->msr, MSR, ME)) { @@ -437,9 +441,15 @@ static void powerpc_mcheck_checkstop(CPUPPCState *env) if (qemu_log_separate()) { qemu_log("Machine check while not allowed. " "Entering checkstop state\n"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); + + /* + * This stops the machine and logs CPU state without killing QEMU + * (like cpu_abort()) so the machine can still be debugged (because + * it is often a guest error). + */ + qemu_system_guest_panicked(NULL); + cpu_loop_exit_noexc(cs); +#endif } static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) From 9728fb5c22a51b5f2f65ba36fdcd8fd0999be6fc Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 12 Mar 2024 04:33:18 +1000 Subject: [PATCH 29/72] target/ppc: improve checkstop logging Change the logging not to print to stderr as well, because a checkstop is a guest error (or perhaps a simulated machine error) rather than a QEMU error, so send it to the log. Update the checkstop message, and log CPU registers too. Reviewed-by: Richard Henderson Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/excp_helper.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index fd00c044b5..a283c97717 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -430,17 +430,19 @@ static void powerpc_mcheck_checkstop(CPUPPCState *env) /* KVM guests always have MSR[ME] enabled */ #ifdef CONFIG_TCG CPUState *cs = env_cpu(env); + FILE *f; if (FIELD_EX64(env->msr, MSR, ME)) { return; } - /* 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"); + f = qemu_log_trylock(); + if (f) { + fprintf(f, "Entering checkstop state: " + "machine check with MSR[ME]=0\n"); + cpu_dump_state(cs, f, CPU_DUMP_FPU | CPU_DUMP_CCOP); + qemu_log_unlock(f); + } /* * This stops the machine and logs CPU state without killing QEMU From 45693f94dd11f26b673541c0c9a270f8462d8633 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 18 Jun 2023 19:39:13 +1000 Subject: [PATCH 30/72] target/ppc: Implement attn instruction on BookS 64-bit processors attn is an implementation-specific instruction that on POWER (and G5/ 970) can be enabled with a HID bit (disabled = illegal), and executing it causes the host processor to stop and the service processor to be notified. Generally used for debugging. Implement attn and make it checkstop the system, which should be good enough for QEMU debugging. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 12 +++++ target/ppc/cpu_init.c | 69 ++++++++++++++++++++++++++++ target/ppc/excp_helper.c | 43 +++++++++++++---- target/ppc/helper.h | 1 + target/ppc/insn32.decode | 4 ++ target/ppc/translate/misc-impl.c.inc | 10 ++++ 6 files changed, 130 insertions(+), 9 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c358927211..2532408be0 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1375,6 +1375,9 @@ struct CPUArchState { /* Power management */ int (*check_pow)(CPUPPCState *env); + /* attn instruction enable */ + int (*check_attn)(CPUPPCState *env); + #if !defined(CONFIG_USER_ONLY) void *load_info; /* holds boot loading state */ #endif @@ -1523,6 +1526,7 @@ struct PowerPCCPUClass { int n_host_threads; void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); + int (*check_attn)(CPUPPCState *env); }; ObjectClass *ppc_cpu_class_by_name(const char *name); @@ -2320,6 +2324,8 @@ void ppc_compat_add_property(Object *obj, const char *name, #define HID0_NAP (1 << 22) /* pre-2.06 */ #define HID0_HILE PPC_BIT(19) /* POWER8 */ #define HID0_POWER9_HILE PPC_BIT(4) +#define HID0_ENABLE_ATTN PPC_BIT(31) /* POWER8 */ +#define HID0_POWER9_ENABLE_ATTN PPC_BIT(3) /*****************************************************************************/ /* PowerPC Instructions types definitions */ @@ -3025,6 +3031,12 @@ static inline int check_pow_nocheck(CPUPPCState *env) return 1; } +/* attn enable check */ +static inline int check_attn_none(CPUPPCState *env) +{ + return 0; +} + /*****************************************************************************/ /* PowerPC implementations definitions */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 1ec84b5ddc..ee01415c32 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2107,6 +2107,26 @@ static int check_pow_hid0_74xx(CPUPPCState *env) return 0; } +#if defined(TARGET_PPC64) +static int check_attn_hid0(CPUPPCState *env) +{ + if (env->spr[SPR_HID0] & HID0_ENABLE_ATTN) { + return 1; + } + + return 0; +} + +static int check_attn_hid0_power9(CPUPPCState *env) +{ + if (env->spr[SPR_HID0] & HID0_POWER9_ENABLE_ATTN) { + return 1; + } + + return 0; +} +#endif + static void init_proc_405(CPUPPCState *env) { register_40x_sprs(env); @@ -2138,6 +2158,7 @@ POWERPC_FAMILY(405)(ObjectClass *oc, void *data) dc->desc = "PowerPC 405"; pcc->init_proc = init_proc_405; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_DCR | PPC_WRTEE | PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | @@ -2210,6 +2231,7 @@ POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440 EP"; pcc->init_proc = init_proc_440EP; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -2248,6 +2270,7 @@ POWERPC_FAMILY(460EX)(ObjectClass *oc, void *data) dc->desc = "PowerPC 460 EX"; pcc->init_proc = init_proc_440EP; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -2308,6 +2331,7 @@ POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440 GP"; pcc->init_proc = init_proc_440GP; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_DCR | PPC_DCRX | PPC_WRTEE | PPC_MFAPIDI | PPC_CACHE | PPC_CACHE_ICBI | @@ -2382,6 +2406,7 @@ POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440x5"; pcc->init_proc = init_proc_440x5; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_DCR | PPC_WRTEE | PPC_RFMCI | PPC_CACHE | PPC_CACHE_ICBI | @@ -2417,6 +2442,7 @@ POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440x5 with double precision FPU"; pcc->init_proc = init_proc_440x5; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_FLOAT | PPC_FLOAT_FSQRT | PPC_FLOAT_STFIWX | @@ -2465,6 +2491,7 @@ POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) dc->desc = "Freescale 5xx cores (aka RCPU)"; pcc->init_proc = init_proc_MPC5xx; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MEM_EIEIO | PPC_MEM_SYNC | PPC_CACHE_ICBI | PPC_FLOAT | PPC_FLOAT_STFIWX | @@ -2507,6 +2534,7 @@ POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) dc->desc = "Freescale 8xx cores (aka PowerQUICC)"; pcc->init_proc = init_proc_MPC8xx; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MEM_EIEIO | PPC_MEM_SYNC | PPC_CACHE_ICBI | PPC_MFTB; @@ -2557,6 +2585,7 @@ POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) dc->desc = "PowerPC G2"; pcc->init_proc = init_proc_G2; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_STFIWX | @@ -2595,6 +2624,7 @@ POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) dc->desc = "PowerPC G2LE"; pcc->init_proc = init_proc_G2; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_STFIWX | @@ -2741,6 +2771,7 @@ POWERPC_FAMILY(e200)(ObjectClass *oc, void *data) dc->desc = "e200 core"; pcc->init_proc = init_proc_e200; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; /* * XXX: unimplemented instructions: * dcblc @@ -3029,6 +3060,7 @@ POWERPC_FAMILY(e500v1)(ObjectClass *oc, void *data) dc->desc = "e500v1 core"; pcc->init_proc = init_proc_e500v1; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_SPE | PPC_SPE_SINGLE | PPC_WRTEE | PPC_RFDI | @@ -3072,6 +3104,7 @@ POWERPC_FAMILY(e500v2)(ObjectClass *oc, void *data) dc->desc = "e500v2 core"; pcc->init_proc = init_proc_e500v2; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE | PPC_WRTEE | PPC_RFDI | @@ -3115,6 +3148,7 @@ POWERPC_FAMILY(e500mc)(ObjectClass *oc, void *data) dc->desc = "e500mc core"; pcc->init_proc = init_proc_e500mc; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | PPC_WRTEE | PPC_RFDI | PPC_RFMCI | PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | @@ -3161,6 +3195,7 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) dc->desc = "e5500 core"; pcc->init_proc = init_proc_e5500; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | PPC_WRTEE | PPC_RFDI | PPC_RFMCI | PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | @@ -3209,6 +3244,7 @@ POWERPC_FAMILY(e6500)(ObjectClass *oc, void *data) dc->desc = "e6500 core"; pcc->init_proc = init_proc_e6500; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | PPC_WRTEE | PPC_RFDI | PPC_RFMCI | PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | @@ -3271,6 +3307,7 @@ POWERPC_FAMILY(603)(ObjectClass *oc, void *data) dc->desc = "PowerPC 603"; pcc->init_proc = init_proc_603; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3310,6 +3347,7 @@ POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) dc->desc = "PowerPC 603e"; pcc->init_proc = init_proc_603; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3355,6 +3393,7 @@ POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) dc->desc = "e300 core"; pcc->init_proc = init_proc_e300; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_STFIWX | @@ -3410,6 +3449,7 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data) dc->desc = "PowerPC 604"; pcc->init_proc = init_proc_604; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3455,6 +3495,7 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) dc->desc = "PowerPC 604E"; pcc->init_proc = init_proc_604E; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3511,6 +3552,7 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data) dc->desc = "PowerPC 740"; pcc->init_proc = init_proc_740; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3576,6 +3618,7 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750"; pcc->init_proc = init_proc_750; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3722,6 +3765,7 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750 CL"; pcc->init_proc = init_proc_750cl; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; /* * XXX: not implemented: * cache lock instructions: @@ -3829,6 +3873,7 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750CX"; pcc->init_proc = init_proc_750cx; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3901,6 +3946,7 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750FX"; pcc->init_proc = init_proc_750fx; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3973,6 +4019,7 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750GX"; pcc->init_proc = init_proc_750gx; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -4032,6 +4079,7 @@ POWERPC_FAMILY(745)(ObjectClass *oc, void *data) dc->desc = "PowerPC 745"; pcc->init_proc = init_proc_745; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -4077,6 +4125,7 @@ POWERPC_FAMILY(755)(ObjectClass *oc, void *data) dc->desc = "PowerPC 755"; pcc->init_proc = init_proc_755; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -4143,6 +4192,7 @@ POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7400 (aka G4)"; pcc->init_proc = init_proc_7400; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4222,6 +4272,7 @@ POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7410 (aka G4)"; pcc->init_proc = init_proc_7410; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4322,6 +4373,7 @@ POWERPC_FAMILY(7440)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7440 (aka G4)"; pcc->init_proc = init_proc_7440; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4444,6 +4496,7 @@ POWERPC_FAMILY(7450)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7450 (aka G4)"; pcc->init_proc = init_proc_7450; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4573,6 +4626,7 @@ POWERPC_FAMILY(7445)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7445 (aka G4)"; pcc->init_proc = init_proc_7445; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4704,6 +4758,7 @@ POWERPC_FAMILY(7455)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7455 (aka G4)"; pcc->init_proc = init_proc_7455; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4855,6 +4910,7 @@ POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7457 (aka G4)"; pcc->init_proc = init_proc_7457; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4989,6 +5045,7 @@ POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) dc->desc = "PowerPC e600"; pcc->init_proc = init_proc_e600; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -5904,6 +5961,7 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) dc->desc = "PowerPC 970"; pcc->init_proc = init_proc_970; pcc->check_pow = check_pow_970; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -5979,6 +6037,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) dc->desc = "POWER5+"; pcc->init_proc = init_proc_power5plus; pcc->check_pow = check_pow_970; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6086,6 +6145,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER7; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6247,6 +6307,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER8; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6439,6 +6500,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER9; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0_power9; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6618,6 +6680,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER10; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0_power9; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -6856,6 +6919,11 @@ static void init_ppc_proc(PowerPCCPU *cpu) warn_report("no power management check handler registered." " Attempt QEMU to crash very soon !"); } + + if (env->check_attn == NULL) { + warn_report("no attn check handler registered." + " Attempt QEMU to crash very soon !"); + } } @@ -7317,6 +7385,7 @@ static void ppc_cpu_instance_init(Object *obj) env->flags = pcc->flags; env->bfd_mach = pcc->bfd_mach; env->check_pow = pcc->check_pow; + env->check_attn = pcc->check_attn; /* * Mark HV mode as supported if the CPU has an MSR_HV bit in the diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index a283c97717..f48eb2eac8 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -154,6 +154,7 @@ static uint32_t ppc_ldl_code(CPUArchState *env, target_ulong addr) return insn; } + #endif static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp) @@ -425,21 +426,20 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, env->reserve_addr = -1; } -static void powerpc_mcheck_checkstop(CPUPPCState *env) -{ - /* KVM guests always have MSR[ME] enabled */ #ifdef CONFIG_TCG +/* + * This stops the machine and logs CPU state without killing QEMU (like + * cpu_abort()) because it is often a guest error as opposed to a QEMU error, + * so the machine can still be debugged. + */ +static G_NORETURN void powerpc_checkstop(CPUPPCState *env, const char *reason) +{ CPUState *cs = env_cpu(env); FILE *f; - if (FIELD_EX64(env->msr, MSR, ME)) { - return; - } - f = qemu_log_trylock(); if (f) { - fprintf(f, "Entering checkstop state: " - "machine check with MSR[ME]=0\n"); + fprintf(f, "Entering checkstop state: %s\n", reason); cpu_dump_state(cs, f, CPU_DUMP_FPU | CPU_DUMP_CCOP); qemu_log_unlock(f); } @@ -451,6 +451,31 @@ static void powerpc_mcheck_checkstop(CPUPPCState *env) */ qemu_system_guest_panicked(NULL); cpu_loop_exit_noexc(cs); +} + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +void helper_attn(CPUPPCState *env) +{ + /* POWER attn is unprivileged when enabled by HID, otherwise illegal */ + if ((*env->check_attn)(env)) { + powerpc_checkstop(env, "host executed attn"); + } else { + raise_exception_err(env, POWERPC_EXCP_HV_EMU, + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); + } +} +#endif +#endif /* CONFIG_TCG */ + +static void powerpc_mcheck_checkstop(CPUPPCState *env) +{ + /* KVM guests always have MSR[ME] enabled */ +#ifdef CONFIG_TCG + if (FIELD_EX64(env->msr, MSR, ME)) { + return; + } + + powerpc_checkstop(env, "machine check with MSR[ME]=0"); #endif } diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 55293e20a9..09d50f9b76 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -825,5 +825,6 @@ DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) #if defined(TARGET_PPC64) DEF_HELPER_1(clrbhrb, void, env) DEF_HELPER_FLAGS_2(mfbhrbe, TCG_CALL_NO_WG, i64, env, i32) +DEF_HELPER_1(attn, noreturn, env) #endif #endif diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index d4dd022df4..ee33141476 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -1198,3 +1198,7 @@ EIEIO 011111 ----- ----- ----- 1101010110 - MFBHRBE 011111 ..... ..... ..... 0100101110 - @XFX_bhrbe CLRBHRB 011111 ----- ----- ----- 0110101110 - + +## Misc POWER instructions + +ATTN 000000 00000 00000 00000 0100000000 0 diff --git a/target/ppc/translate/misc-impl.c.inc b/target/ppc/translate/misc-impl.c.inc index c1661d2f43..cbf82b1ea0 100644 --- a/target/ppc/translate/misc-impl.c.inc +++ b/target/ppc/translate/misc-impl.c.inc @@ -145,3 +145,13 @@ static bool trans_EIEIO(DisasContext *ctx, arg_EIEIO *a) return true; } + +static bool trans_ATTN(DisasContext *ctx, arg_ATTN *a) +{ +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + gen_helper_attn(tcg_env); + return true; +#else + return false; +#endif +} From e89294b27e6fb4e976c08768a4a2ca0e8820a4ca Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Tue, 16 May 2023 01:28:35 +1000 Subject: [PATCH 31/72] target/ppc: BookE DECAR SPR is 32-bit The DECAR SPR is 32-bits width. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index ee01415c32..927721d49a 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -792,7 +792,7 @@ static void register_BookE_sprs(CPUPPCState *env, uint64_t ivor_mask) 0x00000000); spr_register(env, SPR_BOOKE_DECAR, "DECAR", SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_generic, + SPR_NOACCESS, &spr_write_generic32, 0x00000000); /* SPRGs */ spr_register(env, SPR_USPRG0, "USPRG0", From 1cbcbcb8d6f10d742aa7cf6ad7bc768492e6407e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 11 Sep 2023 13:02:35 +1000 Subject: [PATCH 32/72] target/ppc: Add PPR32 SPR PPR32 provides access to the upper half of PPR. Reviewed-by: Richard Henderson Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 1 + target/ppc/cpu_init.c | 12 ++++++++++++ target/ppc/spr_common.h | 2 ++ target/ppc/translate.c | 24 ++++++++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2532408be0..141cbefb4c 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2120,6 +2120,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_POWER_MMCRS (0x37E) #define SPR_WORT (0x37F) #define SPR_PPR (0x380) +#define SPR_PPR32 (0x382) #define SPR_750_GQR0 (0x390) #define SPR_440_DNV0 (0x390) #define SPR_750_GQR1 (0x391) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 927721d49a..6baf7555a7 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5606,6 +5606,14 @@ static void register_HEIR64_spr(CPUPPCState *env) 0x00000000); } +static void register_power7_common_sprs(CPUPPCState *env) +{ + spr_register(env, SPR_PPR32, "PPR32", + &spr_read_ppr32, &spr_write_ppr32, + &spr_read_ppr32, &spr_write_ppr32, + 0x00000000); +} + static void register_power8_tce_address_control_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_TAR, "TAR", @@ -6101,6 +6109,7 @@ static void init_proc_POWER7(CPUPPCState *env) register_power6_common_sprs(env); register_HEIR32_spr(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power7_book4_sprs(env); /* env variables */ @@ -6247,6 +6256,7 @@ static void init_proc_POWER8(CPUPPCState *env) register_power6_common_sprs(env); register_HEIR32_spr(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); register_power8_ebb_sprs(env); @@ -6414,6 +6424,7 @@ static void init_proc_POWER9(CPUPPCState *env) register_power6_common_sprs(env); register_HEIR32_spr(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); register_power8_ebb_sprs(env); @@ -6608,6 +6619,7 @@ static void init_proc_POWER10(CPUPPCState *env) register_power6_common_sprs(env); register_HEIR64_spr(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); register_power8_ebb_sprs(env); diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index eb2561f593..9e40b3b608 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -203,6 +203,8 @@ void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn); void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn); void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_ppr32(DisasContext *ctx, int sprn, int gprn); +void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn); #endif void register_low_BATs(CPUPPCState *env); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index cf42dfcc9d..fb05047d95 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1352,6 +1352,30 @@ void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn) gen_load_spr(t0, sprn + 16); tcg_gen_ext32u_tl(cpu_gpr[gprn], t0); } + +/* The PPR32 SPR accesses the upper 32-bits of PPR */ +void spr_read_ppr32(DisasContext *ctx, int gprn, int sprn) +{ + gen_load_spr(cpu_gpr[gprn], SPR_PPR); + tcg_gen_shri_tl(cpu_gpr[gprn], cpu_gpr[gprn], 32); + spr_load_dump_spr(SPR_PPR); +} + +void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + /* + * Don't clobber the low 32-bits of the PPR. These are all reserved bits + * but TCG does implement them, so it would be surprising to zero them + * here. "Priority nops" are similarly careful not to clobber reserved + * bits. + */ + gen_load_spr(t0, SPR_PPR); + tcg_gen_deposit_tl(t0, t0, cpu_gpr[gprn], 32, 32); + gen_store_spr(SPR_PPR, t0); + spr_store_dump_spr(SPR_PPR); +} #endif #define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \ From 5fa7efe4730586648412f59f13bd370c40f372ff Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sat, 27 May 2023 03:17:38 +1000 Subject: [PATCH 33/72] target/ppc: add helper to write per-LPAR SPRs An SPR can be either per-thread, per-core, or per-LPAR. Per-LPAR means per-thread or per-core, depending on 1LPAR mode. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/spr_common.h | 2 ++ target/ppc/translate.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index 9e40b3b608..85f73b860b 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -83,6 +83,8 @@ void spr_read_generic(DisasContext *ctx, int gprn, int sprn); void spr_write_generic(DisasContext *ctx, int sprn, int gprn); void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn); +void spr_core_write_generic32(DisasContext *ctx, int sprn, int gprn); +void spr_core_lpar_write_generic(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR0(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCRA(DisasContext *ctx, int sprn, int gprn); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index fb05047d95..7b525020ef 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -473,6 +473,34 @@ void spr_core_write_generic(DisasContext *ctx, int sprn, int gprn) spr_store_dump_spr(sprn); } +void spr_core_write_generic32(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0; + + if (!(ctx->flags & POWERPC_FLAG_SMT)) { + spr_write_generic32(ctx, sprn, gprn); + return; + } + + if (!gen_serialize(ctx)) { + return; + } + + t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]); + gen_helper_spr_core_write_generic(tcg_env, tcg_constant_i32(sprn), t0); + spr_store_dump_spr(sprn); +} + +void spr_core_lpar_write_generic(DisasContext *ctx, int sprn, int gprn) +{ + if (ctx->flags & POWERPC_FLAG_SMT_1LPAR) { + spr_core_write_generic(ctx, sprn, gprn); + } else { + spr_write_generic(ctx, sprn, gprn); + } +} + static void spr_write_CTRL_ST(DisasContext *ctx, int sprn, int gprn) { /* This does not implement >1 thread */ From e5c2ac9dc1da8cfb7830a44cb4dcb94ba983c1ad Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 12 Jul 2023 22:54:40 +1000 Subject: [PATCH 34/72] target/ppc: Add SMT support to simple SPRs AMOR, MMCRC, HRMOR, TSCR, HMEER, RPR SPRs are per-core or per-LPAR registers with simple (generic) implementations. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 6baf7555a7..415cc7a4e2 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -246,7 +246,7 @@ static void register_amr_sprs(CPUPPCState *env) spr_register_hv(env, SPR_AMOR, "AMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_lpar_write_generic, 0); #endif /* !CONFIG_USER_ONLY */ } @@ -5472,7 +5472,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) spr_register_hv(env, SPR_MMCRC, "MMCRC", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic32, + &spr_read_generic, &spr_core_write_generic32, 0x00000000); spr_register_hv(env, SPR_MMCRH, "MMCRH", SPR_NOACCESS, SPR_NOACCESS, @@ -5512,7 +5512,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) spr_register_hv(env, SPR_HRMOR, "HRMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_write_generic, 0x00000000); } @@ -5740,7 +5740,7 @@ static void register_power_common_book4_sprs(CPUPPCState *env) spr_register_hv(env, SPR_TSCR, "TSCR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic32, + &spr_read_generic, &spr_core_write_generic32, 0x00000000); spr_register_hv(env, SPR_HMER, "HMER", SPR_NOACCESS, SPR_NOACCESS, @@ -5750,7 +5750,7 @@ static void register_power_common_book4_sprs(CPUPPCState *env) spr_register_hv(env, SPR_HMEER, "HMEER", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_write_generic, 0x00000000); spr_register_hv(env, SPR_TFMR, "TFMR", SPR_NOACCESS, SPR_NOACCESS, @@ -5826,7 +5826,7 @@ static void register_power8_rpr_sprs(CPUPPCState *env) spr_register_hv(env, SPR_RPR, "RPR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_write_generic, 0x00000103070F1F3F); #endif } From 4d2b0ad32a593ac24757b66f64efe2fb84161345 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 12 Jul 2023 23:02:00 +1000 Subject: [PATCH 35/72] target/ppc: Add SMT support to PTCR SPR PTCR is a per-core register. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/misc_helper.c | 16 ++++++++++++++-- target/ppc/translate.c | 4 ++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 6f419c9346..a67930d031 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -173,6 +173,7 @@ void helper_store_sdr1(CPUPPCState *env, target_ulong val) void helper_store_ptcr(CPUPPCState *env, target_ulong val) { if (env->spr[SPR_PTCR] != val) { + CPUState *cs = env_cpu(env); PowerPCCPU *cpu = env_archcpu(env); target_ulong ptcr_mask = PTCR_PATB | PTCR_PATS; target_ulong patbsize = val & PTCR_PATS; @@ -194,8 +195,19 @@ void helper_store_ptcr(CPUPPCState *env, target_ulong val) return; } - env->spr[SPR_PTCR] = val; - tlb_flush(env_cpu(env)); + if (cs->nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) { + env->spr[SPR_PTCR] = val; + tlb_flush(cs); + } else { + CPUState *ccs; + + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + CPUPPCState *cenv = &ccpu->env; + cenv->spr[SPR_PTCR] = val; + tlb_flush(ccs); + } + } } } diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 7b525020ef..64131bf15c 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -909,6 +909,10 @@ void spr_write_hior(DisasContext *ctx, int sprn, int gprn) } void spr_write_ptcr(DisasContext *ctx, int sprn, int gprn) { + if (!gen_serialize_core(ctx)) { + return; + } + gen_helper_store_ptcr(tcg_env, cpu_gpr[gprn]); } From c9d5aedf40cfa0d68a6a4c020d14f8e6116a369e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 22 May 2023 16:23:21 +1000 Subject: [PATCH 36/72] target/ppc: Implement LDBAR, TTR SPRs LDBAR, TTR are a Power-specific SPRs. These simple implementations are enough for IBM proprietary firmware for now. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 2 ++ target/ppc/cpu_init.c | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 141cbefb4c..823be85d03 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2098,6 +2098,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_DEXCR (0x33C) #define SPR_IC (0x350) #define SPR_VTB (0x351) +#define SPR_LDBAR (0x352) #define SPR_MMCRC (0x353) #define SPR_PSSCR (0x357) #define SPR_440_INV0 (0x370) @@ -2144,6 +2145,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_440_IVLIM (0x399) #define SPR_TSCR (0x399) #define SPR_750_DMAU (0x39A) +#define SPR_POWER_TTR (0x39A) #define SPR_750_DMAL (0x39B) #define SPR_440_RSTCFG (0x39B) #define SPR_BOOKE_DCDBTRL (0x39C) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 415cc7a4e2..d181adc5f1 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5767,6 +5767,16 @@ static void register_power_common_book4_sprs(CPUPPCState *env) &spr_access_nop, &spr_write_generic, &spr_access_nop, &spr_write_generic, 0x00000000); + spr_register_hv(env, SPR_LDBAR, "LDBAR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_core_lpar_write_generic, + 0x00000000); + spr_register_hv(env, SPR_POWER_TTR, "TTR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_core_write_generic, + 0x00000000); #endif } From 2736432ffc30b74fc72858854e62b62253b685ff Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Sun, 18 Jun 2023 19:37:07 +1000 Subject: [PATCH 37/72] target/ppc: Implement SPRC/SPRD SPRs This implements the POWER SPRC/SPRD SPRs, and SCRATCH0-7 registers that can be accessed via these indirect SPRs. SCRATCH registers only provide storage, but they are used by firmware for low level crash and progress data, so this implementation logs writes to the registers to help with analysis. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 7 +++-- target/ppc/cpu_init.c | 10 ++++++ target/ppc/helper.h | 3 ++ target/ppc/misc_helper.c | 66 ++++++++++++++++++++++++++++++++++++++++ target/ppc/spr_common.h | 3 ++ target/ppc/translate.c | 18 +++++++++++ 6 files changed, 105 insertions(+), 2 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 823be85d03..e4c342b17d 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1264,6 +1264,9 @@ struct CPUArchState { ppc_slb_t slb[MAX_SLB_ENTRIES]; /* PowerPC 64 SLB area */ struct CPUBreakpoint *ciabr_breakpoint; struct CPUWatchpoint *dawr0_watchpoint; + + /* POWER CPU regs/state */ + target_ulong scratch[8]; /* SCRATCH registers (shared across core) */ #endif target_ulong sr[32]; /* segment registers */ uint32_t nb_BATs; /* number of BATs */ @@ -1806,9 +1809,9 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_SPRG2 (0x112) #define SPR_SPRG3 (0x113) #define SPR_SPRG4 (0x114) -#define SPR_SCOMC (0x114) +#define SPR_POWER_SPRC (0x114) #define SPR_SPRG5 (0x115) -#define SPR_SCOMD (0x115) +#define SPR_POWER_SPRD (0x115) #define SPR_SPRG6 (0x116) #define SPR_SPRG7 (0x117) #define SPR_ASR (0x118) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index d181adc5f1..8cfaee61d9 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5777,6 +5777,16 @@ static void register_power_common_book4_sprs(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_core_write_generic, 0x00000000); + spr_register_hv(env, SPR_POWER_SPRC, "SPRC", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_sprc, + 0x00000000); + spr_register_hv(env, SPR_POWER_SPRD, "SPRD", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_sprd, &spr_write_sprd, + 0x00000000); #endif } diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 09d50f9b76..57bf8354e7 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -730,6 +730,9 @@ DEF_HELPER_2(book3s_msgsndp, void, env, tl) DEF_HELPER_2(book3s_msgclrp, void, env, tl) DEF_HELPER_1(load_tfmr, tl, env) DEF_HELPER_2(store_tfmr, void, env, tl) +DEF_HELPER_FLAGS_2(store_sprc, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_1(load_sprd, TCG_CALL_NO_RWG_SE, tl, env) +DEF_HELPER_FLAGS_2(store_sprd, TCG_CALL_NO_RWG, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index a67930d031..fa47be2298 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -307,6 +307,72 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) } bql_unlock(); } + +/* Indirect SCOM (SPRC/SPRD) access to SCRATCH0-7 are implemented. */ +void helper_store_sprc(CPUPPCState *env, target_ulong val) +{ + if (val & ~0x3f8ULL) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid SPRC register value " + TARGET_FMT_lx"\n", val); + return; + } + env->spr[SPR_POWER_SPRC] = val; +} + +target_ulong helper_load_sprd(CPUPPCState *env) +{ + target_ulong sprc = env->spr[SPR_POWER_SPRC]; + + switch (sprc & 0x3c0) { + case 0: /* SCRATCH0-7 */ + return env->scratch[(sprc >> 3) & 0x7]; + default: + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } + return 0; +} + +static void do_store_scratch(CPUPPCState *env, int nr, target_ulong val) +{ + CPUState *cs = env_cpu(env); + CPUState *ccs; + uint32_t nr_threads = cs->nr_threads; + + /* + * Log stores to SCRATCH, because some firmware uses these for debugging + * and logging, but they would normally be read by the BMC, which is + * not implemented in QEMU yet. This gives a way to get at the information. + * Could also dump these upon checkstop. + */ + qemu_log("SPRD write 0x" TARGET_FMT_lx " to SCRATCH%d\n", val, nr); + + if (nr_threads == 1) { + env->scratch[nr] = val; + return; + } + + THREAD_SIBLING_FOREACH(cs, ccs) { + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; + cenv->scratch[nr] = val; + } +} + +void helper_store_sprd(CPUPPCState *env, target_ulong val) +{ + target_ulong sprc = env->spr[SPR_POWER_SPRC]; + + switch (sprc & 0x3c0) { + case 0: /* SCRATCH0-7 */ + do_store_scratch(env, (sprc >> 3) & 0x7, val); + break; + default: + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } +} #endif /* defined(TARGET_PPC64) */ void helper_store_pidr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index 85f73b860b..01aff449bc 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -207,6 +207,9 @@ void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn); void spr_read_ppr32(DisasContext *ctx, int sprn, int gprn); void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn); +void spr_write_sprc(DisasContext *ctx, int sprn, int gprn); +void spr_read_sprd(DisasContext *ctx, int sprn, int gprn); +void spr_write_sprd(DisasContext *ctx, int sprn, int gprn); #endif void register_low_BATs(CPUPPCState *env); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 64131bf15c..0bc16d7251 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1301,6 +1301,24 @@ void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn) gen_helper_store_tfmr(tcg_env, cpu_gpr[gprn]); } +void spr_write_sprc(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_sprc(tcg_env, cpu_gpr[gprn]); +} + +void spr_read_sprd(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_sprd(cpu_gpr[gprn], tcg_env); +} + +void spr_write_sprd(DisasContext *ctx, int sprn, int gprn) +{ + if (!gen_serialize_core(ctx)) { + return; + } + gen_helper_store_sprd(tcg_env, cpu_gpr[gprn]); +} + void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) { translator_io_start(&ctx->base); From 0dfe59fe77ed571f23aefb70a3a226c3e1779862 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 5 Jun 2023 20:05:36 +1000 Subject: [PATCH 38/72] target/ppc: add SMT support to msgsnd broadcast msgsnd has a broadcast mode that sends hypervisor doorbells to all threads belonging to the same core as the target. A "subcore" mode sends to all or one thread depending on 1LPAR mode. Reviewed-by: Glenn Miles Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 6 +- target/ppc/excp_helper.c | 61 +++++++++++++------ target/ppc/helper.h | 2 +- .../ppc/translate/processor-ctrl-impl.c.inc | 2 +- 4 files changed, 48 insertions(+), 23 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index e4c342b17d..e201b7f6c2 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1163,7 +1163,11 @@ FIELD(FPSCR, FI, FPSCR_FI, 1) #define DBELL_TYPE_DBELL_SERVER (0x05 << DBELL_TYPE_SHIFT) -#define DBELL_BRDCAST PPC_BIT(37) +#define DBELL_BRDCAST_MASK PPC_BITMASK(37, 38) +#define DBELL_BRDCAST_SHIFT 25 +#define DBELL_BRDCAST_SUBPROC (0x1 << DBELL_BRDCAST_SHIFT) +#define DBELL_BRDCAST_CORE (0x2 << DBELL_BRDCAST_SHIFT) + #define DBELL_LPIDTAG_SHIFT 14 #define DBELL_LPIDTAG_MASK (0xfff << DBELL_LPIDTAG_SHIFT) #define DBELL_PIRTAG_MASK 0x3fff diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index f48eb2eac8..0cd542675f 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -2969,7 +2969,7 @@ void helper_msgsnd(target_ulong rb) PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *cenv = &cpu->env; - if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { + if ((rb & DBELL_BRDCAST_MASK) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { ppc_set_irq(cpu, irq, 1); } } @@ -2988,6 +2988,16 @@ static bool dbell_type_server(target_ulong rb) return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER; } +static inline bool dbell_bcast_core(target_ulong rb) +{ + return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_CORE; +} + +static inline bool dbell_bcast_subproc(target_ulong rb) +{ + return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_SUBPROC; +} + void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) { if (!dbell_type_server(rb)) { @@ -2997,32 +3007,43 @@ void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0); } -static void book3s_msgsnd_common(int pir, int irq) -{ - CPUState *cs; - - bql_lock(); - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *cenv = &cpu->env; - - /* TODO: broadcast message to all threads of the same processor */ - if (cenv->spr_cb[SPR_PIR].default_value == pir) { - ppc_set_irq(cpu, irq, 1); - } - } - bql_unlock(); -} - -void helper_book3s_msgsnd(target_ulong rb) +void helper_book3s_msgsnd(CPUPPCState *env, target_ulong rb) { int pir = rb & DBELL_PROCIDTAG_MASK; + bool brdcast = false; + CPUState *cs, *ccs; + PowerPCCPU *cpu; if (!dbell_type_server(rb)) { return; } - book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL); + cpu = ppc_get_vcpu_by_pir(pir); + if (!cpu) { + return; + } + cs = CPU(cpu); + + if (dbell_bcast_core(rb) || (dbell_bcast_subproc(rb) && + (env->flags & POWERPC_FLAG_SMT_1LPAR))) { + brdcast = true; + } + + if (cs->nr_threads == 1 || !brdcast) { + ppc_set_irq(cpu, PPC_INTERRUPT_HDOORBELL, 1); + return; + } + + /* + * Why is bql needed for walking CPU list? Answer seems to be because ppc + * irq handling needs it, but ppc_set_irq takes the lock itself if needed, + * so could this be removed? + */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + ppc_set_irq(POWERPC_CPU(ccs), PPC_INTERRUPT_HDOORBELL, 1); + } + bql_unlock(); } #ifdef TARGET_PPC64 diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 57bf8354e7..dd92c6a937 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -695,7 +695,7 @@ DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_1(msgsnd, void, tl) DEF_HELPER_2(msgclr, void, env, tl) -DEF_HELPER_1(book3s_msgsnd, void, tl) +DEF_HELPER_2(book3s_msgsnd, void, env, tl) DEF_HELPER_2(book3s_msgclr, void, env, tl) #endif diff --git a/target/ppc/translate/processor-ctrl-impl.c.inc b/target/ppc/translate/processor-ctrl-impl.c.inc index 0142801985..8abbb89630 100644 --- a/target/ppc/translate/processor-ctrl-impl.c.inc +++ b/target/ppc/translate/processor-ctrl-impl.c.inc @@ -59,7 +59,7 @@ static bool trans_MSGSND(DisasContext *ctx, arg_X_rb *a) #if !defined(CONFIG_USER_ONLY) if (is_book3s_arch2x(ctx)) { - gen_helper_book3s_msgsnd(cpu_gpr[a->rb]); + gen_helper_book3s_msgsnd(tcg_env, cpu_gpr[a->rb]); } else { gen_helper_msgsnd(cpu_gpr[a->rb]); } From 41e9a098d175fdaeb025fe39be940940d1edd979 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 13 May 2024 01:27:33 +0200 Subject: [PATCH 39/72] target/ppc: Remove unused struct 'mmu_ctx_hash32' I think it's use was removed by Commit 5883d8b296 ("mmu-hash*: Don't use full ppc_hash{32, 64}_translate() path for get_phys_page_debug()") Reviewed-by: BALATON Zoltan Signed-off-by: Dr. David Alan Gilbert Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu-hash32.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 6dfedab11d..da6e8b293c 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -37,12 +37,6 @@ # define LOG_BATS(...) do { } while (0) #endif -struct mmu_ctx_hash32 { - hwaddr raddr; /* Real address */ - int prot; /* Protection bits */ - int key; /* Access key */ -}; - static int ppc_hash32_pp_prot(int key, int pp, int nx) { int prot; From 2b92822acc3e8cb7ce22c75a115b53219f0458b9 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:34 +0200 Subject: [PATCH 40/72] target/ppc: Remove unused helper_rac() The helper_rac function is defined but not used, remove it. Fixes: 005b69fdcc (target/ppc: Remove PowerPC 601 CPUs) Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/helper.h | 2 -- target/ppc/mmu_helper.c | 24 ------------------------ 2 files changed, 26 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index dd92c6a937..76b8f25c77 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -701,8 +701,6 @@ DEF_HELPER_2(book3s_msgclr, void, env, tl) DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_2(rac, tl, env, tl) - DEF_HELPER_2(load_dcr, tl, env, tl) DEF_HELPER_3(store_dcr, void, env, tl, tl) #endif diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index d9d950e220..d4388e66be 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -596,30 +596,6 @@ void helper_6xx_tlbi(CPUPPCState *env, target_ulong EPN) do_6xx_tlb(env, EPN, 1); } -/*****************************************************************************/ -/* PowerPC 601 specific instructions (POWER bridge) */ - -target_ulong helper_rac(CPUPPCState *env, target_ulong addr) -{ - mmu_ctx_t ctx; - int nb_BATs; - target_ulong ret = 0; - - /* - * We don't have to generate many instances of this instruction, - * as rac is supervisor only. - * - * XXX: FIX THIS: Pretend we have no BAT - */ - nb_BATs = env->nb_BATs; - env->nb_BATs = 0; - if (get_physical_address_wtlb(env, &ctx, addr, 0, ACCESS_INT, 0) == 0) { - ret = ctx.raddr; - } - env->nb_BATs = nb_BATs; - return ret; -} - static inline target_ulong booke_tlb_to_page_size(int size) { return 1024 << (2 * size); From 3f520078deba9b5aaf5f5896c5b836341f5fc652 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:35 +0200 Subject: [PATCH 41/72] target/ppc: Move calculation of a value closer to its usage in booke tlb checks In mmubooke_check_tlb() and mmubooke206_check_tlb() prot2 is calculated first but only used after an unrelated check that can return before tha value is used. Move the calculation after the check, closer to where it is used, to keep them together and avoid computing it when not needed. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 4fde7fd3bf..f79e390306 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -635,12 +635,6 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, return -1; } - if (FIELD_EX64(env->msr, MSR, PR)) { - prot2 = tlb->prot & 0xF; - } else { - prot2 = (tlb->prot >> 4) & 0xF; - } - /* Check the address space */ if ((access_type == MMU_INST_FETCH ? FIELD_EX64(env->msr, MSR, IR) : @@ -649,6 +643,11 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, return -1; } + if (FIELD_EX64(env->msr, MSR, PR)) { + prot2 = tlb->prot & 0xF; + } else { + prot2 = (tlb->prot >> 4) & 0xF; + } *prot = prot2; if (prot2 & prot_for_access_type(access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); @@ -830,6 +829,18 @@ static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, found_tlb: + /* Check the address space and permissions */ + if (access_type == MMU_INST_FETCH) { + /* There is no way to fetch code using epid load */ + assert(!use_epid); + as = FIELD_EX64(env->msr, MSR, IR); + } + + if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); + return -1; + } + if (pr) { if (tlb->mas7_3 & MAS3_UR) { prot2 |= PAGE_READ; @@ -851,19 +862,6 @@ found_tlb: prot2 |= PAGE_EXEC; } } - - /* Check the address space and permissions */ - if (access_type == MMU_INST_FETCH) { - /* There is no way to fetch code using epid load */ - assert(!use_epid); - as = FIELD_EX64(env->msr, MSR, IR); - } - - if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); - return -1; - } - *prot = prot2; if (prot2 & prot_for_access_type(access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); From 750fbe334237e03056fda45ab36c8b155f65d1b5 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:36 +0200 Subject: [PATCH 42/72] target/ppc: Remove unneeded local variable from booke tlb checks In mmubooke_check_tlb() and mmubooke206_check_tlb() we can assign the value of prot2 directly to the destination, no need to have a separate local variable for it. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index f79e390306..09cbeb0052 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -628,8 +628,6 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, hwaddr *raddr, int *prot, target_ulong address, MMUAccessType access_type, int i) { - int prot2; - if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); return -1; @@ -644,17 +642,16 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, } if (FIELD_EX64(env->msr, MSR, PR)) { - prot2 = tlb->prot & 0xF; + *prot = tlb->prot & 0xF; } else { - prot2 = (tlb->prot >> 4) & 0xF; + *prot = (tlb->prot >> 4) & 0xF; } - *prot = prot2; - if (prot2 & prot_for_access_type(access_type)) { + if (*prot & prot_for_access_type(access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); return 0; } - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, prot2); + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); return access_type == MMU_INST_FETCH ? -3 : -2; } @@ -795,7 +792,6 @@ static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, target_ulong address, MMUAccessType access_type, int mmu_idx) { - int prot2 = 0; uint32_t epid; bool as, pr; bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); @@ -841,34 +837,34 @@ found_tlb: return -1; } + *prot = 0; if (pr) { if (tlb->mas7_3 & MAS3_UR) { - prot2 |= PAGE_READ; + *prot |= PAGE_READ; } if (tlb->mas7_3 & MAS3_UW) { - prot2 |= PAGE_WRITE; + *prot |= PAGE_WRITE; } if (tlb->mas7_3 & MAS3_UX) { - prot2 |= PAGE_EXEC; + *prot |= PAGE_EXEC; } } else { if (tlb->mas7_3 & MAS3_SR) { - prot2 |= PAGE_READ; + *prot |= PAGE_READ; } if (tlb->mas7_3 & MAS3_SW) { - prot2 |= PAGE_WRITE; + *prot |= PAGE_WRITE; } if (tlb->mas7_3 & MAS3_SX) { - prot2 |= PAGE_EXEC; + *prot |= PAGE_EXEC; } } - *prot = prot2; - if (prot2 & prot_for_access_type(access_type)) { + if (*prot & prot_for_access_type(access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); return 0; } - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, prot2); + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); return access_type == MMU_INST_FETCH ? -3 : -2; } From fef517cd8a3fb6baba15405448c4cb1dcd1b677a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:37 +0200 Subject: [PATCH 43/72] target/ppc: Simplify checking for real mode in get_physical_address_wtlb() In get_physical_address_wtlb() the real_mode flag depends on either the MSR[IR] or MSR[DR] bit depending on access_type. Extract just the needed bit in a more straight forward way instead of doing unnecessary computation. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 09cbeb0052..886fb6a657 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1184,8 +1184,10 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, int mmu_idx) { int ret = -1; - bool real_mode = (type == ACCESS_CODE && !FIELD_EX64(env->msr, MSR, IR)) || - (type != ACCESS_CODE && !FIELD_EX64(env->msr, MSR, DR)); + bool real_mode; + + real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) + : !FIELD_EX64(env->msr, MSR, DR); switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: From cfd5c128320ab36ca5fa330a9e1968bf4d3cd2b6 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:38 +0200 Subject: [PATCH 44/72] target/ppc: Drop cases for unimplemented MPC8xx MMU Drop MPC8xx cases from get_physical_address_wtlb() and ppc_jumbo_xlate(). The default case would still catch this and abort the same way and there is still a warning about it in ppc_tlb_invalidate_all() which is called in ppc_cpu_reset_hold() so likely we never get here but to make sure add a case to ppc_xlate() to the same effect. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 886fb6a657..3391df61cb 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1219,10 +1219,6 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, ret = mmubooke206_get_physical_address(env, ctx, eaddr, access_type, mmu_idx); break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env_cpu(env), "MPC8xx MMU model is not implemented\n"); - break; case POWERPC_MMU_REAL: if (real_mode) { ret = check_physical(env, ctx, eaddr, access_type); @@ -1353,8 +1349,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_BOOKE_DEAR] = eaddr; env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); break; - case POWERPC_MMU_MPC8xx: - cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); @@ -1427,9 +1421,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_ESR] = 0x00000000; } break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); case POWERPC_MMU_BOOKE206: booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); /* fall through */ @@ -1539,7 +1530,8 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_32B: return ppc_hash32_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); - + case POWERPC_MMU_MPC8xx: + cpu_abort(env_cpu(&cpu->env), "MPC8xx MMU model is not implemented\n"); default: return ppc_jumbo_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); From 269d6f006b855266bb60b3e027a143ae1a654179 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:39 +0200 Subject: [PATCH 45/72] target/ppc: Introduce mmu6xx_get_physical_address() Repurpose get_segment_6xx_tlb() to do the whole address translation for POWERPC_MMU_SOFT_6xx MMU model by moving the BAT check there and renaming it to match other similar functions. These are only called once together so no need to keep these separate functions and combining them simplifies the caller allowing further restructuring. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 3391df61cb..b7c07cf515 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -360,19 +360,23 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -/* Perform segment based translation */ -static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type, - int type) +static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, + MMUAccessType access_type, int type) { PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; - target_ulong vsid; + target_ulong vsid, sr, pgidx; int ds, target_page_bits; bool pr; int ret; - target_ulong sr, pgidx; + /* First try to find a BAT entry if there are any */ + if (env->nb_BATs && get_bat_6xx_tlb(env, ctx, eaddr, access_type) == 0) { + return 0; + } + + /* Perform segment based translation when no BATs matched */ pr = FIELD_EX64(env->msr, MSR, PR); ctx->eaddr = eaddr; @@ -1194,14 +1198,8 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, if (real_mode) { ret = check_physical(env, ctx, eaddr, access_type); } else { - /* Try to find a BAT */ - if (env->nb_BATs != 0) { - ret = get_bat_6xx_tlb(env, ctx, eaddr, access_type); - } - if (ret < 0) { - /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment_6xx_tlb(env, ctx, eaddr, access_type, type); - } + ret = mmu6xx_get_physical_address(env, ctx, eaddr, access_type, + type); } break; From f1418bdeb09d201ea636d061fa6edf1175074a09 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:40 +0200 Subject: [PATCH 46/72] target/ppc: Move else branch to avoid large if block in mmu6xx_get_physical_address() In mmu6xx_get_physical_address() we have a large if block with a two line else branch that effectively returns. Invert the condition and move the else there to allow deindenting the large if block to make the flow easier to follow. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 67 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index b7c07cf515..ba60b4902b 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -405,47 +405,44 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ret = -1; if (!ds) { /* Check if instruction fetch is allowed, if needed */ - if (type != ACCESS_CODE || ctx->nx == 0) { - /* Page address translation */ - qemu_log_mask(CPU_LOG_MMU, "htab_base " HWADDR_FMT_plx - " htab_mask " HWADDR_FMT_plx - " hash " HWADDR_FMT_plx "\n", - ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; + if (type == ACCESS_CODE && ctx->nx) { + qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); + return -3; + } + /* Page address translation */ + qemu_log_mask(CPU_LOG_MMU, "htab_base " HWADDR_FMT_plx " htab_mask " + HWADDR_FMT_plx " hash " HWADDR_FMT_plx "\n", + ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; - /* Software TLB search */ - ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; + /* Software TLB search */ + ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); #if defined(DUMP_PAGE_TABLES) - if (qemu_loglevel_mask(CPU_LOG_MMU)) { - CPUState *cs = env_cpu(env); - hwaddr curaddr; - uint32_t a0, a1, a2, a3; + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + CPUState *cs = env_cpu(env); + hwaddr curaddr; + uint32_t a0, a1, a2, a3; - qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx - "\n", ppc_hash32_hpt_base(cpu), - ppc_hash32_hpt_mask(cpu) + 0x80); - for (curaddr = ppc_hash32_hpt_base(cpu); - curaddr < (ppc_hash32_hpt_base(cpu) - + ppc_hash32_hpt_mask(cpu) + 0x80); - curaddr += 16) { - a0 = ldl_phys(cs->as, curaddr); - a1 = ldl_phys(cs->as, curaddr + 4); - a2 = ldl_phys(cs->as, curaddr + 8); - a3 = ldl_phys(cs->as, curaddr + 12); - if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", - curaddr, a0, a1, a2, a3); - } + qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx "\n", + ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu) + 0x80); + for (curaddr = ppc_hash32_hpt_base(cpu); + curaddr < (ppc_hash32_hpt_base(cpu) + + ppc_hash32_hpt_mask(cpu) + 0x80); + curaddr += 16) { + a0 = ldl_phys(cs->as, curaddr); + a1 = ldl_phys(cs->as, curaddr + 4); + a2 = ldl_phys(cs->as, curaddr + 8); + a3 = ldl_phys(cs->as, curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); } } -#endif - } else { - qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); - ret = -3; } +#endif } else { qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); /* Direct-store segment : absolutely *BUGGY* for now */ From 0af20f35d254bc87689a9d5ab2c5e45a677467dc Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:41 +0200 Subject: [PATCH 47/72] target/ppc: Move some debug logging in ppc6xx_tlb_check() Move the debug logging within ppc6xx_tlb_check() from after its only call to simplify the caller. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 52 ++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index ba60b4902b..89bfd9aa45 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -225,17 +225,14 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, access_type == MMU_INST_FETCH ? 'I' : 'D'); switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, 0, access_type)) { - case -3: - /* TLB inconsistency */ - return -1; case -2: /* Access violation */ ret = -2; best = nr; break; - case -1: + case -1: /* No match */ + case -3: /* TLB inconsistency */ default: - /* No match */ break; case 0: /* access granted */ @@ -251,14 +248,34 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, } } if (best != -1) { - done: +done: qemu_log_mask(CPU_LOG_MMU, "found TLB at addr " HWADDR_FMT_plx " prot=%01x ret=%d\n", ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); /* Update page flags */ pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, access_type); } +#if defined(DUMP_PAGE_TABLES) + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + CPUState *cs = env_cpu(env); + hwaddr base = ppc_hash32_hpt_base(env_archcpu(env)); + hwaddr len = ppc_hash32_hpt_mask(env_archcpu(env)) + 0x80; + uint32_t a0, a1, a2, a3; + qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx "\n", + base, len); + for (hwaddr curaddr = base; curaddr < base + len; curaddr += 16) { + a0 = ldl_phys(cs->as, curaddr); + a1 = ldl_phys(cs->as, curaddr + 4); + a2 = ldl_phys(cs->as, curaddr + 8); + a3 = ldl_phys(cs->as, curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif return ret; } @@ -420,29 +437,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ctx->raddr = (hwaddr)-1ULL; /* Software TLB search */ ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); -#if defined(DUMP_PAGE_TABLES) - if (qemu_loglevel_mask(CPU_LOG_MMU)) { - CPUState *cs = env_cpu(env); - hwaddr curaddr; - uint32_t a0, a1, a2, a3; - - qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx "\n", - ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu) + 0x80); - for (curaddr = ppc_hash32_hpt_base(cpu); - curaddr < (ppc_hash32_hpt_base(cpu) - + ppc_hash32_hpt_mask(cpu) + 0x80); - curaddr += 16) { - a0 = ldl_phys(cs->as, curaddr); - a1 = ldl_phys(cs->as, curaddr + 4); - a2 = ldl_phys(cs->as, curaddr + 8); - a3 = ldl_phys(cs->as, curaddr + 12); - if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", - curaddr, a0, a1, a2, a3); - } - } - } -#endif } else { qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); /* Direct-store segment : absolutely *BUGGY* for now */ From f3f66a315714a0bf10c1b82df3324834c9b484fc Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:42 +0200 Subject: [PATCH 48/72] target/ppc: Eliminate ret from mmu6xx_get_physical_address() Return directly, which is simpler than dragging a return value through multpile if and else blocks. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 84 +++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 89bfd9aa45..03d9e6bfda 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -386,7 +386,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong vsid, sr, pgidx; int ds, target_page_bits; bool pr; - int ret; /* First try to find a BAT entry if there are any */ if (env->nb_BATs && get_bat_6xx_tlb(env, ctx, eaddr, access_type) == 0) { @@ -419,7 +418,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, qemu_log_mask(CPU_LOG_MMU, "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", ctx->key, ds, ctx->nx, vsid); - ret = -1; if (!ds) { /* Check if instruction fetch is allowed, if needed */ if (type == ACCESS_CODE && ctx->nx) { @@ -436,51 +434,47 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* Initialize real address with an invalid value */ ctx->raddr = (hwaddr)-1ULL; /* Software TLB search */ - ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); - } else { - qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); - /* Direct-store segment : absolutely *BUGGY* for now */ - - switch (type) { - case ACCESS_INT: - /* Integer load/store : only access allowed */ - break; - case ACCESS_CODE: - /* No code fetch is allowed in direct-store areas */ - return -4; - case ACCESS_FLOAT: - /* Floating point load/store */ - return -4; - case ACCESS_RES: - /* lwarx, ldarx or srwcx. */ - return -4; - case ACCESS_CACHE: - /* - * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi - * - * Should make the instruction do no-op. As it already do - * no-op, it's quite easy :-) - */ - ctx->raddr = eaddr; - return 0; - case ACCESS_EXT: - /* eciwx or ecowx */ - return -4; - default: - qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need " - "address translation\n"); - return -4; - } - if ((access_type == MMU_DATA_STORE || ctx->key != 1) && - (access_type == MMU_DATA_LOAD || ctx->key != 0)) { - ctx->raddr = eaddr; - ret = 2; - } else { - ret = -2; - } + return ppc6xx_tlb_check(env, ctx, eaddr, access_type); } - return ret; + /* Direct-store segment : absolutely *BUGGY* for now */ + qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); + switch (type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_CODE: + /* No code fetch is allowed in direct-store areas */ + return -4; + case ACCESS_FLOAT: + /* Floating point load/store */ + return -4; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + return -4; + case ACCESS_CACHE: + /* + * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi + * + * Should make the instruction do no-op. As it already do + * no-op, it's quite easy :-) + */ + ctx->raddr = eaddr; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + return -4; + default: + qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need address" + " translation\n"); + return -4; + } + if ((access_type == MMU_DATA_STORE || ctx->key != 1) && + (access_type == MMU_DATA_LOAD || ctx->key != 0)) { + ctx->raddr = eaddr; + return 2; + } + return -2; } /* Generic TLB check function for embedded PowerPC implementations */ From 279fe98d0d3057daa4045faa6e2119288d7b7f07 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:43 +0200 Subject: [PATCH 49/72] target/ppc: Split out BookE xlate cases before checking real mode BookE does not have real mode so split off and handle it first in get_physical_address_wtlb() before checking for real mode for other MMU models. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 03d9e6bfda..9f177b6976 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1175,6 +1175,13 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, int ret = -1; bool real_mode; + if (env->mmu_model == POWERPC_MMU_BOOKE) { + return mmubooke_get_physical_address(env, ctx, eaddr, access_type); + } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { + return mmubooke206_get_physical_address(env, ctx, eaddr, access_type, + mmu_idx); + } + real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) : !FIELD_EX64(env->msr, MSR, DR); @@ -1195,13 +1202,6 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, ret = mmu40x_get_physical_address(env, ctx, eaddr, access_type); } break; - case POWERPC_MMU_BOOKE: - ret = mmubooke_get_physical_address(env, ctx, eaddr, access_type); - break; - case POWERPC_MMU_BOOKE206: - ret = mmubooke206_get_physical_address(env, ctx, eaddr, access_type, - mmu_idx); - break; case POWERPC_MMU_REAL: if (real_mode) { ret = check_physical(env, ctx, eaddr, access_type); From 549685161da1d4c948eee0a4a3da6f9a6b879e83 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:44 +0200 Subject: [PATCH 50/72] target/ppc: Split off real mode cases in get_physical_address_wtlb() The real mode handling is identical in the remaining switch cases. Split off these common real mode cases into a separate conditional to leave only the else branches in the switch that are different. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 9f177b6976..b13150ce23 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1172,7 +1172,6 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, MMUAccessType access_type, int type, int mmu_idx) { - int ret = -1; bool real_mode; if (env->mmu_model == POWERPC_MMU_BOOKE) { @@ -1184,38 +1183,23 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) : !FIELD_EX64(env->msr, MSR, DR); + if (real_mode && (env->mmu_model == POWERPC_MMU_SOFT_6xx || + env->mmu_model == POWERPC_MMU_SOFT_4xx || + env->mmu_model == POWERPC_MMU_REAL)) { + return check_physical(env, ctx, eaddr, access_type); + } switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - ret = mmu6xx_get_physical_address(env, ctx, eaddr, access_type, - type); - } - break; - + return mmu6xx_get_physical_address(env, ctx, eaddr, access_type, type); case POWERPC_MMU_SOFT_4xx: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - ret = mmu40x_get_physical_address(env, ctx, eaddr, access_type); - } - break; + return mmu40x_get_physical_address(env, ctx, eaddr, access_type); case POWERPC_MMU_REAL: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - cpu_abort(env_cpu(env), - "PowerPC in real mode do not do any translation\n"); - } - return -1; + cpu_abort(env_cpu(env), + "PowerPC in real mode do not do any translation\n"); default: cpu_abort(env_cpu(env), "Unknown or invalid MMU model\n"); - return -1; } - - return ret; } static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, From 77d9607d711b723b4873764051c4d5dc84894422 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:45 +0200 Subject: [PATCH 51/72] target/ppc: Inline and remove check_physical() This function just does two assignments and and unnecessary check that is always true so inline it in the only caller left and remove it. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index b13150ce23..2f412dd7c5 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1145,28 +1145,6 @@ void dump_mmu(CPUPPCState *env) } } -static int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, - MMUAccessType access_type) -{ - ctx->raddr = eaddr; - ctx->prot = PAGE_READ | PAGE_EXEC; - - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_REAL: - case POWERPC_MMU_BOOKE: - ctx->prot |= PAGE_WRITE; - break; - - default: - /* Caller's checks mean we should never get here for other models */ - g_assert_not_reached(); - } - - return 0; -} - int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, MMUAccessType access_type, int type, @@ -1186,7 +1164,9 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, if (real_mode && (env->mmu_model == POWERPC_MMU_SOFT_6xx || env->mmu_model == POWERPC_MMU_SOFT_4xx || env->mmu_model == POWERPC_MMU_REAL)) { - return check_physical(env, ctx, eaddr, access_type); + ctx->raddr = eaddr; + ctx->prot = PAGE_RWX; + return 0; } switch (env->mmu_model) { From 47bededc299c5cd0cbbf10660405d7076361fbaa Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:47 +0200 Subject: [PATCH 52/72] target/ppc: Fix misindented qemu_log_mask() calls Fix several qemu_log_mask() calls that are misindented. Acked-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 2f412dd7c5..124148b3da 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -315,8 +315,8 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, int ret = -1; bool ifetch = access_type == MMU_INST_FETCH; - qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', virtual); + qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__, + ifetch ? 'I' : 'D', virtual); if (ifetch) { BATlt = env->IBAT[1]; BATut = env->IBAT[0]; @@ -330,9 +330,9 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, BEPIu = *BATu & 0xF0000000; BEPIl = *BATu & 0x0FFE0000; bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); - qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " - TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); + qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " + TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, + ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); if ((virtual & 0xF0000000) == BEPIu && ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { /* BAT matches */ @@ -364,12 +364,11 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, BEPIu = *BATu & 0xF0000000; BEPIl = *BATu & 0x0FFE0000; bl = (*BATu & 0x00001FFC) << 15; - qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " - TARGET_FMT_lx " BATu " TARGET_FMT_lx - " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " - TARGET_FMT_lx " " TARGET_FMT_lx "\n", - __func__, ifetch ? 'I' : 'D', i, virtual, - *BATu, *BATl, BEPIu, BEPIl, bl); + qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx + " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx + "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " + TARGET_FMT_lx "\n", __func__, ifetch ? 'I' : 'D', + i, virtual, *BATu, *BATl, BEPIu, BEPIl, bl); } } } @@ -415,9 +414,8 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, hash = vsid ^ pgidx; ctx->ptem = (vsid << 7) | (pgidx >> 10); - qemu_log_mask(CPU_LOG_MMU, - "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", - ctx->key, ds, ctx->nx, vsid); + qemu_log_mask(CPU_LOG_MMU, "pte segment: key=%d ds %d nx %d vsid " + TARGET_FMT_lx "\n", ctx->key, ds, ctx->nx, vsid); if (!ds) { /* Check if instruction fetch is allowed, if needed */ if (type == ACCESS_CODE && ctx->nx) { @@ -583,9 +581,9 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return 0; } } - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); + qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx + " => " HWADDR_FMT_plx " %d %d\n", + __func__, address, raddr, ctx->prot, ret); return ret; } @@ -704,11 +702,11 @@ int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, } mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); - qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx - " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" - HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", - __func__, address, pid, tlb->mas1, tlb->mas2, mask, - tlb->mas7_3, tlb->mas8); + qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx + " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" + HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", + __func__, address, pid, tlb->mas1, tlb->mas2, mask, + tlb->mas7_3, tlb->mas8); /* Check PID */ tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; From 9e9ca54cdb493721f8444030e6dcf680400c8d0b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:48 +0200 Subject: [PATCH 53/72] target/ppc: Deindent ppc_jumbo_xlate() Instead of putting a large block of code in an if, invert the condition and return early to be able to deindent the code block. Acked-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 331 ++++++++++++++++++++-------------------- 1 file changed, 165 insertions(+), 166 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 124148b3da..f40481b4b1 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1264,187 +1264,186 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, *protp = ctx.prot; *psizep = TARGET_PAGE_BITS; return true; + } else if (!guest_visible) { + return false; } - if (guest_visible) { - log_cpu_state_mask(CPU_LOG_MMU, cs, 0); - if (type == ACCESS_CODE) { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - cs->exception_index = POWERPC_EXCP_IFTLB; - env->error_code = 1 << 18; - env->spr[SPR_IMISS] = eaddr; - env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; - goto tlb_miss; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - env->spr[SPR_40x_ESR] = 0x00000000; - break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); - break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + if (type == ACCESS_CODE) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + cs->exception_index = POWERPC_EXCP_IFTLB; + env->error_code = 1 << 18; + env->spr[SPR_IMISS] = eaddr; + env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; + goto tlb_miss; + case POWERPC_MMU_SOFT_4xx: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + env->spr[SPR_40x_ESR] = 0x00000000; + break; + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); + break; + case POWERPC_MMU_REAL: + cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); - default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); - } - break; - case -2: - /* Access rights violation */ - cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x08000000; - } - break; - case -3: - /* No execute protection violation */ - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_ESR] = 0x00000000; - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } - cs->exception_index = POWERPC_EXCP_ISI; - break; - case -4: - /* Direct store exception */ - /* No code fetch is allowed in direct-store areas */ - cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } - break; + default: + cpu_abort(cs, "Unknown or invalid MMU model\n"); } - } else { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - if (access_type == MMU_DATA_STORE) { - cs->exception_index = POWERPC_EXCP_DSTLB; - env->error_code = 1 << 16; - } else { - cs->exception_index = POWERPC_EXCP_DLTLB; - env->error_code = 0; - } - env->spr[SPR_DMISS] = eaddr; - env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; - tlb_miss: - env->error_code |= ctx.key << 19; - env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[0]); - env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[1]); - break; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_DTLB; + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->error_code = 0; + } else { + env->error_code = 0x08000000; + } + break; + case -3: + /* No execute protection violation */ + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_ESR] = 0x00000000; + env->error_code = 0; + } else { + env->error_code = 0x10000000; + } + cs->exception_index = POWERPC_EXCP_ISI; + break; + case -4: + /* Direct store exception */ + /* No code fetch is allowed in direct-store areas */ + cs->exception_index = POWERPC_EXCP_ISI; + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->error_code = 0; + } else { + env->error_code = 0x10000000; + } + break; + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_SOFT_6xx: + if (access_type == MMU_DATA_STORE) { + cs->exception_index = POWERPC_EXCP_DSTLB; + env->error_code = 1 << 16; + } else { + cs->exception_index = POWERPC_EXCP_DLTLB; env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] = 0x00800000; - } else { - env->spr[SPR_40x_ESR] = 0x00000000; - } - break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); - default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); + } + env->spr[SPR_DMISS] = eaddr; + env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; + tlb_miss: + env->error_code |= ctx.key << 19; + env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[0]); + env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[1]); + break; + case POWERPC_MMU_SOFT_4xx: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] = 0x00800000; + } else { + env->spr[SPR_40x_ESR] = 0x00000000; } break; - case -2: - /* Access rights violation */ + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + case POWERPC_MMU_REAL: + cpu_abort(cs, "PowerPC in real mode should never raise " + "any MMU exceptions\n"); + default: + cpu_abort(cs, "Unknown or invalid MMU model\n"); + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + if (env->mmu_model == POWERPC_MMU_SOFT_4xx) { + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] |= 0x00800000; + } + } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + } else { + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + break; + case -4: + /* Direct store exception */ + switch (type) { + case ACCESS_FLOAT: + /* Floating point load/store */ + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = eaddr; + break; + case ACCESS_RES: + /* lwarx, ldarx or stwcx. */ cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; - if (env->mmu_model == POWERPC_MMU_SOFT_4xx) { - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] |= 0x00800000; - } - } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x06000000; } else { - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; - } + env->spr[SPR_DSISR] = 0x04000000; } break; - case -4: - /* Direct store exception */ - switch (type) { - case ACCESS_FLOAT: - /* Floating point load/store */ - cs->exception_index = POWERPC_EXCP_ALIGN; - env->error_code = POWERPC_EXCP_ALIGN_FP; - env->spr[SPR_DAR] = eaddr; - break; - case ACCESS_RES: - /* lwarx, ldarx or stwcx. */ - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x06000000; - } else { - env->spr[SPR_DSISR] = 0x04000000; - } - break; - case ACCESS_EXT: - /* eciwx or ecowx */ - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x06100000; - } else { - env->spr[SPR_DSISR] = 0x04100000; - } - break; - default: - printf("DSI: invalid exception (%d)\n", ret); - cs->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; - env->spr[SPR_DAR] = eaddr; - break; + case ACCESS_EXT: + /* eciwx or ecowx */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x06100000; + } else { + env->spr[SPR_DSISR] = 0x04100000; } break; + default: + printf("DSI: invalid exception (%d)\n", ret); + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; + env->spr[SPR_DAR] = eaddr; + break; } + break; } } return false; From f178e4f8949ec75d0e1e34f9b1ace646d1e6a031 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:49 +0200 Subject: [PATCH 54/72] target/ppc: Replace hard coded constants in ppc_jumbo_xlate() The "2" in booke206_update_mas_tlb_miss() call corresponds to MMU_INST_FETCH which is the value of access_type in this branch; mmubooke206_esr() only checks for MMU_DATA_STORE and it's called from code access so using MMU_DATA_LOAD here seems wrong so replace it with access_type here as well that yields the same result. This also makes these calls the same as the data access branch further down. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index f40481b4b1..6570b280ca 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1287,13 +1287,13 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_ESR] = 0x00000000; break; case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); /* fall through */ case POWERPC_MMU_BOOKE: cs->exception_index = POWERPC_EXCP_ITLB; env->error_code = 0; env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); break; case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " From 5cc867a679d4b5032284d30d22dad8e81195e60d Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:50 +0200 Subject: [PATCH 55/72] target/ppc: Don't use mmu_ctx_t for mmu40x_get_physical_address() mmu40x_get_physical_address() only uses the raddr and prot fields from mmu_ctx_t. Pass these directly instead of using a ctx struct. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 6570b280ca..adce6cceb8 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -519,20 +519,18 @@ int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) return -1; } -static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, +static int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, MMUAccessType access_type) { ppcemb_tlb_t *tlb; - hwaddr raddr; int i, ret, zsel, zpr, pr; ret = -1; - raddr = (hwaddr)-1ULL; pr = FIELD_EX64(env->msr, MSR, PR); for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; - if (!ppcemb_tlb_check(env, tlb, &raddr, address, + if (!ppcemb_tlb_check(env, tlb, raddr, address, env->spr[SPR_40x_PID], i)) { continue; } @@ -550,40 +548,34 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* fall through */ case 0x3: /* All accesses granted */ - ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + *prot = PAGE_RWX; ret = 0; break; + case 0x0: if (pr != 0) { /* Raise Zone protection fault. */ env->spr[SPR_40x_ESR] = 1 << 22; - ctx->prot = 0; + *prot = 0; ret = -2; break; } /* fall through */ case 0x1: - check_perms: +check_perms: /* Check from TLB entry */ - ctx->prot = tlb->prot; - ret = check_prot(ctx->prot, access_type); + *prot = tlb->prot; + ret = check_prot(*prot, access_type); if (ret == -2) { env->spr[SPR_40x_ESR] = 0; } break; } - if (ret >= 0) { - ctx->raddr = raddr; - qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " HWADDR_FMT_plx - " %d %d\n", __func__, address, ctx->raddr, ctx->prot, - ret); - return 0; - } } - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", - __func__, address, raddr, ctx->prot, ret); + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? 0 : *raddr, *prot, ret); return ret; } @@ -1171,7 +1163,8 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, case POWERPC_MMU_SOFT_6xx: return mmu6xx_get_physical_address(env, ctx, eaddr, access_type, type); case POWERPC_MMU_SOFT_4xx: - return mmu40x_get_physical_address(env, ctx, eaddr, access_type); + return mmu40x_get_physical_address(env, &ctx->raddr, &ctx->prot, eaddr, + access_type); case POWERPC_MMU_REAL: cpu_abort(env_cpu(env), "PowerPC in real mode do not do any translation\n"); From ecff3394a81f536fc537878cb9bfbc48b3f8690e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:51 +0200 Subject: [PATCH 56/72] target/ppc: Don't use mmu_ctx_t in mmubooke_get_physical_address() mmubooke_get_physical_address() only uses the raddr and prot fields from mmu_ctx_t. Pass these directly instead of using a ctx struct. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index adce6cceb8..12dac9e63a 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -634,36 +634,25 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, return access_type == MMU_INST_FETCH ? -3 : -2; } -static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, +static int mmubooke_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, MMUAccessType access_type) { ppcemb_tlb_t *tlb; - hwaddr raddr; - int i, ret; + int i, ret = -1; - ret = -1; - raddr = (hwaddr)-1ULL; for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; - ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, + ret = mmubooke_check_tlb(env, tlb, raddr, prot, address, access_type, i); if (ret != -1) { break; } } - - if (ret >= 0) { - ctx->raddr = raddr; - qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, - address, ctx->raddr, ctx->prot, ret); - } else { - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, - address, raddr, ctx->prot, ret); - } - + qemu_log_mask(CPU_LOG_MMU, + "%s: access %s " TARGET_FMT_lx " => " HWADDR_FMT_plx + " %d %d\n", __func__, ret < 0 ? "refused" : "granted", + address, ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); return ret; } @@ -1143,7 +1132,8 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, bool real_mode; if (env->mmu_model == POWERPC_MMU_BOOKE) { - return mmubooke_get_physical_address(env, ctx, eaddr, access_type); + return mmubooke_get_physical_address(env, &ctx->raddr, &ctx->prot, + eaddr, access_type); } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { return mmubooke206_get_physical_address(env, ctx, eaddr, access_type, mmu_idx); From e8a9c0fbffe80946ae1a6004b77a18a030cce6f1 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:52 +0200 Subject: [PATCH 57/72] target/ppc: Don't use mmu_ctx_t in mmubooke206_get_physical_address() mmubooke206_get_physical_address() only uses the raddr and prot fields from mmu_ctx_t. Pass these directly instead of using a ctx struct. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 12dac9e63a..004ea2111d 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -836,27 +836,22 @@ found_tlb: return access_type == MMU_INST_FETCH ? -3 : -2; } -static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, +static int mmubooke206_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, MMUAccessType access_type, int mmu_idx) { ppcmas_tlb_t *tlb; - hwaddr raddr; - int i, j, ret; - - ret = -1; - raddr = (hwaddr)-1ULL; + int i, j, ret = -1; for (i = 0; i < BOOKE206_MAX_TLBN; i++) { int ways = booke206_tlb_ways(env, i); - for (j = 0; j < ways; j++) { tlb = booke206_get_tlbm(env, i, address, j); if (!tlb) { continue; } - ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, + ret = mmubooke206_check_tlb(env, tlb, raddr, prot, address, access_type, mmu_idx); if (ret != -1) { goto found_tlb; @@ -866,17 +861,10 @@ static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, found_tlb: - if (ret >= 0) { - ctx->raddr = raddr; - qemu_log_mask(CPU_LOG_MMU, "%s: access granted " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, address, - ctx->raddr, ctx->prot, ret); - } else { - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " HWADDR_FMT_plx " %d %d\n", __func__, address, - raddr, ctx->prot, ret); - } - + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); return ret; } @@ -1135,8 +1123,8 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, return mmubooke_get_physical_address(env, &ctx->raddr, &ctx->prot, eaddr, access_type); } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { - return mmubooke206_get_physical_address(env, ctx, eaddr, access_type, - mmu_idx); + return mmubooke206_get_physical_address(env, &ctx->raddr, &ctx->prot, + eaddr, access_type, mmu_idx); } real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) From aa30aa7d8e9232cb1bfdc4feb12b03095c2ff519 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:53 +0200 Subject: [PATCH 58/72] target/ppc: Remove BookE from direct store handling As BookE never returns -4 we can drop BookE from the direct store case in ppc_jumbo_xlate(). Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 004ea2111d..6c6c7c55b6 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1298,12 +1298,7 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, /* Direct store exception */ /* No code fetch is allowed in direct-store areas */ cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } + env->error_code = 0x10000000; break; } } else { From ba91e5d0276607fd6f862b498603f94c16ec0e07 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:54 +0200 Subject: [PATCH 59/72] target/ppc: Split off BookE handling from ppc_jumbo_xlate() Introduce ppc_booke_xlate() to handle BookE and BookE 2.06 cases to reduce ppc_jumbo_xlate() further. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 146 ++++++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 50 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 6c6c7c55b6..09a780bb7a 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1117,21 +1117,9 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, MMUAccessType access_type, int type, int mmu_idx) { - bool real_mode; - - if (env->mmu_model == POWERPC_MMU_BOOKE) { - return mmubooke_get_physical_address(env, &ctx->raddr, &ctx->prot, - eaddr, access_type); - } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { - return mmubooke206_get_physical_address(env, &ctx->raddr, &ctx->prot, - eaddr, access_type, mmu_idx); - } - - real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) - : !FIELD_EX64(env->msr, MSR, DR); - if (real_mode && (env->mmu_model == POWERPC_MMU_SOFT_6xx || - env->mmu_model == POWERPC_MMU_SOFT_4xx || - env->mmu_model == POWERPC_MMU_REAL)) { + bool real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) + : !FIELD_EX64(env->msr, MSR, DR); + if (real_mode) { ctx->raddr = eaddr; ctx->prot = PAGE_RWX; return 0; @@ -1205,6 +1193,93 @@ static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; } +static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + hwaddr raddr; + int prot, ret; + + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + ret = mmubooke206_get_physical_address(env, &raddr, &prot, eaddr, + access_type, mmu_idx); + } else { + ret = mmubooke_get_physical_address(env, &raddr, &prot, eaddr, + access_type); + } + if (ret == 0) { + *raddrp = raddr; + *protp = prot; + *psizep = TARGET_PAGE_BITS; + return true; + } else if (!guest_visible) { + return false; + } + + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + if (access_type == MMU_INST_FETCH) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + default: + g_assert_not_reached(); + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0; + break; + case -3: + /* No execute protection violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->spr[SPR_BOOKE_ESR] = 0; + env->error_code = 0; + break; + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + switch (env->mmu_model) { + case POWERPC_MMU_BOOKE206: + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + /* fall through */ + case POWERPC_MMU_BOOKE: + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + default: + g_assert_not_reached(); + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + } + } + return false; +} + /* Perform address translation */ /* TODO: Split this by mmu_model. */ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, @@ -1257,15 +1332,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_DEAR] = eaddr; env->spr[SPR_40x_ESR] = 0x00000000; break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); @@ -1276,23 +1342,12 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x08000000; - } + env->error_code = 0x08000000; break; case -3: /* No execute protection violation */ - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_ESR] = 0x00000000; - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; break; case -4: /* Direct store exception */ @@ -1333,15 +1388,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_ESR] = 0x00000000; } break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " "any MMU exceptions\n"); @@ -1358,10 +1404,6 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, if (access_type == MMU_DATA_STORE) { env->spr[SPR_40x_ESR] |= 0x00800000; } - } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); } else { env->spr[SPR_DAR] = eaddr; if (access_type == MMU_DATA_STORE) { @@ -1440,6 +1482,10 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_32B: return ppc_hash32_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_BOOKE: + case POWERPC_MMU_BOOKE206: + return ppc_booke_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); case POWERPC_MMU_MPC8xx: cpu_abort(env_cpu(&cpu->env), "MPC8xx MMU model is not implemented\n"); default: From aa20e1c8c642a1986f8e949af311d9bd2ee70f8e Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:55 +0200 Subject: [PATCH 60/72] target/ppc: Simplify ppc_booke_xlate() part 1 Move setting error_code that appears in every case out in front and hoist the common fall through case for BOOKE206 as well which allows removing the nested switches. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 41 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 09a780bb7a..611092966b 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1220,58 +1220,41 @@ static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, } log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + env->error_code = 0; + if (ret == -1) { + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + } + } if (access_type == MMU_INST_FETCH) { switch (ret) { case -1: /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - default: - g_assert_not_reached(); - } + cs->exception_index = POWERPC_EXCP_ITLB; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); break; case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0; break; case -3: /* No execute protection violation */ cs->exception_index = POWERPC_EXCP_ISI; env->spr[SPR_BOOKE_ESR] = 0; - env->error_code = 0; break; } } else { switch (ret) { case -1: /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - default: - g_assert_not_reached(); - } + cs->exception_index = POWERPC_EXCP_DTLB; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); break; case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; env->spr[SPR_BOOKE_DEAR] = eaddr; env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); break; From b18489b326c0677ed7d0178361a65df867b4a16f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:56 +0200 Subject: [PATCH 61/72] target/ppc: Simplify ppc_booke_xlate() part 2 Merge the code fetch and data access cases in a common switch. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 52 ++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 611092966b..8599106f75 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1221,45 +1221,33 @@ static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, log_cpu_state_mask(CPU_LOG_MMU, cs, 0); env->error_code = 0; - if (ret == -1) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ if (env->mmu_model == POWERPC_MMU_BOOKE206) { booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); } - } - if (access_type == MMU_INST_FETCH) { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - cs->exception_index = POWERPC_EXCP_ITLB; + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ITLB : POWERPC_EXCP_DTLB; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + case -2: + /* Access rights violation */ + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ISI : POWERPC_EXCP_DSI; + if (access_type != MMU_INST_FETCH) { env->spr[SPR_BOOKE_DEAR] = eaddr; env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case -2: - /* Access rights violation */ - cs->exception_index = POWERPC_EXCP_ISI; - break; - case -3: - /* No execute protection violation */ - cs->exception_index = POWERPC_EXCP_ISI; - env->spr[SPR_BOOKE_ESR] = 0; - break; - } - } else { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - cs->exception_index = POWERPC_EXCP_DTLB; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case -2: - /* Access rights violation */ - cs->exception_index = POWERPC_EXCP_DSI; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; } + break; + case -3: + /* No execute protection violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->spr[SPR_BOOKE_ESR] = 0; + break; } + return false; } From c29f808af5b14d24ec8591be46c37441c7c15663 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:57 +0200 Subject: [PATCH 62/72] target/ppc: Split off real mode handling from get_physical_address_wtlb() Add ppc_real_mode_xlate() to handle real mode translation and allow removing this case from ppc_jumbo_xlate(). Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 46 ++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 8599106f75..ab912da821 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1117,23 +1117,12 @@ int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, MMUAccessType access_type, int type, int mmu_idx) { - bool real_mode = (type == ACCESS_CODE) ? !FIELD_EX64(env->msr, MSR, IR) - : !FIELD_EX64(env->msr, MSR, DR); - if (real_mode) { - ctx->raddr = eaddr; - ctx->prot = PAGE_RWX; - return 0; - } - switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: return mmu6xx_get_physical_address(env, ctx, eaddr, access_type, type); case POWERPC_MMU_SOFT_4xx: return mmu40x_get_physical_address(env, &ctx->raddr, &ctx->prot, eaddr, access_type); - case POWERPC_MMU_REAL: - cpu_abort(env_cpu(env), - "PowerPC in real mode do not do any translation\n"); default: cpu_abort(env_cpu(env), "Unknown or invalid MMU model\n"); } @@ -1251,6 +1240,24 @@ static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, return false; } +static bool ppc_real_mode_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp) +{ + CPUPPCState *env = &cpu->env; + + if (access_type == MMU_INST_FETCH ? !FIELD_EX64(env->msr, MSR, IR) + : !FIELD_EX64(env->msr, MSR, DR)) { + *raddrp = eaddr; + *protp = PAGE_RWX; + *psizep = TARGET_PAGE_BITS; + return true; + } else if (env->mmu_model == POWERPC_MMU_REAL) { + cpu_abort(CPU(cpu), "PowerPC in real mode shold not do translation\n"); + } + return false; +} + /* Perform address translation */ /* TODO: Split this by mmu_model. */ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, @@ -1264,6 +1271,10 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, int type; int ret; + if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { + return true; + } + if (access_type == MMU_INST_FETCH) { /* code access */ type = ACCESS_CODE; @@ -1303,11 +1314,8 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_DEAR] = eaddr; env->spr[SPR_40x_ESR] = 0x00000000; break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); + g_assert_not_reached(); } break; case -2: @@ -1359,11 +1367,8 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, env->spr[SPR_40x_ESR] = 0x00000000; } break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); + g_assert_not_reached(); } break; case -2: @@ -1457,6 +1462,9 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_BOOKE206: return ppc_booke_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_REAL: + return ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, + protp); case POWERPC_MMU_MPC8xx: cpu_abort(env_cpu(&cpu->env), "MPC8xx MMU model is not implemented\n"); default: From 58b0132553139b481a4b6ea1c597465152381f66 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:58 +0200 Subject: [PATCH 63/72] target/ppc: Split off 40x cases from ppc_jumbo_xlate() Introduce ppc_40x_xlate() to split off 40x handlning leaving only 6xx in ppc_jumbo_xlate() now. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu_common.c | 150 +++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 57 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index ab912da821..ddb014e0aa 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1258,6 +1258,74 @@ static bool ppc_real_mode_xlate(PowerPCCPU *cpu, vaddr eaddr, return false; } +static bool ppc_40x_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int ret; + + if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { + return true; + } + + ret = mmu40x_get_physical_address(env, raddrp, protp, eaddr, access_type); + if (ret == 0) { + *psizep = TARGET_PAGE_BITS; + return true; + } else if (!guest_visible) { + return false; + } + + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + if (access_type == MMU_INST_FETCH) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + env->spr[SPR_40x_ESR] = 0x00000000; + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + break; + default: + g_assert_not_reached(); + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] = 0x00800000; + } else { + env->spr[SPR_40x_ESR] = 0x00000000; + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] |= 0x00800000; + } + break; + default: + g_assert_not_reached(); + } + } + return false; +} + /* Perform address translation */ /* TODO: Split this by mmu_model. */ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, @@ -1301,23 +1369,11 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, switch (ret) { case -1: /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - cs->exception_index = POWERPC_EXCP_IFTLB; - env->error_code = 1 << 18; - env->spr[SPR_IMISS] = eaddr; - env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; - goto tlb_miss; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - env->spr[SPR_40x_ESR] = 0x00000000; - break; - default: - g_assert_not_reached(); - } - break; + cs->exception_index = POWERPC_EXCP_IFTLB; + env->error_code = 1 << 18; + env->spr[SPR_IMISS] = eaddr; + env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; + goto tlb_miss; case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_ISI; @@ -1339,54 +1395,31 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, switch (ret) { case -1: /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - if (access_type == MMU_DATA_STORE) { - cs->exception_index = POWERPC_EXCP_DSTLB; - env->error_code = 1 << 16; - } else { - cs->exception_index = POWERPC_EXCP_DLTLB; - env->error_code = 0; - } - env->spr[SPR_DMISS] = eaddr; - env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; - tlb_miss: - env->error_code |= ctx.key << 19; - env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[0]); - env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[1]); - break; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_DTLB; + if (access_type == MMU_DATA_STORE) { + cs->exception_index = POWERPC_EXCP_DSTLB; + env->error_code = 1 << 16; + } else { + cs->exception_index = POWERPC_EXCP_DLTLB; env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] = 0x00800000; - } else { - env->spr[SPR_40x_ESR] = 0x00000000; - } - break; - default: - g_assert_not_reached(); } + env->spr[SPR_DMISS] = eaddr; + env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; +tlb_miss: + env->error_code |= ctx.key << 19; + env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[0]); + env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ctx.hash[1]); break; case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; - if (env->mmu_model == POWERPC_MMU_SOFT_4xx) { - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] |= 0x00800000; - } + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x0A000000; } else { - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; - } + env->spr[SPR_DSISR] = 0x08000000; } break; case -4: @@ -1462,6 +1495,9 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_BOOKE206: return ppc_booke_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_SOFT_4xx: + return ppc_40x_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); case POWERPC_MMU_REAL: return ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp); From 6b9ea7f3452ecf58582392eefae85db24fc6003f Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:27:59 +0200 Subject: [PATCH 64/72] target/ppc: Transform ppc_jumbo_xlate() into ppc_6xx_xlate() Now that only 6xx cases left in ppc_jumbo_xlate() we can change it to ppc_6xx_xlate() also removing get_physical_address_wtlb(). Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/internal.h | 5 +---- target/ppc/mmu_common.c | 38 ++++++++++++-------------------------- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 98b41a970c..4a4f9b9ec8 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -262,10 +262,7 @@ typedef struct mmu_ctx_t mmu_ctx_t; bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); -int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, - MMUAccessType access_type, int type, - int mmu_idx); + /* Software driven TLB helpers */ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code); diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index ddb014e0aa..961062bca1 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1112,22 +1112,6 @@ void dump_mmu(CPUPPCState *env) } } -int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, - MMUAccessType access_type, int type, - int mmu_idx) -{ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - return mmu6xx_get_physical_address(env, ctx, eaddr, access_type, type); - case POWERPC_MMU_SOFT_4xx: - return mmu40x_get_physical_address(env, &ctx->raddr, &ctx->prot, eaddr, - access_type); - default: - cpu_abort(env_cpu(env), "Unknown or invalid MMU model\n"); - } -} - static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, MMUAccessType access_type, int mmu_idx) { @@ -1326,12 +1310,10 @@ static bool ppc_40x_xlate(PowerPCCPU *cpu, vaddr eaddr, return false; } -/* Perform address translation */ -/* TODO: Split this by mmu_model. */ -static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, - int mmu_idx, bool guest_visible) +static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; @@ -1353,8 +1335,10 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, type = ACCESS_INT; } - ret = get_physical_address_wtlb(env, &ctx, eaddr, access_type, - type, mmu_idx); + ctx.prot = 0; + ctx.hash[0] = 0; + ctx.hash[1] = 0; + ret = mmu6xx_get_physical_address(env, &ctx, eaddr, access_type, type); if (ret == 0) { *raddrp = ctx.raddr; *protp = ctx.prot; @@ -1498,14 +1482,16 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_SOFT_4xx: return ppc_40x_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_SOFT_6xx: + return ppc_6xx_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); case POWERPC_MMU_REAL: return ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp); case POWERPC_MMU_MPC8xx: cpu_abort(env_cpu(&cpu->env), "MPC8xx MMU model is not implemented\n"); default: - return ppc_jumbo_xlate(cpu, eaddr, access_type, raddrp, - psizep, protp, mmu_idx, guest_visible); + cpu_abort(CPU(cpu), "Unknown or invalid MMU model\n"); } } From 306b5320307d13e95e1d43b585879cb56b163f66 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:00 +0200 Subject: [PATCH 65/72] target/ppc: Move mmu_ctx_t type to mmu_common.c Remove mmu_ctx_t definition from internal.h as this type is only used within mmu_common.c. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/internal.h | 12 ------------ target/ppc/mmu_common.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 4a4f9b9ec8..4a90dd2584 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -257,8 +257,6 @@ static inline int prot_for_access_type(MMUAccessType access_type) /* PowerPC MMU emulation */ -typedef struct mmu_ctx_t mmu_ctx_t; - bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); @@ -266,16 +264,6 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, /* Software driven TLB helpers */ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code); -/* Context used internally during MMU translations */ -struct mmu_ctx_t { - hwaddr raddr; /* Real address */ - hwaddr eaddr; /* Effective address */ - int prot; /* Protection bits */ - hwaddr hash[2]; /* Pagetable hash values */ - target_ulong ptem; /* Virtual segment ID | API */ - int key; /* Access key */ - int nx; /* Non-execute area */ -}; #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 961062bca1..34200d9cb1 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -36,6 +36,17 @@ /* #define DUMP_PAGE_TABLES */ +/* Context used internally during MMU translations */ +typedef struct { + hwaddr raddr; /* Real address */ + hwaddr eaddr; /* Effective address */ + int prot; /* Protection bits */ + hwaddr hash[2]; /* Pagetable hash values */ + target_ulong ptem; /* Virtual segment ID | API */ + int key; /* Access key */ + int nx; /* Non-execute area */ +} mmu_ctx_t; + void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { PowerPCCPU *cpu = env_archcpu(env); From 5fd257f5994335c9446b0fa8b6cfd6102c2f74ca Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:02 +0200 Subject: [PATCH 66/72] target/ppc: Remove id_tlbs flag from CPU env This flag for split instruction/data TLBs is only set for 6xx soft TLB MMU model and not used otherwise so no need to have a separate flag for that. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- hw/ppc/pegasos2.c | 2 +- target/ppc/cpu.h | 5 ++++- target/ppc/cpu_init.c | 19 +++++-------------- target/ppc/helper_regs.c | 1 - target/ppc/mmu_common.c | 10 ++-------- target/ppc/mmu_helper.c | 12 ++---------- 6 files changed, 14 insertions(+), 35 deletions(-) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index c22e8b336d..c1bd8dfa21 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -985,7 +985,7 @@ static void *build_fdt(MachineState *machine, int *fdt_size) cpu->env.icache_line_size); qemu_fdt_setprop_cell(fdt, cp, "i-cache-line-size", cpu->env.icache_line_size); - if (cpu->env.id_tlbs) { + if (ppc_is_split_tlb(cpu)) { qemu_fdt_setprop_cell(fdt, cp, "i-tlb-sets", cpu->env.nb_ways); qemu_fdt_setprop_cell(fdt, cp, "i-tlb-size", cpu->env.tlb_per_way); qemu_fdt_setprop_cell(fdt, cp, "d-tlb-sets", cpu->env.nb_ways); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index e201b7f6c2..95cc11dff7 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1281,7 +1281,6 @@ struct CPUArchState { int tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */ int nb_ways; /* Number of ways in the TLB set */ int last_way; /* Last used way used to allocate TLB in a LRU way */ - int id_tlbs; /* If 1, MMU has separated TLBs for instructions & data */ int nb_pids; /* Number of available PID registers */ int tlb_type; /* Type of TLB we're dealing with */ ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ @@ -2897,6 +2896,10 @@ static inline void booke206_fixed_size_tlbn(CPUPPCState *env, const int tlbn, tlb->mas1 |= ((uint32_t)tsize) << MAS1_TSIZE_SHIFT; } +static inline bool ppc_is_split_tlb(PowerPCCPU *cpu) +{ + return cpu->env.tlb_type == TLB_6XX; +} #endif static inline bool msr_is_64bit(CPUPPCState *env, target_ulong msr) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 8cfaee61d9..9401eb28c3 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2137,7 +2137,6 @@ static void init_proc_405(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_4xx(env); @@ -2211,7 +2210,6 @@ static void init_proc_440EP(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); @@ -2311,7 +2309,6 @@ static void init_proc_440GP(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); @@ -2386,7 +2383,6 @@ static void init_proc_440x5(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_BookE(env); @@ -2754,7 +2750,6 @@ static void init_proc_e200(CPUPPCState *env) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; env->nb_ways = 1; - env->id_tlbs = 0; env->tlb_type = TLB_EMB; #endif init_excp_e200(env, 0xFFFF0000UL); @@ -2874,7 +2869,6 @@ static void init_proc_e500(CPUPPCState *env, int version) /* Memory management */ env->nb_pids = 3; env->nb_ways = 2; - env->id_tlbs = 0; switch (version) { case fsl_e500v1: tlbncfg[0] = register_tlbncfg(2, 1, 1, 0, 256); @@ -6927,20 +6921,17 @@ static void init_ppc_proc(PowerPCCPU *cpu) } /* Allocate TLBs buffer when needed */ #if !defined(CONFIG_USER_ONLY) - if (env->nb_tlb != 0) { - int nb_tlb = env->nb_tlb; - if (env->id_tlbs != 0) { - nb_tlb *= 2; - } + if (env->nb_tlb) { switch (env->tlb_type) { case TLB_6XX: - env->tlb.tlb6 = g_new0(ppc6xx_tlb_t, nb_tlb); + /* 6xx has separate TLBs for instructions and data hence times 2 */ + env->tlb.tlb6 = g_new0(ppc6xx_tlb_t, 2 * env->nb_tlb); break; case TLB_EMB: - env->tlb.tlbe = g_new0(ppcemb_tlb_t, nb_tlb); + env->tlb.tlbe = g_new0(ppcemb_tlb_t, env->nb_tlb); break; case TLB_MAS: - env->tlb.tlbm = g_new0(ppcmas_tlb_t, nb_tlb); + env->tlb.tlbm = g_new0(ppcmas_tlb_t, env->nb_tlb); break; } /* Pre-compute some useful values */ diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 16b43702d5..02076e96fb 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -730,7 +730,6 @@ void register_6xx_7xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = nb_tlbs; env->nb_ways = nb_ways; - env->id_tlbs = 1; env->tlb_type = TLB_6XX; spr_register(env, SPR_DMISS, "DMISS", SPR_NOACCESS, SPR_NOACCESS, diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 34200d9cb1..78bdbd506c 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -128,8 +128,8 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); /* Select TLB way */ nr += env->tlb_per_way * way; - /* 6xx have separate TLBs for instructions and data */ - if (is_code && env->id_tlbs == 1) { + /* 6xx has separate TLBs for instructions and data */ + if (is_code) { nr += env->nb_tlb; } @@ -1065,13 +1065,7 @@ static void mmu6xx_dump_mmu(CPUPPCState *env) mmu6xx_dump_BATs(env, ACCESS_INT); mmu6xx_dump_BATs(env, ACCESS_CODE); - if (env->id_tlbs != 1) { - qemu_printf("ERROR: 6xx MMU should have separated TLB" - " for code and data\n"); - } - qemu_printf("\nTLBs [EPN EPN + SIZE]\n"); - for (type = 0; type < 2; type++) { for (way = 0; way < env->nb_ways; way++) { for (entry = env->nb_tlb * type + env->tlb_per_way * way; diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index d4388e66be..238407a7f1 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -45,14 +45,8 @@ static inline void ppc6xx_tlb_invalidate_all(CPUPPCState *env) { ppc6xx_tlb_t *tlb; - int nr, max; + int nr, max = 2 * env->nb_tlb; - /* LOG_SWTLB("Invalidate all TLBs\n"); */ - /* Invalidate all defined software TLB */ - max = env->nb_tlb; - if (env->id_tlbs == 1) { - max *= 2; - } for (nr = 0; nr < max; nr++) { tlb = &env->tlb.tlb6[nr]; pte_invalidate(&tlb->pte0); @@ -308,9 +302,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: ppc6xx_tlb_invalidate_virt(env, addr, 0); - if (env->id_tlbs == 1) { - ppc6xx_tlb_invalidate_virt(env, addr, 1); - } + ppc6xx_tlb_invalidate_virt(env, addr, 1); break; case POWERPC_MMU_32B: /* From 581eea5d656b73c6532109f4ced4c73fd4e5fd47 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:03 +0200 Subject: [PATCH 67/72] target/ppc: Split off common embedded TLB init Several 4xx CPUs and e200 share the same TLB settings enclosed in an ifdef. Split it off in a common function to reduce code duplication and the number of ifdefs. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 46 ++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 9401eb28c3..b1ea301e22 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2127,18 +2127,22 @@ static int check_attn_hid0_power9(CPUPPCState *env) } #endif +static void init_tlbs_emb(CPUPPCState *env) +{ +#ifndef CONFIG_USER_ONLY + env->nb_tlb = 64; + env->nb_ways = 1; + env->tlb_type = TLB_EMB; +#endif +} + static void init_proc_405(CPUPPCState *env) { register_40x_sprs(env); register_405_sprs(env); register_usprgh_sprs(env); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + init_tlbs_emb(env); init_excp_4xx(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2206,12 +2210,8 @@ static void init_proc_440EP(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + + init_tlbs_emb(env); init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2305,12 +2305,7 @@ static void init_proc_440GP(CPUPPCState *env) register_440_sprs(env); register_usprgh_sprs(env); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + init_tlbs_emb(env); init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2379,12 +2374,8 @@ static void init_proc_440x5(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + + init_tlbs_emb(env); init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2747,11 +2738,8 @@ static void init_proc_e200(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->tlb_type = TLB_EMB; -#endif + + init_tlbs_emb(env); init_excp_e200(env, 0xFFFF0000UL); env->dcache_line_size = 32; env->icache_line_size = 32; From e89b0629b99e3df96ef1c190139f77169c58d27b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:05 +0200 Subject: [PATCH 68/72] target/ppc/mmu-hash32.c: Drop a local variable In ppc_hash32_xlate() the value of need_prop is checked in two places but precalculating it does not help because when we reach the first check we always return and not reach the second place so the value will only be used once. We can drop the local variable and calculate it when needed, which makes these checks using it similar to other places with such checks. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu-hash32.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index da6e8b293c..3abaf16e78 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -386,7 +386,6 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr pte_offset; ppc_hash_pte32_t pte; int prot; - int need_prot; hwaddr raddr; /* There are no hash32 large pages. */ @@ -400,13 +399,11 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, return true; } - need_prot = prot_for_access_type(access_type); - /* 2. Check Block Address Translation entries (BATs) */ if (env->nb_BATs != 0) { raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp, mmu_idx); if (raddr != -1) { - if (need_prot & ~*protp) { + if (prot_for_access_type(access_type) & ~*protp) { if (guest_visible) { if (access_type == MMU_INST_FETCH) { cs->exception_index = POWERPC_EXCP_ISI; @@ -474,7 +471,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, prot = ppc_hash32_pte_prot(mmu_idx, sr, pte); - if (need_prot & ~prot) { + if (prot_for_access_type(access_type) & ~prot) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); if (guest_visible) { From 950251ee7b459d160fd3adfa2792326aa29b5d6c Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:06 +0200 Subject: [PATCH 69/72] target/ppc/mmu-radix64.c: Drop a local variable The value is only used once so no need to introduce a local variable for it. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/mmu-radix64.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index fefa55a5f1..c1e4f00335 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -185,7 +185,6 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, int mmu_idx, bool partition_scoped) { CPUPPCState *env = &cpu->env; - int need_prot; /* Check Page Attributes (pte58:59) */ if ((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO && access_type == MMU_INST_FETCH) { @@ -210,8 +209,8 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, } /* Check if requested access type is allowed */ - need_prot = prot_for_access_type(access_type); - if (need_prot & ~*prot) { /* Page Protected for that Access */ + if (prot_for_access_type(access_type) & ~*prot) { + /* Page Protected for that Access */ *fault_cause |= access_type == MMU_INST_FETCH ? SRR1_NOEXEC_GUARD : DSISR_PROTFAULT; return true; From cd1038ec1d316aeca52a0c5d18da055d5aa014c9 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:07 +0200 Subject: [PATCH 70/72] target/ppc: Add a function to check for page protection bit Checking if a page protection bit is set for a given access type is a common operation. Add a function to avoid repeating the same check at multiple places. As this relies on access type and page protection bit values having certain relation also add an assert to ensure that this assumption holds. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/cpu_init.c | 5 +++++ target/ppc/internal.h | 25 ++++++------------------- target/ppc/mmu-hash32.c | 6 +++--- target/ppc/mmu-hash64.c | 2 +- target/ppc/mmu-radix64.c | 2 +- target/ppc/mmu_common.c | 26 +++++++++++++------------- 6 files changed, 29 insertions(+), 37 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index b1ea301e22..01e358a4a5 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7521,6 +7521,11 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &ppc_sysemu_ops; INTERRUPT_STATS_PROVIDER_CLASS(oc)->get_statistics = ppc_get_irq_stats; + + /* check_prot_access_type relies on MMU access and PAGE bits relations */ + qemu_build_assert(MMU_DATA_LOAD == 0 && MMU_DATA_STORE == 1 && + MMU_INST_FETCH == 2 && PAGE_READ == 1 && + PAGE_WRITE == 2 && PAGE_EXEC == 4); #endif cc->gdb_num_core_regs = 71; diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 4a90dd2584..20fb2ec593 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -234,27 +234,14 @@ void destroy_ppc_opcodes(PowerPCCPU *cpu); void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *ppc); const gchar *ppc_gdb_arch_name(CPUState *cs); -/** - * prot_for_access_type: - * @access_type: Access type - * - * Return the protection bit required for the given access type. - */ -static inline int prot_for_access_type(MMUAccessType access_type) -{ - switch (access_type) { - case MMU_INST_FETCH: - return PAGE_EXEC; - case MMU_DATA_LOAD: - return PAGE_READ; - case MMU_DATA_STORE: - return PAGE_WRITE; - } - g_assert_not_reached(); -} - #ifndef CONFIG_USER_ONLY +/* Check if permission bit required for the access_type is set in prot */ +static inline int check_prot_access_type(int prot, MMUAccessType access_type) +{ + return prot & (1 << access_type); +} + /* PowerPC MMU emulation */ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 3abaf16e78..1e8f1df0f0 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -252,7 +252,7 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, } *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ; - if (*prot & prot_for_access_type(access_type)) { + if (check_prot_access_type(*prot, access_type)) { *raddr = eaddr; return true; } @@ -403,7 +403,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, if (env->nb_BATs != 0) { raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp, mmu_idx); if (raddr != -1) { - if (prot_for_access_type(access_type) & ~*protp) { + if (!check_prot_access_type(*protp, access_type)) { if (guest_visible) { if (access_type == MMU_INST_FETCH) { cs->exception_index = POWERPC_EXCP_ISI; @@ -471,7 +471,7 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, prot = ppc_hash32_pte_prot(mmu_idx, sr, pte); - if (prot_for_access_type(access_type) & ~prot) { + if (!check_prot_access_type(prot, access_type)) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); if (guest_visible) { diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index accbf0b2d8..cbc8efa0c3 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -1089,7 +1089,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, amr_prot = ppc_hash64_amr_prot(cpu, pte); prot = exec_prot & pp_prot & amr_prot; - need_prot = prot_for_access_type(access_type); + need_prot = check_prot_access_type(PAGE_RWX, access_type); if (need_prot & ~prot) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index c1e4f00335..5a02e4963b 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -209,7 +209,7 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, } /* Check if requested access type is allowed */ - if (prot_for_access_type(access_type) & ~*prot) { + if (!check_prot_access_type(*prot, access_type)) { /* Page Protected for that Access */ *fault_cause |= access_type == MMU_INST_FETCH ? SRR1_NOEXEC_GUARD : DSISR_PROTFAULT; diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 78bdbd506c..5414a14aad 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -114,11 +114,6 @@ static int pp_check(int key, int pp, int nx) return access; } -static int check_prot(int prot, MMUAccessType access_type) -{ - return prot & prot_for_access_type(access_type) ? 0 : -2; -} - int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code) { @@ -165,13 +160,14 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, /* Keep the matching PTE information */ ctx->raddr = pte1; ctx->prot = access; - ret = check_prot(ctx->prot, access_type); - if (ret == 0) { + if (check_prot_access_type(ctx->prot, access_type)) { /* Access granted */ qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); + ret = 0; } else { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + ret = -2; } } } @@ -354,12 +350,14 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, (virtual & 0x0001F000); /* Compute access rights */ ctx->prot = prot; - ret = check_prot(ctx->prot, access_type); - if (ret == 0) { + if (check_prot_access_type(ctx->prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "BAT %d match: r " HWADDR_FMT_plx " prot=%c%c\n", i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', ctx->prot & PAGE_WRITE ? 'W' : '-'); + ret = 0; + } else { + ret = -2; } break; } @@ -576,9 +574,11 @@ static int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, check_perms: /* Check from TLB entry */ *prot = tlb->prot; - ret = check_prot(*prot, access_type); - if (ret == -2) { + if (check_prot_access_type(*prot, access_type)) { + ret = 0; + } else { env->spr[SPR_40x_ESR] = 0; + ret = -2; } break; } @@ -636,7 +636,7 @@ static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, } else { *prot = (tlb->prot >> 4) & 0xF; } - if (*prot & prot_for_access_type(access_type)) { + if (check_prot_access_type(*prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); return 0; } @@ -838,7 +838,7 @@ found_tlb: *prot |= PAGE_EXEC; } } - if (*prot & prot_for_access_type(access_type)) { + if (check_prot_access_type(*prot, access_type)) { qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); return 0; } From e7baac649bb3d9d72a3e79fc43e360d7ac99aead Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:08 +0200 Subject: [PATCH 71/72] target/ppc: Move out BookE and related MMU functions from mmu_common.c Add a new mmu-booke.c file for BookE and related MMU bits from mmu_common.c. Acked-by: Nicholas Piggin Signed-off-by: BALATON Zoltan Signed-off-by: Nicholas Piggin --- target/ppc/cpu.h | 4 - target/ppc/meson.build | 1 + target/ppc/mmu-booke.c | 531 ++++++++++++++++++++++++++++++++++++++++ target/ppc/mmu-booke.h | 17 ++ target/ppc/mmu_common.c | 507 +------------------------------------- target/ppc/mmu_helper.c | 1 + 6 files changed, 551 insertions(+), 510 deletions(-) create mode 100644 target/ppc/mmu-booke.c create mode 100644 target/ppc/mmu-booke.h diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 95cc11dff7..2015e603d4 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1642,10 +1642,6 @@ void ppc_tlb_invalidate_all(CPUPPCState *env); void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr); void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); void cpu_ppc_set_1lpar(PowerPCCPU *cpu); -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, - target_ulong address, uint32_t pid); -int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid); -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); #endif void ppc_store_fpscr(CPUPPCState *env, target_ulong val); diff --git a/target/ppc/meson.build b/target/ppc/meson.build index 0b89f9b89f..db3b7a0c33 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -37,6 +37,7 @@ ppc_system_ss.add(files( 'arch_dump.c', 'machine.c', 'mmu-hash32.c', + 'mmu-booke.c', 'mmu_common.c', 'ppc-qmp-cmds.c', )) diff --git a/target/ppc/mmu-booke.c b/target/ppc/mmu-booke.c new file mode 100644 index 0000000000..55e5dd7c6b --- /dev/null +++ b/target/ppc/mmu-booke.c @@ -0,0 +1,531 @@ +/* + * PowerPC BookE MMU, TLB emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "exec/page-protection.h" +#include "exec/log.h" +#include "cpu.h" +#include "internal.h" +#include "mmu-booke.h" + +/* Generic TLB check function for embedded PowerPC implementations */ +static bool ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddrp, + target_ulong address, uint32_t pid, int i) +{ + target_ulong mask; + + /* Check valid flag */ + if (!(tlb->prot & PAGE_VALID)) { + return false; + } + mask = ~(tlb->size - 1); + qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx + " PID %u <=> " TARGET_FMT_lx " " TARGET_FMT_lx " %u %x\n", + __func__, i, address, pid, tlb->EPN, + mask, (uint32_t)tlb->PID, tlb->prot); + /* Check PID */ + if (tlb->PID != 0 && tlb->PID != pid) { + return false; + } + /* Check effective address */ + if ((address & mask) != tlb->EPN) { + return false; + } + *raddrp = (tlb->RPN & mask) | (address & ~mask); + return true; +} + +/* Generic TLB search function for PowerPC embedded implementations */ +int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) +{ + ppcemb_tlb_t *tlb; + hwaddr raddr; + int i; + + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i)) { + return i; + } + } + return -1; +} + +int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type) +{ + ppcemb_tlb_t *tlb; + int i, ret, zsel, zpr, pr; + + ret = -1; + pr = FIELD_EX64(env->msr, MSR, PR); + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + if (!ppcemb_tlb_check(env, tlb, raddr, address, + env->spr[SPR_40x_PID], i)) { + continue; + } + zsel = (tlb->attr >> 4) & 0xF; + zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; + qemu_log_mask(CPU_LOG_MMU, + "%s: TLB %d zsel %d zpr %d ty %d attr %08x\n", + __func__, i, zsel, zpr, access_type, tlb->attr); + /* Check execute enable bit */ + switch (zpr) { + case 0x2: + if (pr != 0) { + goto check_perms; + } + /* fall through */ + case 0x3: + /* All accesses granted */ + *prot = PAGE_RWX; + ret = 0; + break; + + case 0x0: + if (pr != 0) { + /* Raise Zone protection fault. */ + env->spr[SPR_40x_ESR] = 1 << 22; + *prot = 0; + ret = -2; + break; + } + /* fall through */ + case 0x1: +check_perms: + /* Check from TLB entry */ + *prot = tlb->prot; + if (check_prot_access_type(*prot, access_type)) { + ret = 0; + } else { + env->spr[SPR_40x_ESR] = 0; + ret = -2; + } + break; + } + } + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? 0 : *raddr, *prot, ret); + + return ret; +} + +static bool mmubooke_check_pid(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddr, target_ulong addr, int i) +{ + if (ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID], i)) { + if (!env->nb_pids) { + /* Extend the physical address to 36 bits */ + *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; + } + return true; + } else if (!env->nb_pids) { + return false; + } + if (env->spr[SPR_BOOKE_PID1] && + ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID1], i)) { + return true; + } + if (env->spr[SPR_BOOKE_PID2] && + ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID2], i)) { + return true; + } + return false; +} + +static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddr, int *prot, target_ulong address, + MMUAccessType access_type, int i) +{ + if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { + qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); + return -1; + } + + /* Check the address space */ + if ((access_type == MMU_INST_FETCH ? + FIELD_EX64(env->msr, MSR, IR) : + FIELD_EX64(env->msr, MSR, DR)) != (tlb->attr & 1)) { + qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); + return -1; + } + + if (FIELD_EX64(env->msr, MSR, PR)) { + *prot = tlb->prot & 0xF; + } else { + *prot = (tlb->prot >> 4) & 0xF; + } + if (check_prot_access_type(*prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); + return 0; + } + + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); + return access_type == MMU_INST_FETCH ? -3 : -2; +} + +static int mmubooke_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, + MMUAccessType access_type) +{ + ppcemb_tlb_t *tlb; + int i, ret = -1; + + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + ret = mmubooke_check_tlb(env, tlb, raddr, prot, address, + access_type, i); + if (ret != -1) { + break; + } + } + qemu_log_mask(CPU_LOG_MMU, + "%s: access %s " TARGET_FMT_lx " => " HWADDR_FMT_plx + " %d %d\n", __func__, ret < 0 ? "refused" : "granted", + address, ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); + return ret; +} + +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) +{ + int tlbm_size; + + tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + + return 1024ULL << tlbm_size; +} + +/* TLB check function for MAS based SoftTLBs */ +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, + target_ulong address, uint32_t pid) +{ + hwaddr mask; + uint32_t tlb_pid; + + if (!FIELD_EX64(env->msr, MSR, CM)) { + /* In 32bit mode we can only address 32bit EAs */ + address = (uint32_t)address; + } + + /* Check valid flag */ + if (!(tlb->mas1 & MAS1_VALID)) { + return -1; + } + + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx + " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" + HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", + __func__, address, pid, tlb->mas1, tlb->mas2, mask, + tlb->mas7_3, tlb->mas8); + + /* Check PID */ + tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; + if (tlb_pid != 0 && tlb_pid != pid) { + return -1; + } + + /* Check effective address */ + if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { + return -1; + } + + if (raddrp) { + *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); + } + + return 0; +} + +static bool is_epid_mmu(int mmu_idx) +{ + return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; +} + +static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type) +{ + uint32_t esr = 0; + if (access_type == MMU_DATA_STORE) { + esr |= ESR_ST; + } + if (is_epid_mmu(mmu_idx)) { + esr |= ESR_EPID; + } + return esr; +} + +/* + * Get EPID register given the mmu_idx. If this is regular load, + * construct the EPID access bits from current processor state + * + * Get the effective AS and PR bits and the PID. The PID is returned + * only if EPID load is requested, otherwise the caller must detect + * the correct EPID. Return true if valid EPID is returned. + */ +static bool mmubooke206_get_as(CPUPPCState *env, + int mmu_idx, uint32_t *epid_out, + bool *as_out, bool *pr_out) +{ + if (is_epid_mmu(mmu_idx)) { + uint32_t epidr; + if (mmu_idx == PPC_TLB_EPID_STORE) { + epidr = env->spr[SPR_BOOKE_EPSC]; + } else { + epidr = env->spr[SPR_BOOKE_EPLC]; + } + *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT; + *as_out = !!(epidr & EPID_EAS); + *pr_out = !!(epidr & EPID_EPR); + return true; + } else { + *as_out = FIELD_EX64(env->msr, MSR, DS); + *pr_out = FIELD_EX64(env->msr, MSR, PR); + return false; + } +} + +/* Check if the tlb found by hashing really matches */ +static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + uint32_t epid; + bool as, pr; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + + if (!use_epid) { + if (ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2]) >= 0) { + goto found_tlb; + } + } else { + if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) { + goto found_tlb; + } + } + + qemu_log_mask(CPU_LOG_MMU, "%s: No TLB entry found for effective address " + "0x" TARGET_FMT_lx "\n", __func__, address); + return -1; + +found_tlb: + + /* Check the address space and permissions */ + if (access_type == MMU_INST_FETCH) { + /* There is no way to fetch code using epid load */ + assert(!use_epid); + as = FIELD_EX64(env->msr, MSR, IR); + } + + if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = 0; + if (pr) { + if (tlb->mas7_3 & MAS3_UR) { + *prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_UW) { + *prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_UX) { + *prot |= PAGE_EXEC; + } + } else { + if (tlb->mas7_3 & MAS3_SR) { + *prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_SW) { + *prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_SX) { + *prot |= PAGE_EXEC; + } + } + if (check_prot_access_type(*prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); + return 0; + } + + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); + return access_type == MMU_INST_FETCH ? -3 : -2; +} + +static int mmubooke206_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, + MMUAccessType access_type, + int mmu_idx) +{ + ppcmas_tlb_t *tlb; + int i, j, ret = -1; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + if (!tlb) { + continue; + } + ret = mmubooke206_check_tlb(env, tlb, raddr, prot, address, + access_type, mmu_idx); + if (ret != -1) { + goto found_tlb; + } + } + } + +found_tlb: + + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); + return ret; +} + +static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + uint32_t epid; + bool as, pr; + uint32_t missed_tid = 0; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + + if (access_type == MMU_INST_FETCH) { + as = FIELD_EX64(env->msr, MSR, IR); + } + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS6] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + /* AS */ + if (as) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; + } + + env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; + env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; + + if (!use_epid) { + switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { + case MAS4_TIDSELD_PID0: + missed_tid = env->spr[SPR_BOOKE_PID]; + break; + case MAS4_TIDSELD_PID1: + missed_tid = env->spr[SPR_BOOKE_PID1]; + break; + case MAS4_TIDSELD_PID2: + missed_tid = env->spr[SPR_BOOKE_PID2]; + break; + } + env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; + } else { + missed_tid = epid; + env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; + } + env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT); + + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + +bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, + bool guest_visible) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + hwaddr raddr; + int prot, ret; + + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + ret = mmubooke206_get_physical_address(env, &raddr, &prot, eaddr, + access_type, mmu_idx); + } else { + ret = mmubooke_get_physical_address(env, &raddr, &prot, eaddr, + access_type); + } + if (ret == 0) { + *raddrp = raddr; + *protp = prot; + *psizep = TARGET_PAGE_BITS; + return true; + } else if (!guest_visible) { + return false; + } + + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + env->error_code = 0; + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + } + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ITLB : POWERPC_EXCP_DTLB; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + case -2: + /* Access rights violation */ + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ISI : POWERPC_EXCP_DSI; + if (access_type != MMU_INST_FETCH) { + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + } + break; + case -3: + /* No execute protection violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->spr[SPR_BOOKE_ESR] = 0; + break; + } + + return false; +} diff --git a/target/ppc/mmu-booke.h b/target/ppc/mmu-booke.h new file mode 100644 index 0000000000..f972843bbb --- /dev/null +++ b/target/ppc/mmu-booke.h @@ -0,0 +1,17 @@ +#ifndef PPC_MMU_BOOKE_H +#define PPC_MMU_BOOKE_H + +#include "cpu.h" + +int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid); +int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type); +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, + target_ulong address, uint32_t pid); +bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, + bool guest_visible); + +#endif diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 5414a14aad..2c75e53250 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -33,6 +33,7 @@ #include "internal.h" #include "mmu-book3s-v3.h" #include "mmu-radix64.h" +#include "mmu-booke.h" /* #define DUMP_PAGE_TABLES */ @@ -484,401 +485,6 @@ static int mmu6xx_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return -2; } -/* Generic TLB check function for embedded PowerPC implementations */ -static bool ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddrp, - target_ulong address, uint32_t pid, int i) -{ - target_ulong mask; - - /* Check valid flag */ - if (!(tlb->prot & PAGE_VALID)) { - return false; - } - mask = ~(tlb->size - 1); - qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx - " PID %u <=> " TARGET_FMT_lx " " TARGET_FMT_lx " %u %x\n", - __func__, i, address, pid, tlb->EPN, - mask, (uint32_t)tlb->PID, tlb->prot); - /* Check PID */ - if (tlb->PID != 0 && tlb->PID != pid) { - return false; - } - /* Check effective address */ - if ((address & mask) != tlb->EPN) { - return false; - } - *raddrp = (tlb->RPN & mask) | (address & ~mask); - return true; -} - -/* Generic TLB search function for PowerPC embedded implementations */ -int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid) -{ - ppcemb_tlb_t *tlb; - hwaddr raddr; - int i; - - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, i)) { - return i; - } - } - return -1; -} - -static int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, - int *prot, target_ulong address, - MMUAccessType access_type) -{ - ppcemb_tlb_t *tlb; - int i, ret, zsel, zpr, pr; - - ret = -1; - pr = FIELD_EX64(env->msr, MSR, PR); - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - if (!ppcemb_tlb_check(env, tlb, raddr, address, - env->spr[SPR_40x_PID], i)) { - continue; - } - zsel = (tlb->attr >> 4) & 0xF; - zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; - qemu_log_mask(CPU_LOG_MMU, - "%s: TLB %d zsel %d zpr %d ty %d attr %08x\n", - __func__, i, zsel, zpr, access_type, tlb->attr); - /* Check execute enable bit */ - switch (zpr) { - case 0x2: - if (pr != 0) { - goto check_perms; - } - /* fall through */ - case 0x3: - /* All accesses granted */ - *prot = PAGE_RWX; - ret = 0; - break; - - case 0x0: - if (pr != 0) { - /* Raise Zone protection fault. */ - env->spr[SPR_40x_ESR] = 1 << 22; - *prot = 0; - ret = -2; - break; - } - /* fall through */ - case 0x1: -check_perms: - /* Check from TLB entry */ - *prot = tlb->prot; - if (check_prot_access_type(*prot, access_type)) { - ret = 0; - } else { - env->spr[SPR_40x_ESR] = 0; - ret = -2; - } - break; - } - } - qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " - HWADDR_FMT_plx " %d %d\n", __func__, - ret < 0 ? "refused" : "granted", address, - ret < 0 ? 0 : *raddr, *prot, ret); - - return ret; -} - -static bool mmubooke_check_pid(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddr, target_ulong addr, int i) -{ - if (ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID], i)) { - if (!env->nb_pids) { - /* Extend the physical address to 36 bits */ - *raddr |= (uint64_t)(tlb->RPN & 0xF) << 32; - } - return true; - } else if (!env->nb_pids) { - return false; - } - if (env->spr[SPR_BOOKE_PID1] && - ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID1], i)) { - return true; - } - if (env->spr[SPR_BOOKE_PID2] && - ppcemb_tlb_check(env, tlb, raddr, addr, env->spr[SPR_BOOKE_PID2], i)) { - return true; - } - return false; -} - -static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddr, int *prot, target_ulong address, - MMUAccessType access_type, int i) -{ - if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { - qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); - return -1; - } - - /* Check the address space */ - if ((access_type == MMU_INST_FETCH ? - FIELD_EX64(env->msr, MSR, IR) : - FIELD_EX64(env->msr, MSR, DR)) != (tlb->attr & 1)) { - qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); - return -1; - } - - if (FIELD_EX64(env->msr, MSR, PR)) { - *prot = tlb->prot & 0xF; - } else { - *prot = (tlb->prot >> 4) & 0xF; - } - if (check_prot_access_type(*prot, access_type)) { - qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); - return 0; - } - - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); - return access_type == MMU_INST_FETCH ? -3 : -2; -} - -static int mmubooke_get_physical_address(CPUPPCState *env, hwaddr *raddr, - int *prot, target_ulong address, - MMUAccessType access_type) -{ - ppcemb_tlb_t *tlb; - int i, ret = -1; - - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - ret = mmubooke_check_tlb(env, tlb, raddr, prot, address, - access_type, i); - if (ret != -1) { - break; - } - } - qemu_log_mask(CPU_LOG_MMU, - "%s: access %s " TARGET_FMT_lx " => " HWADDR_FMT_plx - " %d %d\n", __func__, ret < 0 ? "refused" : "granted", - address, ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); - return ret; -} - -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) -{ - int tlbm_size; - - tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; - - return 1024ULL << tlbm_size; -} - -/* TLB check function for MAS based SoftTLBs */ -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, - target_ulong address, uint32_t pid) -{ - hwaddr mask; - uint32_t tlb_pid; - - if (!FIELD_EX64(env->msr, MSR, CM)) { - /* In 32bit mode we can only address 32bit EAs */ - address = (uint32_t)address; - } - - /* Check valid flag */ - if (!(tlb->mas1 & MAS1_VALID)) { - return -1; - } - - mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); - qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx - " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" - HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", - __func__, address, pid, tlb->mas1, tlb->mas2, mask, - tlb->mas7_3, tlb->mas8); - - /* Check PID */ - tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; - if (tlb_pid != 0 && tlb_pid != pid) { - return -1; - } - - /* Check effective address */ - if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { - return -1; - } - - if (raddrp) { - *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); - } - - return 0; -} - -static bool is_epid_mmu(int mmu_idx) -{ - return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; -} - -static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type) -{ - uint32_t esr = 0; - if (access_type == MMU_DATA_STORE) { - esr |= ESR_ST; - } - if (is_epid_mmu(mmu_idx)) { - esr |= ESR_EPID; - } - return esr; -} - -/* - * Get EPID register given the mmu_idx. If this is regular load, - * construct the EPID access bits from current processor state - * - * Get the effective AS and PR bits and the PID. The PID is returned - * only if EPID load is requested, otherwise the caller must detect - * the correct EPID. Return true if valid EPID is returned. - */ -static bool mmubooke206_get_as(CPUPPCState *env, - int mmu_idx, uint32_t *epid_out, - bool *as_out, bool *pr_out) -{ - if (is_epid_mmu(mmu_idx)) { - uint32_t epidr; - if (mmu_idx == PPC_TLB_EPID_STORE) { - epidr = env->spr[SPR_BOOKE_EPSC]; - } else { - epidr = env->spr[SPR_BOOKE_EPLC]; - } - *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT; - *as_out = !!(epidr & EPID_EAS); - *pr_out = !!(epidr & EPID_EPR); - return true; - } else { - *as_out = FIELD_EX64(env->msr, MSR, DS); - *pr_out = FIELD_EX64(env->msr, MSR, PR); - return false; - } -} - -/* Check if the tlb found by hashing really matches */ -static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddr, int *prot, - target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - uint32_t epid; - bool as, pr; - bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - - if (!use_epid) { - if (ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID1] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID2] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2]) >= 0) { - goto found_tlb; - } - } else { - if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) { - goto found_tlb; - } - } - - qemu_log_mask(CPU_LOG_MMU, "%s: No TLB entry found for effective address " - "0x" TARGET_FMT_lx "\n", __func__, address); - return -1; - -found_tlb: - - /* Check the address space and permissions */ - if (access_type == MMU_INST_FETCH) { - /* There is no way to fetch code using epid load */ - assert(!use_epid); - as = FIELD_EX64(env->msr, MSR, IR); - } - - if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = 0; - if (pr) { - if (tlb->mas7_3 & MAS3_UR) { - *prot |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_UW) { - *prot |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_UX) { - *prot |= PAGE_EXEC; - } - } else { - if (tlb->mas7_3 & MAS3_SR) { - *prot |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_SW) { - *prot |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_SX) { - *prot |= PAGE_EXEC; - } - } - if (check_prot_access_type(*prot, access_type)) { - qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); - return 0; - } - - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); - return access_type == MMU_INST_FETCH ? -3 : -2; -} - -static int mmubooke206_get_physical_address(CPUPPCState *env, hwaddr *raddr, - int *prot, target_ulong address, - MMUAccessType access_type, - int mmu_idx) -{ - ppcmas_tlb_t *tlb; - int i, j, ret = -1; - - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - int ways = booke206_tlb_ways(env, i); - for (j = 0; j < ways; j++) { - tlb = booke206_get_tlbm(env, i, address, j); - if (!tlb) { - continue; - } - ret = mmubooke206_check_tlb(env, tlb, raddr, prot, address, - access_type, mmu_idx); - if (ret != -1) { - goto found_tlb; - } - } - } - -found_tlb: - - qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " - HWADDR_FMT_plx " %d %d\n", __func__, - ret < 0 ? "refused" : "granted", address, - ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); - return ret; -} - static const char *book3e_tsize_to_str[32] = { "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M", @@ -1117,117 +723,6 @@ void dump_mmu(CPUPPCState *env) } } -static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - uint32_t epid; - bool as, pr; - uint32_t missed_tid = 0; - bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - - if (access_type == MMU_INST_FETCH) { - as = FIELD_EX64(env->msr, MSR, IR); - } - env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; - env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; - env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; - env->spr[SPR_BOOKE_MAS3] = 0; - env->spr[SPR_BOOKE_MAS6] = 0; - env->spr[SPR_BOOKE_MAS7] = 0; - - /* AS */ - if (as) { - env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; - env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; - } - - env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; - env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; - - if (!use_epid) { - switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { - case MAS4_TIDSELD_PID0: - missed_tid = env->spr[SPR_BOOKE_PID]; - break; - case MAS4_TIDSELD_PID1: - missed_tid = env->spr[SPR_BOOKE_PID1]; - break; - case MAS4_TIDSELD_PID2: - missed_tid = env->spr[SPR_BOOKE_PID2]; - break; - } - env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; - } else { - missed_tid = epid; - env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; - } - env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT); - - - /* next victim logic */ - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; - env->last_way++; - env->last_way &= booke206_tlb_ways(env, 0) - 1; - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; -} - -static bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, - int mmu_idx, bool guest_visible) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - hwaddr raddr; - int prot, ret; - - if (env->mmu_model == POWERPC_MMU_BOOKE206) { - ret = mmubooke206_get_physical_address(env, &raddr, &prot, eaddr, - access_type, mmu_idx); - } else { - ret = mmubooke_get_physical_address(env, &raddr, &prot, eaddr, - access_type); - } - if (ret == 0) { - *raddrp = raddr; - *protp = prot; - *psizep = TARGET_PAGE_BITS; - return true; - } else if (!guest_visible) { - return false; - } - - log_cpu_state_mask(CPU_LOG_MMU, cs, 0); - env->error_code = 0; - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - if (env->mmu_model == POWERPC_MMU_BOOKE206) { - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - } - cs->exception_index = (access_type == MMU_INST_FETCH) ? - POWERPC_EXCP_ITLB : POWERPC_EXCP_DTLB; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case -2: - /* Access rights violation */ - cs->exception_index = (access_type == MMU_INST_FETCH) ? - POWERPC_EXCP_ISI : POWERPC_EXCP_DSI; - if (access_type != MMU_INST_FETCH) { - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - } - break; - case -3: - /* No execute protection violation */ - cs->exception_index = POWERPC_EXCP_ISI; - env->spr[SPR_BOOKE_ESR] = 0; - break; - } - - return false; -} static bool ppc_real_mode_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 238407a7f1..b0a0676beb 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -33,6 +33,7 @@ #include "internal.h" #include "mmu-book3s-v3.h" #include "mmu-radix64.h" +#include "mmu-booke.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" From e48fb4c590a23d81ee1d2f09ee9bcf5dd5f98e43 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Mon, 13 May 2024 01:28:09 +0200 Subject: [PATCH 72/72] target/ppc: Remove pp_check() and reuse ppc_hash32_pp_prot() The ppc_hash32_pp_prot() function in mmu-hash32.c is the same as pp_check() in mmu_common.c, merge these to remove duplicated code. Define the common function as static lnline otherwise exporting the function from mmu-hash32.c would stop the compiler inlining it which results in slightly lower performance. Reviewed-by: Nicholas Piggin Signed-off-by: BALATON Zoltan [np: move ppc_hash32_pp_prot inline without changing it] Signed-off-by: Nicholas Piggin --- target/ppc/mmu-hash32.c | 45 ----------------------------------------- target/ppc/mmu-hash32.h | 45 +++++++++++++++++++++++++++++++++++++++++ target/ppc/mmu_common.c | 44 ++-------------------------------------- 3 files changed, 47 insertions(+), 87 deletions(-) diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 1e8f1df0f0..d5f2057eb1 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -37,51 +37,6 @@ # define LOG_BATS(...) do { } while (0) #endif -static int ppc_hash32_pp_prot(int key, int pp, int nx) -{ - int prot; - - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - prot = PAGE_READ | PAGE_WRITE; - break; - - case 0x3: - prot = PAGE_READ; - break; - - default: - abort(); - } - } else { - switch (pp) { - case 0x0: - prot = 0; - break; - - case 0x1: - case 0x3: - prot = PAGE_READ; - break; - - case 0x2: - prot = PAGE_READ | PAGE_WRITE; - break; - - default: - abort(); - } - } - if (nx == 0) { - prot |= PAGE_EXEC; - } - - return prot; -} - static int ppc_hash32_pte_prot(int mmu_idx, target_ulong sr, ppc_hash_pte32_t pte) { diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index 7119a63d97..f0ce6951b4 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -102,6 +102,51 @@ static inline void ppc_hash32_store_hpte1(PowerPCCPU *cpu, stl_phys(CPU(cpu)->as, base + pte_offset + HASH_PTE_SIZE_32 / 2, pte1); } +static inline int ppc_hash32_pp_prot(bool key, int pp, bool nx) +{ + int prot; + + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + case 0x3: + prot = PAGE_READ; + break; + + default: + abort(); + } + } else { + switch (pp) { + case 0x0: + prot = 0; + break; + + case 0x1: + case 0x3: + prot = PAGE_READ; + break; + + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + default: + abort(); + } + } + if (nx == 0) { + prot |= PAGE_EXEC; + } + + return prot; +} + typedef struct { uint32_t pte0, pte1; } ppc_hash_pte32_t; diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 2c75e53250..e2542694f0 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -77,44 +77,6 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) /*****************************************************************************/ /* PowerPC MMU emulation */ -static int pp_check(int key, int pp, int nx) -{ - int access; - - /* Compute access rights */ - access = 0; - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - access |= PAGE_WRITE; - /* fall through */ - case 0x3: - access |= PAGE_READ; - break; - } - } else { - switch (pp) { - case 0x0: - access = 0; - break; - case 0x1: - case 0x3: - access = PAGE_READ; - break; - case 0x2: - access = PAGE_READ | PAGE_WRITE; - break; - } - } - if (nx == 0) { - access |= PAGE_EXEC; - } - - return access; -} - int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code) { @@ -137,7 +99,7 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, MMUAccessType access_type) { target_ulong ptem, mmask; - int access, ret, pteh, ptev, pp; + int ret, pteh, ptev, pp; ret = -1; /* Check validity and table match */ @@ -156,11 +118,9 @@ static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, return -3; } } - /* Compute access rights */ - access = pp_check(ctx->key, pp, ctx->nx); /* Keep the matching PTE information */ ctx->raddr = pte1; - ctx->prot = access; + ctx->prot = ppc_hash32_pp_prot(ctx->key, pp, ctx->nx); if (check_prot_access_type(ctx->prot, access_type)) { /* Access granted */ qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n");