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