summaryrefslogtreecommitdiffstats
path: root/target/ppc/mmu_helper.c
diff options
context:
space:
mode:
authorLeandro Lupori2022-07-12 21:37:41 +0200
committerDaniel Henrique Barboza2022-07-18 18:59:43 +0200
commite7beaea55bd1efcb554b8e021092a2e79a317b61 (patch)
treeecb27392b085b4dcca6f4d4584273d4714a128f2 /target/ppc/mmu_helper.c
parenttarget/ppc: Move tlbie[l] to decode tree (diff)
downloadqemu-e7beaea55bd1efcb554b8e021092a2e79a317b61.tar.gz
qemu-e7beaea55bd1efcb554b8e021092a2e79a317b61.tar.xz
qemu-e7beaea55bd1efcb554b8e021092a2e79a317b61.zip
target/ppc: Implement ISA 3.00 tlbie[l]
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> Message-Id: <20220712193741.59134-3-leandro.lupori@eldorado.org.br> [danielhb: moved 'set' declaration to TLBIE_RIC_PWC block] Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Diffstat (limited to 'target/ppc/mmu_helper.c')
-rw-r--r--target/ppc/mmu_helper.c154
1 files changed, 154 insertions, 0 deletions
diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c
index 15239dc95b..2a91f3f46a 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);
+ 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) {
+ unsigned 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 */