Message ID | 20220712193741.59134-3-leandro.lupori@eldorado.org.br |
---|---|
State | New |
Headers | show |
Series | target/ppc: Implement ISA 3.00 tlbie[l] | expand |
On 7/12/22 16:37, Leandro Lupori wrote: > This initial version supports the invalidation of one or all > TLB entries. Flush by PID/LPID, or based in process/partition > scope is not supported, because it would make using the > generic QEMU TLB implementation hard. In these cases, all > entries are flushed. > > Signed-off-by: Leandro Lupori <leandro.lupori@eldorado.org.br> > --- Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com> > target/ppc/helper.h | 2 + > target/ppc/mmu-book3s-v3.h | 15 ++ > target/ppc/mmu_helper.c | 154 +++++++++++++++++++ > target/ppc/translate/storage-ctrl-impl.c.inc | 17 ++ > 4 files changed, 188 insertions(+) > > diff --git a/target/ppc/helper.h b/target/ppc/helper.h > index d627cfe6ed..90d16f00e7 100644 > --- a/target/ppc/helper.h > +++ b/target/ppc/helper.h > @@ -672,6 +672,8 @@ DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_NO_RWG, void, env) > DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_NO_RWG, void, env, tl) > DEF_HELPER_FLAGS_2(tlbiva, TCG_CALL_NO_RWG, void, env, tl) > #if defined(TARGET_PPC64) > +DEF_HELPER_FLAGS_4(tlbie_isa300, TCG_CALL_NO_WG, void, \ > + env, tl, tl, i32) > DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_NO_RWG, void, env, tl, tl) > DEF_HELPER_2(load_slb_esid, tl, env, tl) > DEF_HELPER_2(load_slb_vsid, tl, env, tl) > diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h > index d6d5ed8f8e..674377a19e 100644 > --- a/target/ppc/mmu-book3s-v3.h > +++ b/target/ppc/mmu-book3s-v3.h > @@ -50,6 +50,21 @@ struct prtb_entry { > > #ifdef TARGET_PPC64 > > +/* > + * tlbie[l] helper flags > + * > + * RIC, PRS, R and local are passed as flags in the last argument. > + */ > +#define TLBIE_F_RIC_SHIFT 0 > +#define TLBIE_F_PRS_SHIFT 2 > +#define TLBIE_F_R_SHIFT 3 > +#define TLBIE_F_LOCAL_SHIFT 4 > + > +#define TLBIE_F_RIC_MASK (3 << TLBIE_F_RIC_SHIFT) > +#define TLBIE_F_PRS (1 << TLBIE_F_PRS_SHIFT) > +#define TLBIE_F_R (1 << TLBIE_F_R_SHIFT) > +#define TLBIE_F_LOCAL (1 << TLBIE_F_LOCAL_SHIFT) > + > static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu) > { > return !!(cpu->env.spr[SPR_LPCR] & LPCR_UPRT); > diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c > index 15239dc95b..b881aee23f 100644 > --- a/target/ppc/mmu_helper.c > +++ b/target/ppc/mmu_helper.c > @@ -429,6 +429,160 @@ void helper_tlbie(CPUPPCState *env, target_ulong addr) > ppc_tlb_invalidate_one(env, addr); > } > > +#if defined(TARGET_PPC64) > + > +/* Invalidation Selector */ > +#define TLBIE_IS_VA 0 > +#define TLBIE_IS_PID 1 > +#define TLBIE_IS_LPID 2 > +#define TLBIE_IS_ALL 3 > + > +/* Radix Invalidation Control */ > +#define TLBIE_RIC_TLB 0 > +#define TLBIE_RIC_PWC 1 > +#define TLBIE_RIC_ALL 2 > +#define TLBIE_RIC_GRP 3 > + > +/* Radix Actual Page sizes */ > +#define TLBIE_R_AP_4K 0 > +#define TLBIE_R_AP_64K 5 > +#define TLBIE_R_AP_2M 1 > +#define TLBIE_R_AP_1G 2 > + > +/* RB field masks */ > +#define TLBIE_RB_EPN_MASK PPC_BITMASK(0, 51) > +#define TLBIE_RB_IS_MASK PPC_BITMASK(52, 53) > +#define TLBIE_RB_AP_MASK PPC_BITMASK(56, 58) > + > +void helper_tlbie_isa300(CPUPPCState *env, target_ulong rb, target_ulong rs, > + uint32_t flags) > +{ > + unsigned ric = (flags & TLBIE_F_RIC_MASK) >> TLBIE_F_RIC_SHIFT; > + /* > + * With the exception of the checks for invalid instruction forms, > + * PRS is currently ignored, because we don't know if a given TLB entry > + * is process or partition scoped. > + */ > + bool prs = flags & TLBIE_F_PRS; > + bool r = flags & TLBIE_F_R; > + bool local = flags & TLBIE_F_LOCAL; > + bool effR; > + unsigned is = extract64(rb, PPC_BIT_NR(53), 2), set; > + unsigned ap; /* actual page size */ > + target_ulong addr, pgoffs_mask; > + > + qemu_log_mask(CPU_LOG_MMU, > + "%s: local=%d addr=" TARGET_FMT_lx " ric=%u prs=%d r=%d is=%u\n", > + __func__, local, rb & TARGET_PAGE_MASK, ric, prs, r, is); > + > + effR = FIELD_EX64(env->msr, MSR, HV) ? r : env->spr[SPR_LPCR] & LPCR_HR; > + > + /* Partial TLB invalidation is supported for Radix only for now. */ > + if (!effR) { > + goto inval_all; > + } > + > + /* Check for invalid instruction forms (effR=1). */ > + if (unlikely(ric == TLBIE_RIC_GRP || > + ((ric == TLBIE_RIC_PWC || ric == TLBIE_RIC_ALL) && > + is == TLBIE_IS_VA) || > + (!prs && is == TLBIE_IS_PID))) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "%s: invalid instruction form: ric=%u prs=%d r=%d is=%u\n", > + __func__, ric, prs, r, is); > + goto invalid; > + } > + > + /* We don't cache Page Walks. */ > + if (ric == TLBIE_RIC_PWC) { > + if (local) { > + set = extract64(rb, PPC_BIT_NR(51), 12); > + if (set != 0) { > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid set: %d\n", > + __func__, set); > + goto invalid; > + } > + } > + return; > + } > + > + /* > + * Invalidation by LPID or PID is not supported, so fallback > + * to full TLB flush in these cases. > + */ > + if (is != TLBIE_IS_VA) { > + goto inval_all; > + } > + > + /* > + * The results of an attempt to invalidate a translation outside of > + * quadrant 0 for Radix Tree translation (effR=1, RIC=0, PRS=1, IS=0, > + * and EA 0:1 != 0b00) are boundedly undefined. > + */ > + if (unlikely(ric == TLBIE_RIC_TLB && prs && is == TLBIE_IS_VA && > + (rb & R_EADDR_QUADRANT) != R_EADDR_QUADRANT0)) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "%s: attempt to invalidate a translation outside of quadrant 0\n", > + __func__); > + goto inval_all; > + } > + > + assert(is == TLBIE_IS_VA); > + assert(ric == TLBIE_RIC_TLB || ric == TLBIE_RIC_ALL); > + > + ap = extract64(rb, PPC_BIT_NR(58), 3); > + switch (ap) { > + case TLBIE_R_AP_4K: > + pgoffs_mask = 0xfffull; > + break; > + > + case TLBIE_R_AP_64K: > + pgoffs_mask = 0xffffull; > + break; > + > + case TLBIE_R_AP_2M: > + pgoffs_mask = 0x1fffffull; > + break; > + > + case TLBIE_R_AP_1G: > + pgoffs_mask = 0x3fffffffull; > + break; > + > + default: > + /* > + * If the value specified in RS 0:31, RS 32:63, RB 54:55, RB 56:58, > + * RB 44:51, or RB 56:63, when it is needed to perform the specified > + * operation, is not supported by the implementation, the instruction > + * is treated as if the instruction form were invalid. > + */ > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid AP: %d\n", __func__, ap); > + goto invalid; > + } > + > + addr = rb & TLBIE_RB_EPN_MASK & ~pgoffs_mask; > + > + if (local) { > + tlb_flush_page(env_cpu(env), addr); > + } else { > + tlb_flush_page_all_cpus(env_cpu(env), addr); > + } > + return; > + > +inval_all: > + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; > + if (!local) { > + env->tlb_need_flush |= TLB_NEED_GLOBAL_FLUSH; > + } > + return; > + > +invalid: > + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, > + POWERPC_EXCP_INVAL | > + POWERPC_EXCP_INVAL_INVAL, GETPC()); > +} > + > +#endif > + > void helper_tlbiva(CPUPPCState *env, target_ulong addr) > { > /* tlbiva instruction only exists on BookE */ > diff --git a/target/ppc/translate/storage-ctrl-impl.c.inc b/target/ppc/translate/storage-ctrl-impl.c.inc > index 7793297dd4..467c390888 100644 > --- a/target/ppc/translate/storage-ctrl-impl.c.inc > +++ b/target/ppc/translate/storage-ctrl-impl.c.inc > @@ -21,6 +21,8 @@ > * Store Control Instructions > */ > > +#include "mmu-book3s-v3.h" > + > static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) > { > #if defined(CONFIG_USER_ONLY) > @@ -65,6 +67,21 @@ static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) > tcg_gen_ext32u_tl(t0, cpu_gpr[rb]); > gen_helper_tlbie(cpu_env, t0); > tcg_temp_free(t0); > + > +#if defined(TARGET_PPC64) > + /* > + * ISA 3.1B says that MSR SF must be 1 when this instruction is executed; > + * otherwise the results are undefined. > + */ > + } else if (a->r) { > + gen_helper_tlbie_isa300(cpu_env, cpu_gpr[rb], cpu_gpr[a->rs], > + tcg_constant_i32(a->ric << TLBIE_F_RIC_SHIFT | > + a->prs << TLBIE_F_PRS_SHIFT | > + a->r << TLBIE_F_R_SHIFT | > + local << TLBIE_F_LOCAL_SHIFT)); > + return true; > +#endif > + > } else { > gen_helper_tlbie(cpu_env, cpu_gpr[rb]); > }
diff --git a/target/ppc/helper.h b/target/ppc/helper.h index d627cfe6ed..90d16f00e7 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -672,6 +672,8 @@ DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(tlbiva, TCG_CALL_NO_RWG, void, env, tl) #if defined(TARGET_PPC64) +DEF_HELPER_FLAGS_4(tlbie_isa300, TCG_CALL_NO_WG, void, \ + env, tl, tl, i32) DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_2(load_slb_esid, tl, env, tl) DEF_HELPER_2(load_slb_vsid, tl, env, tl) diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index d6d5ed8f8e..674377a19e 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -50,6 +50,21 @@ struct prtb_entry { #ifdef TARGET_PPC64 +/* + * tlbie[l] helper flags + * + * RIC, PRS, R and local are passed as flags in the last argument. + */ +#define TLBIE_F_RIC_SHIFT 0 +#define TLBIE_F_PRS_SHIFT 2 +#define TLBIE_F_R_SHIFT 3 +#define TLBIE_F_LOCAL_SHIFT 4 + +#define TLBIE_F_RIC_MASK (3 << TLBIE_F_RIC_SHIFT) +#define TLBIE_F_PRS (1 << TLBIE_F_PRS_SHIFT) +#define TLBIE_F_R (1 << TLBIE_F_R_SHIFT) +#define TLBIE_F_LOCAL (1 << TLBIE_F_LOCAL_SHIFT) + static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu) { return !!(cpu->env.spr[SPR_LPCR] & LPCR_UPRT); diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 15239dc95b..b881aee23f 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -429,6 +429,160 @@ void helper_tlbie(CPUPPCState *env, target_ulong addr) ppc_tlb_invalidate_one(env, addr); } +#if defined(TARGET_PPC64) + +/* Invalidation Selector */ +#define TLBIE_IS_VA 0 +#define TLBIE_IS_PID 1 +#define TLBIE_IS_LPID 2 +#define TLBIE_IS_ALL 3 + +/* Radix Invalidation Control */ +#define TLBIE_RIC_TLB 0 +#define TLBIE_RIC_PWC 1 +#define TLBIE_RIC_ALL 2 +#define TLBIE_RIC_GRP 3 + +/* Radix Actual Page sizes */ +#define TLBIE_R_AP_4K 0 +#define TLBIE_R_AP_64K 5 +#define TLBIE_R_AP_2M 1 +#define TLBIE_R_AP_1G 2 + +/* RB field masks */ +#define TLBIE_RB_EPN_MASK PPC_BITMASK(0, 51) +#define TLBIE_RB_IS_MASK PPC_BITMASK(52, 53) +#define TLBIE_RB_AP_MASK PPC_BITMASK(56, 58) + +void helper_tlbie_isa300(CPUPPCState *env, target_ulong rb, target_ulong rs, + uint32_t flags) +{ + unsigned ric = (flags & TLBIE_F_RIC_MASK) >> TLBIE_F_RIC_SHIFT; + /* + * With the exception of the checks for invalid instruction forms, + * PRS is currently ignored, because we don't know if a given TLB entry + * is process or partition scoped. + */ + bool prs = flags & TLBIE_F_PRS; + bool r = flags & TLBIE_F_R; + bool local = flags & TLBIE_F_LOCAL; + bool effR; + unsigned is = extract64(rb, PPC_BIT_NR(53), 2), set; + unsigned ap; /* actual page size */ + target_ulong addr, pgoffs_mask; + + qemu_log_mask(CPU_LOG_MMU, + "%s: local=%d addr=" TARGET_FMT_lx " ric=%u prs=%d r=%d is=%u\n", + __func__, local, rb & TARGET_PAGE_MASK, ric, prs, r, is); + + effR = FIELD_EX64(env->msr, MSR, HV) ? r : env->spr[SPR_LPCR] & LPCR_HR; + + /* Partial TLB invalidation is supported for Radix only for now. */ + if (!effR) { + goto inval_all; + } + + /* Check for invalid instruction forms (effR=1). */ + if (unlikely(ric == TLBIE_RIC_GRP || + ((ric == TLBIE_RIC_PWC || ric == TLBIE_RIC_ALL) && + is == TLBIE_IS_VA) || + (!prs && is == TLBIE_IS_PID))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid instruction form: ric=%u prs=%d r=%d is=%u\n", + __func__, ric, prs, r, is); + goto invalid; + } + + /* We don't cache Page Walks. */ + if (ric == TLBIE_RIC_PWC) { + if (local) { + set = extract64(rb, PPC_BIT_NR(51), 12); + if (set != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid set: %d\n", + __func__, set); + goto invalid; + } + } + return; + } + + /* + * Invalidation by LPID or PID is not supported, so fallback + * to full TLB flush in these cases. + */ + if (is != TLBIE_IS_VA) { + goto inval_all; + } + + /* + * The results of an attempt to invalidate a translation outside of + * quadrant 0 for Radix Tree translation (effR=1, RIC=0, PRS=1, IS=0, + * and EA 0:1 != 0b00) are boundedly undefined. + */ + if (unlikely(ric == TLBIE_RIC_TLB && prs && is == TLBIE_IS_VA && + (rb & R_EADDR_QUADRANT) != R_EADDR_QUADRANT0)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: attempt to invalidate a translation outside of quadrant 0\n", + __func__); + goto inval_all; + } + + assert(is == TLBIE_IS_VA); + assert(ric == TLBIE_RIC_TLB || ric == TLBIE_RIC_ALL); + + ap = extract64(rb, PPC_BIT_NR(58), 3); + switch (ap) { + case TLBIE_R_AP_4K: + pgoffs_mask = 0xfffull; + break; + + case TLBIE_R_AP_64K: + pgoffs_mask = 0xffffull; + break; + + case TLBIE_R_AP_2M: + pgoffs_mask = 0x1fffffull; + break; + + case TLBIE_R_AP_1G: + pgoffs_mask = 0x3fffffffull; + break; + + default: + /* + * If the value specified in RS 0:31, RS 32:63, RB 54:55, RB 56:58, + * RB 44:51, or RB 56:63, when it is needed to perform the specified + * operation, is not supported by the implementation, the instruction + * is treated as if the instruction form were invalid. + */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid AP: %d\n", __func__, ap); + goto invalid; + } + + addr = rb & TLBIE_RB_EPN_MASK & ~pgoffs_mask; + + if (local) { + tlb_flush_page(env_cpu(env), addr); + } else { + tlb_flush_page_all_cpus(env_cpu(env), addr); + } + return; + +inval_all: + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + if (!local) { + env->tlb_need_flush |= TLB_NEED_GLOBAL_FLUSH; + } + return; + +invalid: + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL | + POWERPC_EXCP_INVAL_INVAL, GETPC()); +} + +#endif + void helper_tlbiva(CPUPPCState *env, target_ulong addr) { /* tlbiva instruction only exists on BookE */ diff --git a/target/ppc/translate/storage-ctrl-impl.c.inc b/target/ppc/translate/storage-ctrl-impl.c.inc index 7793297dd4..467c390888 100644 --- a/target/ppc/translate/storage-ctrl-impl.c.inc +++ b/target/ppc/translate/storage-ctrl-impl.c.inc @@ -21,6 +21,8 @@ * Store Control Instructions */ +#include "mmu-book3s-v3.h" + static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) { #if defined(CONFIG_USER_ONLY) @@ -65,6 +67,21 @@ static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) tcg_gen_ext32u_tl(t0, cpu_gpr[rb]); gen_helper_tlbie(cpu_env, t0); tcg_temp_free(t0); + +#if defined(TARGET_PPC64) + /* + * ISA 3.1B says that MSR SF must be 1 when this instruction is executed; + * otherwise the results are undefined. + */ + } else if (a->r) { + gen_helper_tlbie_isa300(cpu_env, cpu_gpr[rb], cpu_gpr[a->rs], + tcg_constant_i32(a->ric << TLBIE_F_RIC_SHIFT | + a->prs << TLBIE_F_PRS_SHIFT | + a->r << TLBIE_F_R_SHIFT | + local << TLBIE_F_LOCAL_SHIFT)); + return true; +#endif + } else { gen_helper_tlbie(cpu_env, cpu_gpr[rb]); }
This initial version supports the invalidation of one or all TLB entries. Flush by PID/LPID, or based in process/partition scope is not supported, because it would make using the generic QEMU TLB implementation hard. In these cases, all entries are flushed. Signed-off-by: Leandro Lupori <leandro.lupori@eldorado.org.br> --- target/ppc/helper.h | 2 + target/ppc/mmu-book3s-v3.h | 15 ++ target/ppc/mmu_helper.c | 154 +++++++++++++++++++ target/ppc/translate/storage-ctrl-impl.c.inc | 17 ++ 4 files changed, 188 insertions(+)