xref: /openbmc/qemu/target/loongarch/tcg/tlb_helper.c (revision a18ffbcf)
15c23704eSSong Gao /* SPDX-License-Identifier: GPL-2.0-or-later */
25c23704eSSong Gao /*
35c23704eSSong Gao  * QEMU LoongArch TLB helpers
45c23704eSSong Gao  *
55c23704eSSong Gao  * Copyright (c) 2021 Loongson Technology Corporation Limited
65c23704eSSong Gao  *
75c23704eSSong Gao  */
85c23704eSSong Gao 
95c23704eSSong Gao #include "qemu/osdep.h"
105c23704eSSong Gao #include "qemu/guest-random.h"
115c23704eSSong Gao 
125c23704eSSong Gao #include "cpu.h"
135c23704eSSong Gao #include "internals.h"
145c23704eSSong Gao #include "exec/helper-proto.h"
155c23704eSSong Gao #include "exec/exec-all.h"
1674781c08SPhilippe Mathieu-Daudé #include "exec/page-protection.h"
175c23704eSSong Gao #include "exec/cpu_ldst.h"
185c23704eSSong Gao #include "exec/log.h"
195c23704eSSong Gao #include "cpu-csr.h"
205c23704eSSong Gao 
get_dir_base_width(CPULoongArchState * env,uint64_t * dir_base,uint64_t * dir_width,target_ulong level)219c70db9aSXianglai Li static void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
229c70db9aSXianglai Li                                uint64_t *dir_width, target_ulong level)
239c70db9aSXianglai Li {
249c70db9aSXianglai Li     switch (level) {
259c70db9aSXianglai Li     case 1:
269c70db9aSXianglai Li         *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
279c70db9aSXianglai Li         *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
289c70db9aSXianglai Li         break;
299c70db9aSXianglai Li     case 2:
309c70db9aSXianglai Li         *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE);
319c70db9aSXianglai Li         *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH);
329c70db9aSXianglai Li         break;
339c70db9aSXianglai Li     case 3:
349c70db9aSXianglai Li         *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
359c70db9aSXianglai Li         *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
369c70db9aSXianglai Li         break;
379c70db9aSXianglai Li     case 4:
389c70db9aSXianglai Li         *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE);
399c70db9aSXianglai Li         *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH);
409c70db9aSXianglai Li         break;
419c70db9aSXianglai Li     default:
429c70db9aSXianglai Li         /* level may be zero for ldpte */
439c70db9aSXianglai Li         *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
449c70db9aSXianglai Li         *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
459c70db9aSXianglai Li         break;
469c70db9aSXianglai Li     }
479c70db9aSXianglai Li }
489c70db9aSXianglai Li 
raise_mmu_exception(CPULoongArchState * env,target_ulong address,MMUAccessType access_type,int tlb_error)495c23704eSSong Gao static void raise_mmu_exception(CPULoongArchState *env, target_ulong address,
505c23704eSSong Gao                                 MMUAccessType access_type, int tlb_error)
515c23704eSSong Gao {
525c23704eSSong Gao     CPUState *cs = env_cpu(env);
535c23704eSSong Gao 
545c23704eSSong Gao     switch (tlb_error) {
555c23704eSSong Gao     default:
565c23704eSSong Gao     case TLBRET_BADADDR:
575c23704eSSong Gao         cs->exception_index = access_type == MMU_INST_FETCH
585c23704eSSong Gao                               ? EXCCODE_ADEF : EXCCODE_ADEM;
595c23704eSSong Gao         break;
605c23704eSSong Gao     case TLBRET_NOMATCH:
615c23704eSSong Gao         /* No TLB match for a mapped address */
625c23704eSSong Gao         if (access_type == MMU_DATA_LOAD) {
635c23704eSSong Gao             cs->exception_index = EXCCODE_PIL;
645c23704eSSong Gao         } else if (access_type == MMU_DATA_STORE) {
655c23704eSSong Gao             cs->exception_index = EXCCODE_PIS;
665c23704eSSong Gao         } else if (access_type == MMU_INST_FETCH) {
675c23704eSSong Gao             cs->exception_index = EXCCODE_PIF;
685c23704eSSong Gao         }
695c23704eSSong Gao         env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1);
705c23704eSSong Gao         break;
715c23704eSSong Gao     case TLBRET_INVALID:
725c23704eSSong Gao         /* TLB match with no valid bit */
735c23704eSSong Gao         if (access_type == MMU_DATA_LOAD) {
745c23704eSSong Gao             cs->exception_index = EXCCODE_PIL;
755c23704eSSong Gao         } else if (access_type == MMU_DATA_STORE) {
765c23704eSSong Gao             cs->exception_index = EXCCODE_PIS;
775c23704eSSong Gao         } else if (access_type == MMU_INST_FETCH) {
785c23704eSSong Gao             cs->exception_index = EXCCODE_PIF;
795c23704eSSong Gao         }
805c23704eSSong Gao         break;
815c23704eSSong Gao     case TLBRET_DIRTY:
825c23704eSSong Gao         /* TLB match but 'D' bit is cleared */
835c23704eSSong Gao         cs->exception_index = EXCCODE_PME;
845c23704eSSong Gao         break;
855c23704eSSong Gao     case TLBRET_XI:
865c23704eSSong Gao         /* Execute-Inhibit Exception */
875c23704eSSong Gao         cs->exception_index = EXCCODE_PNX;
885c23704eSSong Gao         break;
895c23704eSSong Gao     case TLBRET_RI:
905c23704eSSong Gao         /* Read-Inhibit Exception */
915c23704eSSong Gao         cs->exception_index = EXCCODE_PNR;
925c23704eSSong Gao         break;
935c23704eSSong Gao     case TLBRET_PE:
945c23704eSSong Gao         /* Privileged Exception */
955c23704eSSong Gao         cs->exception_index = EXCCODE_PPI;
965c23704eSSong Gao         break;
975c23704eSSong Gao     }
985c23704eSSong Gao 
995c23704eSSong Gao     if (tlb_error == TLBRET_NOMATCH) {
1005c23704eSSong Gao         env->CSR_TLBRBADV = address;
1015c23704eSSong Gao         if (is_la64(env)) {
1025c23704eSSong Gao             env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_64,
1035c23704eSSong Gao                                         VPPN, extract64(address, 13, 35));
1045c23704eSSong Gao         } else {
1055c23704eSSong Gao             env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_32,
1065c23704eSSong Gao                                         VPPN, extract64(address, 13, 19));
1075c23704eSSong Gao         }
1085c23704eSSong Gao     } else {
1095c23704eSSong Gao         if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) {
1105c23704eSSong Gao             env->CSR_BADV = address;
1115c23704eSSong Gao         }
1125c23704eSSong Gao         env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1);
1135c23704eSSong Gao    }
1145c23704eSSong Gao }
1155c23704eSSong Gao 
invalidate_tlb_entry(CPULoongArchState * env,int index)1165c23704eSSong Gao static void invalidate_tlb_entry(CPULoongArchState *env, int index)
1175c23704eSSong Gao {
1185c23704eSSong Gao     target_ulong addr, mask, pagesize;
1195c23704eSSong Gao     uint8_t tlb_ps;
1205c23704eSSong Gao     LoongArchTLB *tlb = &env->tlb[index];
1215c23704eSSong Gao 
1223b916140SRichard Henderson     int mmu_idx = cpu_mmu_index(env_cpu(env), false);
1235c23704eSSong Gao     uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V);
1245c23704eSSong Gao     uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V);
1255c23704eSSong Gao     uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
1265c23704eSSong Gao 
1275c23704eSSong Gao     if (index >= LOONGARCH_STLB) {
1285c23704eSSong Gao         tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
1295c23704eSSong Gao     } else {
1305c23704eSSong Gao         tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
1315c23704eSSong Gao     }
1325c23704eSSong Gao     pagesize = MAKE_64BIT_MASK(tlb_ps, 1);
1335c23704eSSong Gao     mask = MAKE_64BIT_MASK(0, tlb_ps + 1);
1345c23704eSSong Gao 
1355c23704eSSong Gao     if (tlb_v0) {
1365c23704eSSong Gao         addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask;    /* even */
1375c23704eSSong Gao         tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
1385c23704eSSong Gao                                   mmu_idx, TARGET_LONG_BITS);
1395c23704eSSong Gao     }
1405c23704eSSong Gao 
1415c23704eSSong Gao     if (tlb_v1) {
1425c23704eSSong Gao         addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize;    /* odd */
1435c23704eSSong Gao         tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize,
1445c23704eSSong Gao                                   mmu_idx, TARGET_LONG_BITS);
1455c23704eSSong Gao     }
1465c23704eSSong Gao }
1475c23704eSSong Gao 
invalidate_tlb(CPULoongArchState * env,int index)1485c23704eSSong Gao static void invalidate_tlb(CPULoongArchState *env, int index)
1495c23704eSSong Gao {
1505c23704eSSong Gao     LoongArchTLB *tlb;
1515c23704eSSong Gao     uint16_t csr_asid, tlb_asid, tlb_g;
1525c23704eSSong Gao 
1535c23704eSSong Gao     csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
1545c23704eSSong Gao     tlb = &env->tlb[index];
1555c23704eSSong Gao     tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
1565c23704eSSong Gao     tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
1575c23704eSSong Gao     if (tlb_g == 0 && tlb_asid != csr_asid) {
1585c23704eSSong Gao         return;
1595c23704eSSong Gao     }
1605c23704eSSong Gao     invalidate_tlb_entry(env, index);
1615c23704eSSong Gao }
1625c23704eSSong Gao 
fill_tlb_entry(CPULoongArchState * env,int index)1635c23704eSSong Gao static void fill_tlb_entry(CPULoongArchState *env, int index)
1645c23704eSSong Gao {
1655c23704eSSong Gao     LoongArchTLB *tlb = &env->tlb[index];
1665c23704eSSong Gao     uint64_t lo0, lo1, csr_vppn;
1675c23704eSSong Gao     uint16_t csr_asid;
1685c23704eSSong Gao     uint8_t csr_ps;
1695c23704eSSong Gao 
1705c23704eSSong Gao     if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
1715c23704eSSong Gao         csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
1725c23704eSSong Gao         if (is_la64(env)) {
1735c23704eSSong Gao             csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_64, VPPN);
1745c23704eSSong Gao         } else {
1755c23704eSSong Gao             csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_32, VPPN);
1765c23704eSSong Gao         }
1775c23704eSSong Gao         lo0 = env->CSR_TLBRELO0;
1785c23704eSSong Gao         lo1 = env->CSR_TLBRELO1;
1795c23704eSSong Gao     } else {
1805c23704eSSong Gao         csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
1815c23704eSSong Gao         if (is_la64(env)) {
1825c23704eSSong Gao             csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_64, VPPN);
1835c23704eSSong Gao         } else {
1845c23704eSSong Gao             csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_32, VPPN);
1855c23704eSSong Gao         }
1865c23704eSSong Gao         lo0 = env->CSR_TLBELO0;
1875c23704eSSong Gao         lo1 = env->CSR_TLBELO1;
1885c23704eSSong Gao     }
1895c23704eSSong Gao 
1905c23704eSSong Gao     if (csr_ps == 0) {
1915c23704eSSong Gao         qemu_log_mask(CPU_LOG_MMU, "page size is 0\n");
1925c23704eSSong Gao     }
1935c23704eSSong Gao 
1945c23704eSSong Gao     /* Only MTLB has the ps fields */
1955c23704eSSong Gao     if (index >= LOONGARCH_STLB) {
1965c23704eSSong Gao         tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps);
1975c23704eSSong Gao     }
1985c23704eSSong Gao 
1995c23704eSSong Gao     tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn);
2005c23704eSSong Gao     tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1);
2015c23704eSSong Gao     csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
2025c23704eSSong Gao     tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid);
2035c23704eSSong Gao 
2045c23704eSSong Gao     tlb->tlb_entry0 = lo0;
2055c23704eSSong Gao     tlb->tlb_entry1 = lo1;
2065c23704eSSong Gao }
2075c23704eSSong Gao 
2085c23704eSSong Gao /* Return an random value between low and high */
get_random_tlb(uint32_t low,uint32_t high)2095c23704eSSong Gao static uint32_t get_random_tlb(uint32_t low, uint32_t high)
2105c23704eSSong Gao {
2115c23704eSSong Gao     uint32_t val;
2125c23704eSSong Gao 
2135c23704eSSong Gao     qemu_guest_getrandom_nofail(&val, sizeof(val));
2145c23704eSSong Gao     return val % (high - low + 1) + low;
2155c23704eSSong Gao }
2165c23704eSSong Gao 
helper_tlbsrch(CPULoongArchState * env)2175c23704eSSong Gao void helper_tlbsrch(CPULoongArchState *env)
2185c23704eSSong Gao {
2195c23704eSSong Gao     int index, match;
2205c23704eSSong Gao 
2215c23704eSSong Gao     if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
2225c23704eSSong Gao         match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index);
2235c23704eSSong Gao     } else {
2245c23704eSSong Gao         match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index);
2255c23704eSSong Gao     }
2265c23704eSSong Gao 
2275c23704eSSong Gao     if (match) {
2285c23704eSSong Gao         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX, index);
2295c23704eSSong Gao         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
2305c23704eSSong Gao         return;
2315c23704eSSong Gao     }
2325c23704eSSong Gao 
2335c23704eSSong Gao     env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
2345c23704eSSong Gao }
2355c23704eSSong Gao 
helper_tlbrd(CPULoongArchState * env)2365c23704eSSong Gao void helper_tlbrd(CPULoongArchState *env)
2375c23704eSSong Gao {
2385c23704eSSong Gao     LoongArchTLB *tlb;
2395c23704eSSong Gao     int index;
2405c23704eSSong Gao     uint8_t tlb_ps, tlb_e;
2415c23704eSSong Gao 
2425c23704eSSong Gao     index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
2435c23704eSSong Gao     tlb = &env->tlb[index];
2445c23704eSSong Gao 
2455c23704eSSong Gao     if (index >= LOONGARCH_STLB) {
2465c23704eSSong Gao         tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
2475c23704eSSong Gao     } else {
2485c23704eSSong Gao         tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
2495c23704eSSong Gao     }
2505c23704eSSong Gao     tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E);
2515c23704eSSong Gao 
2525c23704eSSong Gao     if (!tlb_e) {
2535c23704eSSong Gao         /* Invalid TLB entry */
2545c23704eSSong Gao         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1);
2555c23704eSSong Gao         env->CSR_ASID  = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0);
2565c23704eSSong Gao         env->CSR_TLBEHI = 0;
2575c23704eSSong Gao         env->CSR_TLBELO0 = 0;
2585c23704eSSong Gao         env->CSR_TLBELO1 = 0;
2595c23704eSSong Gao         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0);
2605c23704eSSong Gao     } else {
2615c23704eSSong Gao         /* Valid TLB entry */
2625c23704eSSong Gao         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0);
2635c23704eSSong Gao         env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX,
2645c23704eSSong Gao                                      PS, (tlb_ps & 0x3f));
2655c23704eSSong Gao         env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) <<
2665c23704eSSong Gao                                      R_TLB_MISC_VPPN_SHIFT;
2675c23704eSSong Gao         env->CSR_TLBELO0 = tlb->tlb_entry0;
2685c23704eSSong Gao         env->CSR_TLBELO1 = tlb->tlb_entry1;
2695c23704eSSong Gao     }
2705c23704eSSong Gao }
2715c23704eSSong Gao 
helper_tlbwr(CPULoongArchState * env)2725c23704eSSong Gao void helper_tlbwr(CPULoongArchState *env)
2735c23704eSSong Gao {
2745c23704eSSong Gao     int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
2755c23704eSSong Gao 
2765c23704eSSong Gao     invalidate_tlb(env, index);
2775c23704eSSong Gao 
2785c23704eSSong Gao     if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) {
2795c23704eSSong Gao         env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc,
2805c23704eSSong Gao                                               TLB_MISC, E, 0);
2815c23704eSSong Gao         return;
2825c23704eSSong Gao     }
2835c23704eSSong Gao 
2845c23704eSSong Gao     fill_tlb_entry(env, index);
2855c23704eSSong Gao }
2865c23704eSSong Gao 
helper_tlbfill(CPULoongArchState * env)2875c23704eSSong Gao void helper_tlbfill(CPULoongArchState *env)
2885c23704eSSong Gao {
2895c23704eSSong Gao     uint64_t address, entryhi;
2905c23704eSSong Gao     int index, set, stlb_idx;
2915c23704eSSong Gao     uint16_t pagesize, stlb_ps;
2925c23704eSSong Gao 
2935c23704eSSong Gao     if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) {
2945c23704eSSong Gao         entryhi = env->CSR_TLBREHI;
2955c23704eSSong Gao         pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS);
2965c23704eSSong Gao     } else {
2975c23704eSSong Gao         entryhi = env->CSR_TLBEHI;
2985c23704eSSong Gao         pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS);
2995c23704eSSong Gao     }
3005c23704eSSong Gao 
3015c23704eSSong Gao     stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
3025c23704eSSong Gao 
3035c23704eSSong Gao     if (pagesize == stlb_ps) {
3045c23704eSSong Gao         /* Only write into STLB bits [47:13] */
3055c23704eSSong Gao         address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT);
3065c23704eSSong Gao 
3075c23704eSSong Gao         /* Choose one set ramdomly */
3085c23704eSSong Gao         set = get_random_tlb(0, 7);
3095c23704eSSong Gao 
3105c23704eSSong Gao         /* Index in one set */
3115c23704eSSong Gao         stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */
3125c23704eSSong Gao 
3135c23704eSSong Gao         index = set * 256 + stlb_idx;
3145c23704eSSong Gao     } else {
3155c23704eSSong Gao         /* Only write into MTLB */
3165c23704eSSong Gao         index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1);
3175c23704eSSong Gao     }
3185c23704eSSong Gao 
3195c23704eSSong Gao     invalidate_tlb(env, index);
3205c23704eSSong Gao     fill_tlb_entry(env, index);
3215c23704eSSong Gao }
3225c23704eSSong Gao 
helper_tlbclr(CPULoongArchState * env)3235c23704eSSong Gao void helper_tlbclr(CPULoongArchState *env)
3245c23704eSSong Gao {
3255c23704eSSong Gao     LoongArchTLB *tlb;
3265c23704eSSong Gao     int i, index;
3275c23704eSSong Gao     uint16_t csr_asid, tlb_asid, tlb_g;
3285c23704eSSong Gao 
3295c23704eSSong Gao     csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID);
3305c23704eSSong Gao     index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
3315c23704eSSong Gao 
3325c23704eSSong Gao     if (index < LOONGARCH_STLB) {
3335c23704eSSong Gao         /* STLB. One line per operation */
3345c23704eSSong Gao         for (i = 0; i < 8; i++) {
3355c23704eSSong Gao             tlb = &env->tlb[i * 256 + (index % 256)];
3365c23704eSSong Gao             tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
3375c23704eSSong Gao             tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
3385c23704eSSong Gao             if (!tlb_g && tlb_asid == csr_asid) {
3395c23704eSSong Gao                 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
3405c23704eSSong Gao             }
3415c23704eSSong Gao         }
3425c23704eSSong Gao     } else if (index < LOONGARCH_TLB_MAX) {
3435c23704eSSong Gao         /* All MTLB entries */
3445c23704eSSong Gao         for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
3455c23704eSSong Gao             tlb = &env->tlb[i];
3465c23704eSSong Gao             tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
3475c23704eSSong Gao             tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
3485c23704eSSong Gao             if (!tlb_g && tlb_asid == csr_asid) {
3495c23704eSSong Gao                 tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
3505c23704eSSong Gao             }
3515c23704eSSong Gao         }
3525c23704eSSong Gao     }
3535c23704eSSong Gao 
3545c23704eSSong Gao     tlb_flush(env_cpu(env));
3555c23704eSSong Gao }
3565c23704eSSong Gao 
helper_tlbflush(CPULoongArchState * env)3575c23704eSSong Gao void helper_tlbflush(CPULoongArchState *env)
3585c23704eSSong Gao {
3595c23704eSSong Gao     int i, index;
3605c23704eSSong Gao 
3615c23704eSSong Gao     index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX);
3625c23704eSSong Gao 
3635c23704eSSong Gao     if (index < LOONGARCH_STLB) {
3645c23704eSSong Gao         /* STLB. One line per operation */
3655c23704eSSong Gao         for (i = 0; i < 8; i++) {
3665c23704eSSong Gao             int s_idx = i * 256 + (index % 256);
3675c23704eSSong Gao             env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc,
3685c23704eSSong Gao                                                   TLB_MISC, E, 0);
3695c23704eSSong Gao         }
3705c23704eSSong Gao     } else if (index < LOONGARCH_TLB_MAX) {
3715c23704eSSong Gao         /* All MTLB entries */
3725c23704eSSong Gao         for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) {
3735c23704eSSong Gao             env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
3745c23704eSSong Gao                                               TLB_MISC, E, 0);
3755c23704eSSong Gao         }
3765c23704eSSong Gao     }
3775c23704eSSong Gao 
3785c23704eSSong Gao     tlb_flush(env_cpu(env));
3795c23704eSSong Gao }
3805c23704eSSong Gao 
helper_invtlb_all(CPULoongArchState * env)3815c23704eSSong Gao void helper_invtlb_all(CPULoongArchState *env)
3825c23704eSSong Gao {
3835c23704eSSong Gao     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
3845c23704eSSong Gao         env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc,
3855c23704eSSong Gao                                           TLB_MISC, E, 0);
3865c23704eSSong Gao     }
3875c23704eSSong Gao     tlb_flush(env_cpu(env));
3885c23704eSSong Gao }
3895c23704eSSong Gao 
helper_invtlb_all_g(CPULoongArchState * env,uint32_t g)3905c23704eSSong Gao void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g)
3915c23704eSSong Gao {
3925c23704eSSong Gao     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
3935c23704eSSong Gao         LoongArchTLB *tlb = &env->tlb[i];
3945c23704eSSong Gao         uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
3955c23704eSSong Gao 
3965c23704eSSong Gao         if (tlb_g == g) {
3975c23704eSSong Gao             tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
3985c23704eSSong Gao         }
3995c23704eSSong Gao     }
4005c23704eSSong Gao     tlb_flush(env_cpu(env));
4015c23704eSSong Gao }
4025c23704eSSong Gao 
helper_invtlb_all_asid(CPULoongArchState * env,target_ulong info)4035c23704eSSong Gao void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info)
4045c23704eSSong Gao {
4055c23704eSSong Gao     uint16_t asid = info & R_CSR_ASID_ASID_MASK;
4065c23704eSSong Gao 
4075c23704eSSong Gao     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
4085c23704eSSong Gao         LoongArchTLB *tlb = &env->tlb[i];
4095c23704eSSong Gao         uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
4105c23704eSSong Gao         uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
4115c23704eSSong Gao 
4125c23704eSSong Gao         if (!tlb_g && (tlb_asid == asid)) {
4135c23704eSSong Gao             tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
4145c23704eSSong Gao         }
4155c23704eSSong Gao     }
4165c23704eSSong Gao     tlb_flush(env_cpu(env));
4175c23704eSSong Gao }
4185c23704eSSong Gao 
helper_invtlb_page_asid(CPULoongArchState * env,target_ulong info,target_ulong addr)4195c23704eSSong Gao void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info,
4205c23704eSSong Gao                              target_ulong addr)
4215c23704eSSong Gao {
4225c23704eSSong Gao     uint16_t asid = info & 0x3ff;
4235c23704eSSong Gao 
4245c23704eSSong Gao     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
4255c23704eSSong Gao         LoongArchTLB *tlb = &env->tlb[i];
4265c23704eSSong Gao         uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
4275c23704eSSong Gao         uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
4285c23704eSSong Gao         uint64_t vpn, tlb_vppn;
4295c23704eSSong Gao         uint8_t tlb_ps, compare_shift;
4305c23704eSSong Gao 
4315c23704eSSong Gao         if (i >= LOONGARCH_STLB) {
4325c23704eSSong Gao             tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
4335c23704eSSong Gao         } else {
4345c23704eSSong Gao             tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
4355c23704eSSong Gao         }
4365c23704eSSong Gao         tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
4375c23704eSSong Gao         vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
4385c23704eSSong Gao         compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
4395c23704eSSong Gao 
4405c23704eSSong Gao         if (!tlb_g && (tlb_asid == asid) &&
4415c23704eSSong Gao            (vpn == (tlb_vppn >> compare_shift))) {
4425c23704eSSong Gao             tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
4435c23704eSSong Gao         }
4445c23704eSSong Gao     }
4455c23704eSSong Gao     tlb_flush(env_cpu(env));
4465c23704eSSong Gao }
4475c23704eSSong Gao 
helper_invtlb_page_asid_or_g(CPULoongArchState * env,target_ulong info,target_ulong addr)4485c23704eSSong Gao void helper_invtlb_page_asid_or_g(CPULoongArchState *env,
4495c23704eSSong Gao                                   target_ulong info, target_ulong addr)
4505c23704eSSong Gao {
4515c23704eSSong Gao     uint16_t asid = info & 0x3ff;
4525c23704eSSong Gao 
4535c23704eSSong Gao     for (int i = 0; i < LOONGARCH_TLB_MAX; i++) {
4545c23704eSSong Gao         LoongArchTLB *tlb = &env->tlb[i];
4555c23704eSSong Gao         uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G);
4565c23704eSSong Gao         uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID);
4575c23704eSSong Gao         uint64_t vpn, tlb_vppn;
4585c23704eSSong Gao         uint8_t tlb_ps, compare_shift;
4595c23704eSSong Gao 
4605c23704eSSong Gao         if (i >= LOONGARCH_STLB) {
4615c23704eSSong Gao             tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS);
4625c23704eSSong Gao         } else {
4635c23704eSSong Gao             tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS);
4645c23704eSSong Gao         }
4655c23704eSSong Gao         tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN);
4665c23704eSSong Gao         vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1);
4675c23704eSSong Gao         compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT;
4685c23704eSSong Gao 
4695c23704eSSong Gao         if ((tlb_g || (tlb_asid == asid)) &&
4705c23704eSSong Gao             (vpn == (tlb_vppn >> compare_shift))) {
4715c23704eSSong Gao             tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0);
4725c23704eSSong Gao         }
4735c23704eSSong Gao     }
4745c23704eSSong Gao     tlb_flush(env_cpu(env));
4755c23704eSSong Gao }
4765c23704eSSong Gao 
loongarch_cpu_tlb_fill(CPUState * cs,vaddr address,int size,MMUAccessType access_type,int mmu_idx,bool probe,uintptr_t retaddr)4775c23704eSSong Gao bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
4785c23704eSSong Gao                             MMUAccessType access_type, int mmu_idx,
4795c23704eSSong Gao                             bool probe, uintptr_t retaddr)
4805c23704eSSong Gao {
481f3b603b9SPhilippe Mathieu-Daudé     CPULoongArchState *env = cpu_env(cs);
4825c23704eSSong Gao     hwaddr physical;
4835c23704eSSong Gao     int prot;
4845c23704eSSong Gao     int ret;
4855c23704eSSong Gao 
4865c23704eSSong Gao     /* Data access */
4875c23704eSSong Gao     ret = get_physical_address(env, &physical, &prot, address,
4885c23704eSSong Gao                                access_type, mmu_idx);
4895c23704eSSong Gao 
4905c23704eSSong Gao     if (ret == TLBRET_MATCH) {
4915c23704eSSong Gao         tlb_set_page(cs, address & TARGET_PAGE_MASK,
4925c23704eSSong Gao                      physical & TARGET_PAGE_MASK, prot,
4935c23704eSSong Gao                      mmu_idx, TARGET_PAGE_SIZE);
4945c23704eSSong Gao         qemu_log_mask(CPU_LOG_MMU,
4955c23704eSSong Gao                       "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx
4965c23704eSSong Gao                       " prot %d\n", __func__, address, physical, prot);
4975c23704eSSong Gao         return true;
4985c23704eSSong Gao     } else {
4995c23704eSSong Gao         qemu_log_mask(CPU_LOG_MMU,
5005c23704eSSong Gao                       "%s address=%" VADDR_PRIx " ret %d\n", __func__, address,
5015c23704eSSong Gao                       ret);
5025c23704eSSong Gao     }
5035c23704eSSong Gao     if (probe) {
5045c23704eSSong Gao         return false;
5055c23704eSSong Gao     }
5065c23704eSSong Gao     raise_mmu_exception(env, address, access_type, ret);
5075c23704eSSong Gao     cpu_loop_exit_restore(cs, retaddr);
5085c23704eSSong Gao }
5095c23704eSSong Gao 
helper_lddir(CPULoongArchState * env,target_ulong base,target_ulong level,uint32_t mem_idx)5105c23704eSSong Gao target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
5115c23704eSSong Gao                           target_ulong level, uint32_t mem_idx)
5125c23704eSSong Gao {
5135c23704eSSong Gao     CPUState *cs = env_cpu(env);
5145c23704eSSong Gao     target_ulong badvaddr, index, phys, ret;
5155c23704eSSong Gao     int shift;
5165c23704eSSong Gao     uint64_t dir_base, dir_width;
5179c70db9aSXianglai Li 
5189c70db9aSXianglai Li     if (unlikely((level == 0) || (level > 4))) {
5199c70db9aSXianglai Li         qemu_log_mask(LOG_GUEST_ERROR,
5209c70db9aSXianglai Li                       "Attepted LDDIR with level %"PRId64"\n", level);
5219c70db9aSXianglai Li         return base;
5229c70db9aSXianglai Li     }
5239c70db9aSXianglai Li 
5249c70db9aSXianglai Li     if (FIELD_EX64(base, TLBENTRY, HUGE)) {
5259c70db9aSXianglai Li         if (unlikely(level == 4)) {
5269c70db9aSXianglai Li             qemu_log_mask(LOG_GUEST_ERROR,
5279c70db9aSXianglai Li                           "Attempted use of level 4 huge page\n");
528*a18ffbcfSSong Gao             return base;
5299c70db9aSXianglai Li         }
5309c70db9aSXianglai Li 
5319c70db9aSXianglai Li         if (FIELD_EX64(base, TLBENTRY, LEVEL)) {
5329c70db9aSXianglai Li             return base;
5339c70db9aSXianglai Li         } else {
5349c70db9aSXianglai Li             return FIELD_DP64(base, TLBENTRY, LEVEL, level);
5359c70db9aSXianglai Li         }
5369c70db9aSXianglai Li     }
5375c23704eSSong Gao 
5385c23704eSSong Gao     badvaddr = env->CSR_TLBRBADV;
5395c23704eSSong Gao     base = base & TARGET_PHYS_MASK;
5405c23704eSSong Gao 
5415c23704eSSong Gao     /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */
5425c23704eSSong Gao     shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
5435c23704eSSong Gao     shift = (shift + 1) * 3;
5445c23704eSSong Gao 
5459c70db9aSXianglai Li     get_dir_base_width(env, &dir_base, &dir_width, level);
5465c23704eSSong Gao     index = (badvaddr >> dir_base) & ((1 << dir_width) - 1);
5475c23704eSSong Gao     phys = base | index << shift;
5485c23704eSSong Gao     ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
5495c23704eSSong Gao     return ret;
5505c23704eSSong Gao }
5515c23704eSSong Gao 
helper_ldpte(CPULoongArchState * env,target_ulong base,target_ulong odd,uint32_t mem_idx)5525c23704eSSong Gao void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
5535c23704eSSong Gao                   uint32_t mem_idx)
5545c23704eSSong Gao {
5555c23704eSSong Gao     CPUState *cs = env_cpu(env);
5565c23704eSSong Gao     target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv;
5575c23704eSSong Gao     int shift;
5585c23704eSSong Gao     uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
5595c23704eSSong Gao     uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
5609c70db9aSXianglai Li     uint64_t dir_base, dir_width;
5615c23704eSSong Gao 
5629c70db9aSXianglai Li     /*
5639c70db9aSXianglai Li      * The parameter "base" has only two types,
5649c70db9aSXianglai Li      * one is the page table base address,
5659c70db9aSXianglai Li      * whose bit 6 should be 0,
5669c70db9aSXianglai Li      * and the other is the huge page entry,
5679c70db9aSXianglai Li      * whose bit 6 should be 1.
5689c70db9aSXianglai Li      */
5695c23704eSSong Gao     base = base & TARGET_PHYS_MASK;
5709c70db9aSXianglai Li     if (FIELD_EX64(base, TLBENTRY, HUGE)) {
5719c70db9aSXianglai Li         /*
5729c70db9aSXianglai Li          * Gets the huge page level and Gets huge page size.
5739c70db9aSXianglai Li          * Clears the huge page level information in the entry.
5749c70db9aSXianglai Li          * Clears huge page bit.
5759c70db9aSXianglai Li          * Move HGLOBAL bit to GLOBAL bit.
5769c70db9aSXianglai Li          */
5779c70db9aSXianglai Li         get_dir_base_width(env, &dir_base, &dir_width,
5789c70db9aSXianglai Li                            FIELD_EX64(base, TLBENTRY, LEVEL));
5795c23704eSSong Gao 
5809c70db9aSXianglai Li         base = FIELD_DP64(base, TLBENTRY, LEVEL, 0);
5819c70db9aSXianglai Li         base = FIELD_DP64(base, TLBENTRY, HUGE, 0);
5829c70db9aSXianglai Li         if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) {
5839c70db9aSXianglai Li             base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0);
5849c70db9aSXianglai Li             base = FIELD_DP64(base, TLBENTRY, G, 1);
5859c70db9aSXianglai Li         }
5869c70db9aSXianglai Li 
5879c70db9aSXianglai Li         ps = dir_base + dir_width - 1;
5889c70db9aSXianglai Li         /*
5899c70db9aSXianglai Li          * Huge pages are evenly split into parity pages
5909c70db9aSXianglai Li          * when loaded into the tlb,
5919c70db9aSXianglai Li          * so the tlb page size needs to be divided by 2.
5929c70db9aSXianglai Li          */
5939c70db9aSXianglai Li         tmp0 = base;
5945c23704eSSong Gao         if (odd) {
5955c23704eSSong Gao             tmp0 += MAKE_64BIT_MASK(ps, 1);
5965c23704eSSong Gao         }
5975c23704eSSong Gao     } else {
5985c23704eSSong Gao         /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */
5995c23704eSSong Gao         shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
6005c23704eSSong Gao         shift = (shift + 1) * 3;
6015c23704eSSong Gao         badv = env->CSR_TLBRBADV;
6025c23704eSSong Gao 
6035c23704eSSong Gao         ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1);
6045c23704eSSong Gao         ptindex = ptindex & ~0x1;   /* clear bit 0 */
6055c23704eSSong Gao         ptoffset0 = ptindex << shift;
6065c23704eSSong Gao         ptoffset1 = (ptindex + 1) << shift;
6075c23704eSSong Gao 
6085c23704eSSong Gao         phys = base | (odd ? ptoffset1 : ptoffset0);
6095c23704eSSong Gao         tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
6105c23704eSSong Gao         ps = ptbase;
6115c23704eSSong Gao     }
6125c23704eSSong Gao 
6135c23704eSSong Gao     if (odd) {
6145c23704eSSong Gao         env->CSR_TLBRELO1 = tmp0;
6155c23704eSSong Gao     } else {
6165c23704eSSong Gao         env->CSR_TLBRELO0 = tmp0;
6175c23704eSSong Gao     }
6185c23704eSSong Gao     env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps);
6195c23704eSSong Gao }
620