16af0bf9cSbellard /* 24cb213dcSPhilippe Mathieu-Daudé * MIPS TLB (Translation lookaside buffer) helpers. 36af0bf9cSbellard * 46af0bf9cSbellard * Copyright (c) 2004-2005 Jocelyn Mayer 56af0bf9cSbellard * 66af0bf9cSbellard * This library is free software; you can redistribute it and/or 76af0bf9cSbellard * modify it under the terms of the GNU Lesser General Public 86af0bf9cSbellard * License as published by the Free Software Foundation; either 989975214SChetan Pant * version 2.1 of the License, or (at your option) any later version. 106af0bf9cSbellard * 116af0bf9cSbellard * This library is distributed in the hope that it will be useful, 126af0bf9cSbellard * but WITHOUT ANY WARRANTY; without even the implied warranty of 136af0bf9cSbellard * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 146af0bf9cSbellard * Lesser General Public License for more details. 156af0bf9cSbellard * 166af0bf9cSbellard * You should have received a copy of the GNU Lesser General Public 178167ee88SBlue Swirl * License along with this library; if not, see <http://www.gnu.org/licenses/>. 186af0bf9cSbellard */ 19c684822aSPeter Maydell #include "qemu/osdep.h" 202838b1d6SPhilippe Mathieu-Daudé #include "qemu/bitops.h" 21e37e863fSbellard 22e37e863fSbellard #include "cpu.h" 2326aa3d9aSPhilippe Mathieu-Daudé #include "internal.h" 24eb9b25c6SPhilippe Mathieu-Daudé #include "exec/cputlb.h" 2563c91552SPaolo Bonzini #include "exec/exec-all.h" 2674781c08SPhilippe Mathieu-Daudé #include "exec/page-protection.h" 279c2ff9cdSPierrick Bouvier #include "exec/target_page.h" 28*42fa9665SPhilippe Mathieu-Daudé #include "accel/tcg/cpu-ldst.h" 29508127e2SPaolo Bonzini #include "exec/log.h" 306575529bSPhilippe Mathieu-Daudé #include "exec/helper-proto.h" 316575529bSPhilippe Mathieu-Daudé 326575529bSPhilippe Mathieu-Daudé /* TLB management */ 336575529bSPhilippe Mathieu-Daudé static void r4k_mips_tlb_flush_extra(CPUMIPSState *env, int first) 346575529bSPhilippe Mathieu-Daudé { 356575529bSPhilippe Mathieu-Daudé /* Discard entries from env->tlb[first] onwards. */ 366575529bSPhilippe Mathieu-Daudé while (env->tlb->tlb_in_use > first) { 376575529bSPhilippe Mathieu-Daudé r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0); 386575529bSPhilippe Mathieu-Daudé } 396575529bSPhilippe Mathieu-Daudé } 406575529bSPhilippe Mathieu-Daudé 416575529bSPhilippe Mathieu-Daudé static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo) 426575529bSPhilippe Mathieu-Daudé { 436575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 446575529bSPhilippe Mathieu-Daudé return extract64(entrylo, 6, 54); 456575529bSPhilippe Mathieu-Daudé #else 466575529bSPhilippe Mathieu-Daudé return extract64(entrylo, 6, 24) | /* PFN */ 476575529bSPhilippe Mathieu-Daudé (extract64(entrylo, 32, 32) << 24); /* PFNX */ 486575529bSPhilippe Mathieu-Daudé #endif 496575529bSPhilippe Mathieu-Daudé } 506575529bSPhilippe Mathieu-Daudé 516575529bSPhilippe Mathieu-Daudé static void r4k_fill_tlb(CPUMIPSState *env, int idx) 526575529bSPhilippe Mathieu-Daudé { 536575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 546575529bSPhilippe Mathieu-Daudé uint64_t mask = env->CP0_PageMask >> (TARGET_PAGE_BITS + 1); 556575529bSPhilippe Mathieu-Daudé 566575529bSPhilippe Mathieu-Daudé /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */ 576575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 586575529bSPhilippe Mathieu-Daudé if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) { 596575529bSPhilippe Mathieu-Daudé tlb->EHINV = 1; 606575529bSPhilippe Mathieu-Daudé return; 616575529bSPhilippe Mathieu-Daudé } 626575529bSPhilippe Mathieu-Daudé tlb->EHINV = 0; 636575529bSPhilippe Mathieu-Daudé tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); 646575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 656575529bSPhilippe Mathieu-Daudé tlb->VPN &= env->SEGMask; 666575529bSPhilippe Mathieu-Daudé #endif 676575529bSPhilippe Mathieu-Daudé tlb->ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 686575529bSPhilippe Mathieu-Daudé tlb->MMID = env->CP0_MemoryMapID; 696575529bSPhilippe Mathieu-Daudé tlb->PageMask = env->CP0_PageMask; 706575529bSPhilippe Mathieu-Daudé tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; 716575529bSPhilippe Mathieu-Daudé tlb->V0 = (env->CP0_EntryLo0 & 2) != 0; 726575529bSPhilippe Mathieu-Daudé tlb->D0 = (env->CP0_EntryLo0 & 4) != 0; 736575529bSPhilippe Mathieu-Daudé tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7; 746575529bSPhilippe Mathieu-Daudé tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1; 756575529bSPhilippe Mathieu-Daudé tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1; 766575529bSPhilippe Mathieu-Daudé tlb->PFN[0] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) & ~mask) << 12; 776575529bSPhilippe Mathieu-Daudé tlb->V1 = (env->CP0_EntryLo1 & 2) != 0; 786575529bSPhilippe Mathieu-Daudé tlb->D1 = (env->CP0_EntryLo1 & 4) != 0; 796575529bSPhilippe Mathieu-Daudé tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7; 806575529bSPhilippe Mathieu-Daudé tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1; 816575529bSPhilippe Mathieu-Daudé tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1; 826575529bSPhilippe Mathieu-Daudé tlb->PFN[1] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) & ~mask) << 12; 836575529bSPhilippe Mathieu-Daudé } 846575529bSPhilippe Mathieu-Daudé 856575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbinv(CPUMIPSState *env) 866575529bSPhilippe Mathieu-Daudé { 876575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 886575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 896575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 906575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 916575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 926575529bSPhilippe Mathieu-Daudé int idx; 936575529bSPhilippe Mathieu-Daudé 946575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 956575529bSPhilippe Mathieu-Daudé for (idx = 0; idx < env->tlb->nb_tlb; idx++) { 966575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 976575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 986575529bSPhilippe Mathieu-Daudé if (!tlb->G && tlb_mmid == MMID) { 996575529bSPhilippe Mathieu-Daudé tlb->EHINV = 1; 1006575529bSPhilippe Mathieu-Daudé } 1016575529bSPhilippe Mathieu-Daudé } 1026575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 1036575529bSPhilippe Mathieu-Daudé } 1046575529bSPhilippe Mathieu-Daudé 1056575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbinvf(CPUMIPSState *env) 1066575529bSPhilippe Mathieu-Daudé { 1076575529bSPhilippe Mathieu-Daudé int idx; 1086575529bSPhilippe Mathieu-Daudé 1096575529bSPhilippe Mathieu-Daudé for (idx = 0; idx < env->tlb->nb_tlb; idx++) { 1106575529bSPhilippe Mathieu-Daudé env->tlb->mmu.r4k.tlb[idx].EHINV = 1; 1116575529bSPhilippe Mathieu-Daudé } 1126575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 1136575529bSPhilippe Mathieu-Daudé } 1146575529bSPhilippe Mathieu-Daudé 1156575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbwi(CPUMIPSState *env) 1166575529bSPhilippe Mathieu-Daudé { 1176575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 1186575529bSPhilippe Mathieu-Daudé target_ulong VPN; 1196575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 1206575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 1216575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 1226575529bSPhilippe Mathieu-Daudé bool EHINV, G, V0, D0, V1, D1, XI0, XI1, RI0, RI1; 1236575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 1246575529bSPhilippe Mathieu-Daudé int idx; 1256575529bSPhilippe Mathieu-Daudé 1266575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 1276575529bSPhilippe Mathieu-Daudé 1286575529bSPhilippe Mathieu-Daudé idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; 1296575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 1306575529bSPhilippe Mathieu-Daudé VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); 1316575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 1326575529bSPhilippe Mathieu-Daudé VPN &= env->SEGMask; 1336575529bSPhilippe Mathieu-Daudé #endif 1346575529bSPhilippe Mathieu-Daudé EHINV = (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) != 0; 1356575529bSPhilippe Mathieu-Daudé G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; 1366575529bSPhilippe Mathieu-Daudé V0 = (env->CP0_EntryLo0 & 2) != 0; 1376575529bSPhilippe Mathieu-Daudé D0 = (env->CP0_EntryLo0 & 4) != 0; 1386575529bSPhilippe Mathieu-Daudé XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) &1; 1396575529bSPhilippe Mathieu-Daudé RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) &1; 1406575529bSPhilippe Mathieu-Daudé V1 = (env->CP0_EntryLo1 & 2) != 0; 1416575529bSPhilippe Mathieu-Daudé D1 = (env->CP0_EntryLo1 & 4) != 0; 1426575529bSPhilippe Mathieu-Daudé XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) &1; 1436575529bSPhilippe Mathieu-Daudé RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) &1; 1446575529bSPhilippe Mathieu-Daudé 1456575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 1466575529bSPhilippe Mathieu-Daudé /* 1476575529bSPhilippe Mathieu-Daudé * Discard cached TLB entries, unless tlbwi is just upgrading access 1486575529bSPhilippe Mathieu-Daudé * permissions on the current entry. 1496575529bSPhilippe Mathieu-Daudé */ 1506575529bSPhilippe Mathieu-Daudé if (tlb->VPN != VPN || tlb_mmid != MMID || tlb->G != G || 1516575529bSPhilippe Mathieu-Daudé (!tlb->EHINV && EHINV) || 1526575529bSPhilippe Mathieu-Daudé (tlb->V0 && !V0) || (tlb->D0 && !D0) || 1536575529bSPhilippe Mathieu-Daudé (!tlb->XI0 && XI0) || (!tlb->RI0 && RI0) || 1546575529bSPhilippe Mathieu-Daudé (tlb->V1 && !V1) || (tlb->D1 && !D1) || 1556575529bSPhilippe Mathieu-Daudé (!tlb->XI1 && XI1) || (!tlb->RI1 && RI1)) { 1566575529bSPhilippe Mathieu-Daudé r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); 1576575529bSPhilippe Mathieu-Daudé } 1586575529bSPhilippe Mathieu-Daudé 1596575529bSPhilippe Mathieu-Daudé r4k_invalidate_tlb(env, idx, 0); 1606575529bSPhilippe Mathieu-Daudé r4k_fill_tlb(env, idx); 1616575529bSPhilippe Mathieu-Daudé } 1626575529bSPhilippe Mathieu-Daudé 1636575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbwr(CPUMIPSState *env) 1646575529bSPhilippe Mathieu-Daudé { 1656575529bSPhilippe Mathieu-Daudé int r = cpu_mips_get_random(env); 1666575529bSPhilippe Mathieu-Daudé 1676575529bSPhilippe Mathieu-Daudé r4k_invalidate_tlb(env, r, 1); 1686575529bSPhilippe Mathieu-Daudé r4k_fill_tlb(env, r); 1696575529bSPhilippe Mathieu-Daudé } 1706575529bSPhilippe Mathieu-Daudé 1716575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbp(CPUMIPSState *env) 1726575529bSPhilippe Mathieu-Daudé { 1736575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 1746575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 1756575529bSPhilippe Mathieu-Daudé target_ulong mask; 1766575529bSPhilippe Mathieu-Daudé target_ulong tag; 1776575529bSPhilippe Mathieu-Daudé target_ulong VPN; 1786575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 1796575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 1806575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 1816575529bSPhilippe Mathieu-Daudé int i; 1826575529bSPhilippe Mathieu-Daudé 1836575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 1846575529bSPhilippe Mathieu-Daudé for (i = 0; i < env->tlb->nb_tlb; i++) { 1856575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[i]; 1866575529bSPhilippe Mathieu-Daudé /* 1k pages are not supported. */ 1876575529bSPhilippe Mathieu-Daudé mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 1886575529bSPhilippe Mathieu-Daudé tag = env->CP0_EntryHi & ~mask; 1896575529bSPhilippe Mathieu-Daudé VPN = tlb->VPN & ~mask; 1906575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 1916575529bSPhilippe Mathieu-Daudé tag &= env->SEGMask; 1926575529bSPhilippe Mathieu-Daudé #endif 1936575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 1946575529bSPhilippe Mathieu-Daudé /* Check ASID/MMID, virtual page number & size */ 1956575529bSPhilippe Mathieu-Daudé if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { 1966575529bSPhilippe Mathieu-Daudé /* TLB match */ 1976575529bSPhilippe Mathieu-Daudé env->CP0_Index = i; 1986575529bSPhilippe Mathieu-Daudé break; 1996575529bSPhilippe Mathieu-Daudé } 2006575529bSPhilippe Mathieu-Daudé } 2016575529bSPhilippe Mathieu-Daudé if (i == env->tlb->nb_tlb) { 2026575529bSPhilippe Mathieu-Daudé /* No match. Discard any shadow entries, if any of them match. */ 2036575529bSPhilippe Mathieu-Daudé for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) { 2046575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[i]; 2056575529bSPhilippe Mathieu-Daudé /* 1k pages are not supported. */ 2066575529bSPhilippe Mathieu-Daudé mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 2076575529bSPhilippe Mathieu-Daudé tag = env->CP0_EntryHi & ~mask; 2086575529bSPhilippe Mathieu-Daudé VPN = tlb->VPN & ~mask; 2096575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 2106575529bSPhilippe Mathieu-Daudé tag &= env->SEGMask; 2116575529bSPhilippe Mathieu-Daudé #endif 2126575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 2136575529bSPhilippe Mathieu-Daudé /* Check ASID/MMID, virtual page number & size */ 2146575529bSPhilippe Mathieu-Daudé if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag) { 2156575529bSPhilippe Mathieu-Daudé r4k_mips_tlb_flush_extra(env, i); 2166575529bSPhilippe Mathieu-Daudé break; 2176575529bSPhilippe Mathieu-Daudé } 2186575529bSPhilippe Mathieu-Daudé } 2196575529bSPhilippe Mathieu-Daudé 2206575529bSPhilippe Mathieu-Daudé env->CP0_Index |= 0x80000000; 2216575529bSPhilippe Mathieu-Daudé } 2226575529bSPhilippe Mathieu-Daudé } 2236575529bSPhilippe Mathieu-Daudé 2246575529bSPhilippe Mathieu-Daudé static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn) 2256575529bSPhilippe Mathieu-Daudé { 2266575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 2276575529bSPhilippe Mathieu-Daudé return tlb_pfn << 6; 2286575529bSPhilippe Mathieu-Daudé #else 2296575529bSPhilippe Mathieu-Daudé return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */ 2306575529bSPhilippe Mathieu-Daudé (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */ 2316575529bSPhilippe Mathieu-Daudé #endif 2326575529bSPhilippe Mathieu-Daudé } 2336575529bSPhilippe Mathieu-Daudé 2346575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbr(CPUMIPSState *env) 2356575529bSPhilippe Mathieu-Daudé { 2366575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 2376575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 2386575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 2396575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 2406575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 2416575529bSPhilippe Mathieu-Daudé int idx; 2426575529bSPhilippe Mathieu-Daudé 2436575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 2446575529bSPhilippe Mathieu-Daudé idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; 2456575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 2466575529bSPhilippe Mathieu-Daudé 2476575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 2486575529bSPhilippe Mathieu-Daudé /* If this will change the current ASID/MMID, flush qemu's TLB. */ 2496575529bSPhilippe Mathieu-Daudé if (MMID != tlb_mmid) { 2506575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 2516575529bSPhilippe Mathieu-Daudé } 2526575529bSPhilippe Mathieu-Daudé 2536575529bSPhilippe Mathieu-Daudé r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); 2546575529bSPhilippe Mathieu-Daudé 2556575529bSPhilippe Mathieu-Daudé if (tlb->EHINV) { 2566575529bSPhilippe Mathieu-Daudé env->CP0_EntryHi = 1 << CP0EnHi_EHINV; 2576575529bSPhilippe Mathieu-Daudé env->CP0_PageMask = 0; 2586575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo0 = 0; 2596575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo1 = 0; 2606575529bSPhilippe Mathieu-Daudé } else { 2616575529bSPhilippe Mathieu-Daudé env->CP0_EntryHi = mi ? tlb->VPN : tlb->VPN | tlb->ASID; 2626575529bSPhilippe Mathieu-Daudé env->CP0_MemoryMapID = tlb->MMID; 2636575529bSPhilippe Mathieu-Daudé env->CP0_PageMask = tlb->PageMask; 2646575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | 2656575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->RI0 << CP0EnLo_RI) | 2666575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) | 2676575529bSPhilippe Mathieu-Daudé get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12); 2686575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | 2696575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->RI1 << CP0EnLo_RI) | 2706575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) | 2716575529bSPhilippe Mathieu-Daudé get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12); 2726575529bSPhilippe Mathieu-Daudé } 2736575529bSPhilippe Mathieu-Daudé } 2746575529bSPhilippe Mathieu-Daudé 2756575529bSPhilippe Mathieu-Daudé void helper_tlbwi(CPUMIPSState *env) 2766575529bSPhilippe Mathieu-Daudé { 2776575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbwi(env); 2786575529bSPhilippe Mathieu-Daudé } 2796575529bSPhilippe Mathieu-Daudé 2806575529bSPhilippe Mathieu-Daudé void helper_tlbwr(CPUMIPSState *env) 2816575529bSPhilippe Mathieu-Daudé { 2826575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbwr(env); 2836575529bSPhilippe Mathieu-Daudé } 2846575529bSPhilippe Mathieu-Daudé 2856575529bSPhilippe Mathieu-Daudé void helper_tlbp(CPUMIPSState *env) 2866575529bSPhilippe Mathieu-Daudé { 2876575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbp(env); 2886575529bSPhilippe Mathieu-Daudé } 2896575529bSPhilippe Mathieu-Daudé 2906575529bSPhilippe Mathieu-Daudé void helper_tlbr(CPUMIPSState *env) 2916575529bSPhilippe Mathieu-Daudé { 2926575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbr(env); 2936575529bSPhilippe Mathieu-Daudé } 2946575529bSPhilippe Mathieu-Daudé 2956575529bSPhilippe Mathieu-Daudé void helper_tlbinv(CPUMIPSState *env) 2966575529bSPhilippe Mathieu-Daudé { 2976575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbinv(env); 2986575529bSPhilippe Mathieu-Daudé } 2996575529bSPhilippe Mathieu-Daudé 3006575529bSPhilippe Mathieu-Daudé void helper_tlbinvf(CPUMIPSState *env) 3016575529bSPhilippe Mathieu-Daudé { 3026575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbinvf(env); 3036575529bSPhilippe Mathieu-Daudé } 3046575529bSPhilippe Mathieu-Daudé 3056575529bSPhilippe Mathieu-Daudé static void global_invalidate_tlb(CPUMIPSState *env, 3066575529bSPhilippe Mathieu-Daudé uint32_t invMsgVPN2, 3076575529bSPhilippe Mathieu-Daudé uint8_t invMsgR, 3086575529bSPhilippe Mathieu-Daudé uint32_t invMsgMMid, 3096575529bSPhilippe Mathieu-Daudé bool invAll, 3106575529bSPhilippe Mathieu-Daudé bool invVAMMid, 3116575529bSPhilippe Mathieu-Daudé bool invMMid, 3126575529bSPhilippe Mathieu-Daudé bool invVA) 3136575529bSPhilippe Mathieu-Daudé { 3146575529bSPhilippe Mathieu-Daudé 3156575529bSPhilippe Mathieu-Daudé int idx; 3166575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 3176575529bSPhilippe Mathieu-Daudé bool VAMatch; 3186575529bSPhilippe Mathieu-Daudé bool MMidMatch; 3196575529bSPhilippe Mathieu-Daudé 3206575529bSPhilippe Mathieu-Daudé for (idx = 0; idx < env->tlb->nb_tlb; idx++) { 3216575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 3226575529bSPhilippe Mathieu-Daudé VAMatch = 3236575529bSPhilippe Mathieu-Daudé (((tlb->VPN & ~tlb->PageMask) == (invMsgVPN2 & ~tlb->PageMask)) 3246575529bSPhilippe Mathieu-Daudé #ifdef TARGET_MIPS64 3256575529bSPhilippe Mathieu-Daudé && 3266575529bSPhilippe Mathieu-Daudé (extract64(env->CP0_EntryHi, 62, 2) == invMsgR) 3276575529bSPhilippe Mathieu-Daudé #endif 3286575529bSPhilippe Mathieu-Daudé ); 3296575529bSPhilippe Mathieu-Daudé MMidMatch = tlb->MMID == invMsgMMid; 3306575529bSPhilippe Mathieu-Daudé if ((invAll && (idx > env->CP0_Wired)) || 3316575529bSPhilippe Mathieu-Daudé (VAMatch && invVAMMid && (tlb->G || MMidMatch)) || 3326575529bSPhilippe Mathieu-Daudé (VAMatch && invVA) || 3336575529bSPhilippe Mathieu-Daudé (MMidMatch && !(tlb->G) && invMMid)) { 3346575529bSPhilippe Mathieu-Daudé tlb->EHINV = 1; 3356575529bSPhilippe Mathieu-Daudé } 3366575529bSPhilippe Mathieu-Daudé } 3376575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 3386575529bSPhilippe Mathieu-Daudé } 3396575529bSPhilippe Mathieu-Daudé 3406575529bSPhilippe Mathieu-Daudé void helper_ginvt(CPUMIPSState *env, target_ulong arg, uint32_t type) 3416575529bSPhilippe Mathieu-Daudé { 3426575529bSPhilippe Mathieu-Daudé bool invAll = type == 0; 3436575529bSPhilippe Mathieu-Daudé bool invVA = type == 1; 3446575529bSPhilippe Mathieu-Daudé bool invMMid = type == 2; 3456575529bSPhilippe Mathieu-Daudé bool invVAMMid = type == 3; 3466575529bSPhilippe Mathieu-Daudé uint32_t invMsgVPN2 = arg & (TARGET_PAGE_MASK << 1); 3476575529bSPhilippe Mathieu-Daudé uint8_t invMsgR = 0; 3486575529bSPhilippe Mathieu-Daudé uint32_t invMsgMMid = env->CP0_MemoryMapID; 3496575529bSPhilippe Mathieu-Daudé CPUState *other_cs = first_cpu; 3506575529bSPhilippe Mathieu-Daudé 3516575529bSPhilippe Mathieu-Daudé #ifdef TARGET_MIPS64 3526575529bSPhilippe Mathieu-Daudé invMsgR = extract64(arg, 62, 2); 3536575529bSPhilippe Mathieu-Daudé #endif 3546575529bSPhilippe Mathieu-Daudé 3556575529bSPhilippe Mathieu-Daudé CPU_FOREACH(other_cs) { 3566575529bSPhilippe Mathieu-Daudé MIPSCPU *other_cpu = MIPS_CPU(other_cs); 3576575529bSPhilippe Mathieu-Daudé global_invalidate_tlb(&other_cpu->env, invMsgVPN2, invMsgR, invMsgMMid, 3586575529bSPhilippe Mathieu-Daudé invAll, invVAMMid, invMMid, invVA); 3596575529bSPhilippe Mathieu-Daudé } 3606575529bSPhilippe Mathieu-Daudé } 3616af0bf9cSbellard 36229929e34Sths /* no MMU emulation */ 363f3185ec2SPhilippe Mathieu-Daudé static int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, 364edbd4992SPhilippe Mathieu-Daudé target_ulong address, MMUAccessType access_type) 36529929e34Sths { 36629929e34Sths *physical = address; 3677353113fSJakub Jermář *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 36829929e34Sths return TLBRET_MATCH; 36929929e34Sths } 37029929e34Sths 37129929e34Sths /* fixed mapping MMU emulation */ 372f3185ec2SPhilippe Mathieu-Daudé static int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, 373f3185ec2SPhilippe Mathieu-Daudé int *prot, target_ulong address, 374f3185ec2SPhilippe Mathieu-Daudé MMUAccessType access_type) 37529929e34Sths { 37629929e34Sths if (address <= (int32_t)0x7FFFFFFFUL) { 377d7551eceSAleksandar Markovic if (!(env->CP0_Status & (1 << CP0St_ERL))) { 37829929e34Sths *physical = address + 0x40000000UL; 379d7551eceSAleksandar Markovic } else { 38029929e34Sths *physical = address; 381d7551eceSAleksandar Markovic } 382d7551eceSAleksandar Markovic } else if (address <= (int32_t)0xBFFFFFFFUL) { 38329929e34Sths *physical = address & 0x1FFFFFFF; 384d7551eceSAleksandar Markovic } else { 38529929e34Sths *physical = address; 386d7551eceSAleksandar Markovic } 38729929e34Sths 3887353113fSJakub Jermář *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 38929929e34Sths return TLBRET_MATCH; 39029929e34Sths } 39129929e34Sths 39229929e34Sths /* MIPS32/MIPS64 R4000-style MMU emulation */ 393f3185ec2SPhilippe Mathieu-Daudé static int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, 394edbd4992SPhilippe Mathieu-Daudé target_ulong address, MMUAccessType access_type) 3956af0bf9cSbellard { 3962d72e7b0SPaul Burton uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 39799029be1SYongbok Kim uint32_t MMID = env->CP0_MemoryMapID; 39899029be1SYongbok Kim bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 39999029be1SYongbok Kim uint32_t tlb_mmid; 4003b1c8be4Sths int i; 4016af0bf9cSbellard 40299029be1SYongbok Kim MMID = mi ? MMID : (uint32_t) ASID; 40399029be1SYongbok Kim 404ead9360eSths for (i = 0; i < env->tlb->tlb_in_use; i++) { 405c227f099SAnthony Liguori r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; 4063b1c8be4Sths /* 1k pages are not supported. */ 407f2e9ebefSths target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 4083b1c8be4Sths target_ulong tag = address & ~mask; 409f2e9ebefSths target_ulong VPN = tlb->VPN & ~mask; 410d26bc211Sths #if defined(TARGET_MIPS64) 411e034e2c3Sths tag &= env->SEGMask; 412100ce988Sths #endif 4133b1c8be4Sths 41499029be1SYongbok Kim /* Check ASID/MMID, virtual page number & size */ 41599029be1SYongbok Kim tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 41699029be1SYongbok Kim if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { 4176af0bf9cSbellard /* TLB match */ 418f2e9ebefSths int n = !!(address & mask & ~(mask >> 1)); 4196af0bf9cSbellard /* Check access rights */ 4202fb58b73SLeon Alrae if (!(n ? tlb->V1 : tlb->V0)) { 42143057ab1Sbellard return TLBRET_INVALID; 4222fb58b73SLeon Alrae } 423edbd4992SPhilippe Mathieu-Daudé if (access_type == MMU_INST_FETCH && (n ? tlb->XI1 : tlb->XI0)) { 4242fb58b73SLeon Alrae return TLBRET_XI; 4252fb58b73SLeon Alrae } 426edbd4992SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_LOAD && (n ? tlb->RI1 : tlb->RI0)) { 4272fb58b73SLeon Alrae return TLBRET_RI; 4282fb58b73SLeon Alrae } 429edbd4992SPhilippe Mathieu-Daudé if (access_type != MMU_DATA_STORE || (n ? tlb->D1 : tlb->D0)) { 4303b1c8be4Sths *physical = tlb->PFN[n] | (address & (mask >> 1)); 4319fb63ac2Sbellard *prot = PAGE_READ; 432d7551eceSAleksandar Markovic if (n ? tlb->D1 : tlb->D0) { 4339fb63ac2Sbellard *prot |= PAGE_WRITE; 434d7551eceSAleksandar Markovic } 4357353113fSJakub Jermář if (!(n ? tlb->XI1 : tlb->XI0)) { 4367353113fSJakub Jermář *prot |= PAGE_EXEC; 4377353113fSJakub Jermář } 43843057ab1Sbellard return TLBRET_MATCH; 4396af0bf9cSbellard } 44043057ab1Sbellard return TLBRET_DIRTY; 4416af0bf9cSbellard } 4426af0bf9cSbellard } 44343057ab1Sbellard return TLBRET_NOMATCH; 4446af0bf9cSbellard } 4456af0bf9cSbellard 446f2c5b39eSPhilippe Mathieu-Daudé static void no_mmu_init(CPUMIPSState *env, const mips_def_t *def) 447f2c5b39eSPhilippe Mathieu-Daudé { 448f2c5b39eSPhilippe Mathieu-Daudé env->tlb->nb_tlb = 1; 449f2c5b39eSPhilippe Mathieu-Daudé env->tlb->map_address = &no_mmu_map_address; 450f2c5b39eSPhilippe Mathieu-Daudé } 451f2c5b39eSPhilippe Mathieu-Daudé 452f2c5b39eSPhilippe Mathieu-Daudé static void fixed_mmu_init(CPUMIPSState *env, const mips_def_t *def) 453f2c5b39eSPhilippe Mathieu-Daudé { 454f2c5b39eSPhilippe Mathieu-Daudé env->tlb->nb_tlb = 1; 455f2c5b39eSPhilippe Mathieu-Daudé env->tlb->map_address = &fixed_mmu_map_address; 456f2c5b39eSPhilippe Mathieu-Daudé } 457f2c5b39eSPhilippe Mathieu-Daudé 458f2c5b39eSPhilippe Mathieu-Daudé static void r4k_mmu_init(CPUMIPSState *env, const mips_def_t *def) 459f2c5b39eSPhilippe Mathieu-Daudé { 460f2c5b39eSPhilippe Mathieu-Daudé env->tlb->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63); 461f2c5b39eSPhilippe Mathieu-Daudé env->tlb->map_address = &r4k_map_address; 462f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbwi = r4k_helper_tlbwi; 463f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbwr = r4k_helper_tlbwr; 464f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbp = r4k_helper_tlbp; 465f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbr = r4k_helper_tlbr; 466f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbinv = r4k_helper_tlbinv; 467f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbinvf = r4k_helper_tlbinvf; 468f2c5b39eSPhilippe Mathieu-Daudé } 469f2c5b39eSPhilippe Mathieu-Daudé 470f2c5b39eSPhilippe Mathieu-Daudé void mmu_init(CPUMIPSState *env, const mips_def_t *def) 471f2c5b39eSPhilippe Mathieu-Daudé { 472f2c5b39eSPhilippe Mathieu-Daudé env->tlb = g_malloc0(sizeof(CPUMIPSTLBContext)); 473f2c5b39eSPhilippe Mathieu-Daudé 474f2c5b39eSPhilippe Mathieu-Daudé switch (def->mmu_type) { 475f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_NONE: 476f2c5b39eSPhilippe Mathieu-Daudé no_mmu_init(env, def); 477f2c5b39eSPhilippe Mathieu-Daudé break; 478f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R4000: 479f2c5b39eSPhilippe Mathieu-Daudé r4k_mmu_init(env, def); 480f2c5b39eSPhilippe Mathieu-Daudé break; 481f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_FMT: 482f2c5b39eSPhilippe Mathieu-Daudé fixed_mmu_init(env, def); 483f2c5b39eSPhilippe Mathieu-Daudé break; 484f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R3000: 485f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R6000: 486f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R8000: 487f2c5b39eSPhilippe Mathieu-Daudé default: 488f2c5b39eSPhilippe Mathieu-Daudé cpu_abort(env_cpu(env), "MMU type not supported\n"); 489f2c5b39eSPhilippe Mathieu-Daudé } 490f2c5b39eSPhilippe Mathieu-Daudé } 491f2c5b39eSPhilippe Mathieu-Daudé 492d10eb08fSAlex Bennée void cpu_mips_tlb_flush(CPUMIPSState *env) 493e6623d88SPaolo Bonzini { 494e6623d88SPaolo Bonzini /* Flush qemu's TLB and discard all shadowed entries. */ 4955a7330b3SRichard Henderson tlb_flush(env_cpu(env)); 496e6623d88SPaolo Bonzini env->tlb->tlb_in_use = env->tlb->nb_tlb; 497e6623d88SPaolo Bonzini } 498e6623d88SPaolo Bonzini 4997db13faeSAndreas Färber static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, 500ca354f00SPhilippe Mathieu-Daudé MMUAccessType access_type, int tlb_error) 5016af0bf9cSbellard { 5025a7330b3SRichard Henderson CPUState *cs = env_cpu(env); 5036af0bf9cSbellard int exception = 0, error_code = 0; 5046af0bf9cSbellard 505ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_INST_FETCH) { 506aea14095SLeon Alrae error_code |= EXCP_INST_NOTAVAIL; 507aea14095SLeon Alrae } 508aea14095SLeon Alrae 5091147e189SAurelien Jarno switch (tlb_error) { 5106af0bf9cSbellard default: 51143057ab1Sbellard case TLBRET_BADADDR: 5126af0bf9cSbellard /* Reference to kernel address from user mode or supervisor mode */ 5136af0bf9cSbellard /* Reference to supervisor address from user mode */ 514ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_STORE) { 5156af0bf9cSbellard exception = EXCP_AdES; 5169f6bcedbSLeon Alrae } else { 5176af0bf9cSbellard exception = EXCP_AdEL; 5189f6bcedbSLeon Alrae } 5196af0bf9cSbellard break; 52043057ab1Sbellard case TLBRET_NOMATCH: 5216af0bf9cSbellard /* No TLB match for a mapped address */ 522ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_STORE) { 5236af0bf9cSbellard exception = EXCP_TLBS; 5249f6bcedbSLeon Alrae } else { 5256af0bf9cSbellard exception = EXCP_TLBL; 5269f6bcedbSLeon Alrae } 527aea14095SLeon Alrae error_code |= EXCP_TLB_NOMATCH; 5286af0bf9cSbellard break; 52943057ab1Sbellard case TLBRET_INVALID: 5306af0bf9cSbellard /* TLB match with no valid bit */ 531ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_STORE) { 5326af0bf9cSbellard exception = EXCP_TLBS; 5339f6bcedbSLeon Alrae } else { 5346af0bf9cSbellard exception = EXCP_TLBL; 5359f6bcedbSLeon Alrae } 5366af0bf9cSbellard break; 53743057ab1Sbellard case TLBRET_DIRTY: 5386af0bf9cSbellard /* TLB match but 'D' bit is cleared */ 5396af0bf9cSbellard exception = EXCP_LTLBL; 5406af0bf9cSbellard break; 54192ceb440SLeon Alrae case TLBRET_XI: 54292ceb440SLeon Alrae /* Execute-Inhibit Exception */ 54392ceb440SLeon Alrae if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { 54492ceb440SLeon Alrae exception = EXCP_TLBXI; 54592ceb440SLeon Alrae } else { 54692ceb440SLeon Alrae exception = EXCP_TLBL; 54792ceb440SLeon Alrae } 54892ceb440SLeon Alrae break; 54992ceb440SLeon Alrae case TLBRET_RI: 55092ceb440SLeon Alrae /* Read-Inhibit Exception */ 55192ceb440SLeon Alrae if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { 55292ceb440SLeon Alrae exception = EXCP_TLBRI; 55392ceb440SLeon Alrae } else { 55492ceb440SLeon Alrae exception = EXCP_TLBL; 55592ceb440SLeon Alrae } 55692ceb440SLeon Alrae break; 5576af0bf9cSbellard } 5586af0bf9cSbellard /* Raise exception */ 559e807bcc1SYongbok Kim if (!(env->hflags & MIPS_HFLAG_DM)) { 5606af0bf9cSbellard env->CP0_BadVAddr = address; 561e807bcc1SYongbok Kim } 562100ce988Sths env->CP0_Context = (env->CP0_Context & ~0x007fffff) | 5634ad40f36Sbellard ((address >> 9) & 0x007ffff0); 5646ec98bd7SPaul Burton env->CP0_EntryHi = (env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask) | 565701074a6SLeon Alrae (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) | 5666ec98bd7SPaul Burton (address & (TARGET_PAGE_MASK << 1)); 567d26bc211Sths #if defined(TARGET_MIPS64) 568e034e2c3Sths env->CP0_EntryHi &= env->SEGMask; 56960270f85SYongbok Kim env->CP0_XContext = 570d7551eceSAleksandar Markovic (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | /* PTEBase */ 571d7551eceSAleksandar Markovic (extract64(address, 62, 2) << (env->SEGBITS - 9)) | /* R */ 572d7551eceSAleksandar Markovic (extract64(address, 13, env->SEGBITS - 13) << 4); /* BadVPN2 */ 573100ce988Sths #endif 57427103424SAndreas Färber cs->exception_index = exception; 5756af0bf9cSbellard env->error_code = error_code; 5761147e189SAurelien Jarno } 5771147e189SAurelien Jarno 578074cfcb4SYongbok Kim #if !defined(TARGET_MIPS64) 579074cfcb4SYongbok Kim 580074cfcb4SYongbok Kim /* 581074cfcb4SYongbok Kim * Perform hardware page table walk 582074cfcb4SYongbok Kim * 583074cfcb4SYongbok Kim * Memory accesses are performed using the KERNEL privilege level. 584074cfcb4SYongbok Kim * Synchronous exceptions detected on memory accesses cause a silent exit 585074cfcb4SYongbok Kim * from page table walking, resulting in a TLB or XTLB Refill exception. 586074cfcb4SYongbok Kim * 587074cfcb4SYongbok Kim * Implementations are not required to support page table walk memory 588074cfcb4SYongbok Kim * accesses from mapped memory regions. When an unsupported access is 589074cfcb4SYongbok Kim * attempted, a silent exit is taken, resulting in a TLB or XTLB Refill 590074cfcb4SYongbok Kim * exception. 591074cfcb4SYongbok Kim * 592074cfcb4SYongbok Kim * Note that if an exception is caused by AddressTranslation or LoadMemory 593074cfcb4SYongbok Kim * functions, the exception is not taken, a silent exit is taken, 594074cfcb4SYongbok Kim * resulting in a TLB or XTLB Refill exception. 595074cfcb4SYongbok Kim */ 596074cfcb4SYongbok Kim 597453ba4f6SPhilippe Mathieu-Daudé static bool get_pte(CPUMIPSState *env, uint64_t vaddr, MemOp op, 5987ce9760dSPhilippe Mathieu-Daudé uint64_t *pte, unsigned ptw_mmu_idx) 599074cfcb4SYongbok Kim { 6007ce9760dSPhilippe Mathieu-Daudé MemOpIdx oi; 6017ce9760dSPhilippe Mathieu-Daudé 602453ba4f6SPhilippe Mathieu-Daudé if ((vaddr & (memop_size(op) - 1)) != 0) { 603074cfcb4SYongbok Kim return false; 604074cfcb4SYongbok Kim } 6057ce9760dSPhilippe Mathieu-Daudé 6062cf8226fSPhilippe Mathieu-Daudé oi = make_memop_idx(op | mo_endian_env(env), ptw_mmu_idx); 607453ba4f6SPhilippe Mathieu-Daudé if (op == MO_64) { 60844017c66SPhilippe Mathieu-Daudé *pte = cpu_ldq_mmu(env, vaddr, oi, 0); 609074cfcb4SYongbok Kim } else { 61044017c66SPhilippe Mathieu-Daudé *pte = cpu_ldl_mmu(env, vaddr, oi, 0); 611074cfcb4SYongbok Kim } 6127ce9760dSPhilippe Mathieu-Daudé 613074cfcb4SYongbok Kim return true; 614074cfcb4SYongbok Kim } 615074cfcb4SYongbok Kim 616074cfcb4SYongbok Kim static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, 617453ba4f6SPhilippe Mathieu-Daudé MemOp op, int ptei) 618074cfcb4SYongbok Kim { 619453ba4f6SPhilippe Mathieu-Daudé unsigned entry_size = memop_size(op) << 3; 620074cfcb4SYongbok Kim uint64_t result = entry; 621074cfcb4SYongbok Kim uint64_t rixi; 622074cfcb4SYongbok Kim if (ptei > entry_size) { 623074cfcb4SYongbok Kim ptei -= 32; 624074cfcb4SYongbok Kim } 625074cfcb4SYongbok Kim result >>= (ptei - 2); 626074cfcb4SYongbok Kim rixi = result & 3; 627074cfcb4SYongbok Kim result >>= 2; 628074cfcb4SYongbok Kim result |= rixi << CP0EnLo_XI; 629074cfcb4SYongbok Kim return result; 630074cfcb4SYongbok Kim } 631074cfcb4SYongbok Kim 632074cfcb4SYongbok Kim static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, 633074cfcb4SYongbok Kim int directory_index, bool *huge_page, bool *hgpg_directory_hit, 63460a38a3aSPhilippe Mathieu-Daudé uint64_t *pw_entrylo0, uint64_t *pw_entrylo1, 635453ba4f6SPhilippe Mathieu-Daudé MemOp directory_mop, MemOp leaf_mop, int ptw_mmu_idx) 636074cfcb4SYongbok Kim { 637074cfcb4SYongbok Kim int dph = (env->CP0_PWCtl >> CP0PC_DPH) & 0x1; 638074cfcb4SYongbok Kim int psn = (env->CP0_PWCtl >> CP0PC_PSN) & 0x3F; 639074cfcb4SYongbok Kim int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; 640074cfcb4SYongbok Kim int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; 641074cfcb4SYongbok Kim uint64_t entry; 642074cfcb4SYongbok Kim uint64_t paddr; 643074cfcb4SYongbok Kim int prot; 644074cfcb4SYongbok Kim uint64_t lsb = 0; 645074cfcb4SYongbok Kim uint64_t w = 0; 646074cfcb4SYongbok Kim 647074cfcb4SYongbok Kim if (get_physical_address(env, &paddr, &prot, *vaddr, MMU_DATA_LOAD, 6484e999bf4SRichard Henderson ptw_mmu_idx) != TLBRET_MATCH) { 649074cfcb4SYongbok Kim /* wrong base address */ 650074cfcb4SYongbok Kim return 0; 651074cfcb4SYongbok Kim } 6527ce9760dSPhilippe Mathieu-Daudé if (!get_pte(env, *vaddr, directory_mop, &entry, ptw_mmu_idx)) { 653074cfcb4SYongbok Kim return 0; 654074cfcb4SYongbok Kim } 655074cfcb4SYongbok Kim 656074cfcb4SYongbok Kim if ((entry & (1 << psn)) && hugepg) { 657074cfcb4SYongbok Kim *huge_page = true; 658074cfcb4SYongbok Kim *hgpg_directory_hit = true; 659453ba4f6SPhilippe Mathieu-Daudé entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); 660074cfcb4SYongbok Kim w = directory_index - 1; 661074cfcb4SYongbok Kim if (directory_index & 0x1) { 662074cfcb4SYongbok Kim /* Generate adjacent page from same PTE for odd TLB page */ 6632838b1d6SPhilippe Mathieu-Daudé lsb = BIT_ULL(w) >> 6; 664074cfcb4SYongbok Kim *pw_entrylo0 = entry & ~lsb; /* even page */ 665074cfcb4SYongbok Kim *pw_entrylo1 = entry | lsb; /* odd page */ 666074cfcb4SYongbok Kim } else if (dph) { 667453ba4f6SPhilippe Mathieu-Daudé int oddpagebit = 1 << leaf_mop; 668074cfcb4SYongbok Kim uint64_t vaddr2 = *vaddr ^ oddpagebit; 669074cfcb4SYongbok Kim if (*vaddr & oddpagebit) { 670074cfcb4SYongbok Kim *pw_entrylo1 = entry; 671074cfcb4SYongbok Kim } else { 672074cfcb4SYongbok Kim *pw_entrylo0 = entry; 673074cfcb4SYongbok Kim } 674074cfcb4SYongbok Kim if (get_physical_address(env, &paddr, &prot, vaddr2, MMU_DATA_LOAD, 6754e999bf4SRichard Henderson ptw_mmu_idx) != TLBRET_MATCH) { 676074cfcb4SYongbok Kim return 0; 677074cfcb4SYongbok Kim } 6787ce9760dSPhilippe Mathieu-Daudé if (!get_pte(env, vaddr2, leaf_mop, &entry, ptw_mmu_idx)) { 679074cfcb4SYongbok Kim return 0; 680074cfcb4SYongbok Kim } 681453ba4f6SPhilippe Mathieu-Daudé entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); 682074cfcb4SYongbok Kim if (*vaddr & oddpagebit) { 683074cfcb4SYongbok Kim *pw_entrylo0 = entry; 684074cfcb4SYongbok Kim } else { 685074cfcb4SYongbok Kim *pw_entrylo1 = entry; 686074cfcb4SYongbok Kim } 687074cfcb4SYongbok Kim } else { 688074cfcb4SYongbok Kim return 0; 689074cfcb4SYongbok Kim } 690074cfcb4SYongbok Kim return 1; 691074cfcb4SYongbok Kim } else { 692074cfcb4SYongbok Kim *vaddr = entry; 693074cfcb4SYongbok Kim return 2; 694074cfcb4SYongbok Kim } 695074cfcb4SYongbok Kim } 696074cfcb4SYongbok Kim 697bca3763bSPhilippe Mathieu-Daudé static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, 6984e999bf4SRichard Henderson int ptw_mmu_idx) 699074cfcb4SYongbok Kim { 700074cfcb4SYongbok Kim int gdw = (env->CP0_PWSize >> CP0PS_GDW) & 0x3F; 701074cfcb4SYongbok Kim int udw = (env->CP0_PWSize >> CP0PS_UDW) & 0x3F; 702074cfcb4SYongbok Kim int mdw = (env->CP0_PWSize >> CP0PS_MDW) & 0x3F; 703074cfcb4SYongbok Kim int ptw = (env->CP0_PWSize >> CP0PS_PTW) & 0x3F; 704074cfcb4SYongbok Kim int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; 705074cfcb4SYongbok Kim 706074cfcb4SYongbok Kim /* Initial values */ 707074cfcb4SYongbok Kim bool huge_page = false; 708074cfcb4SYongbok Kim bool hgpg_bdhit = false; 709074cfcb4SYongbok Kim bool hgpg_gdhit = false; 710074cfcb4SYongbok Kim bool hgpg_udhit = false; 711074cfcb4SYongbok Kim bool hgpg_mdhit = false; 712074cfcb4SYongbok Kim 713074cfcb4SYongbok Kim int32_t pw_pagemask = 0; 714074cfcb4SYongbok Kim target_ulong pw_entryhi = 0; 715074cfcb4SYongbok Kim uint64_t pw_entrylo0 = 0; 716074cfcb4SYongbok Kim uint64_t pw_entrylo1 = 0; 717074cfcb4SYongbok Kim 718074cfcb4SYongbok Kim /* Native pointer size */ 719074cfcb4SYongbok Kim /*For the 32-bit architectures, this bit is fixed to 0.*/ 720453ba4f6SPhilippe Mathieu-Daudé MemOp native_op = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? MO_32 : MO_64; 721074cfcb4SYongbok Kim 722074cfcb4SYongbok Kim /* Indices from PWField */ 723074cfcb4SYongbok Kim int pf_gdw = (env->CP0_PWField >> CP0PF_GDW) & 0x3F; 724074cfcb4SYongbok Kim int pf_udw = (env->CP0_PWField >> CP0PF_UDW) & 0x3F; 725074cfcb4SYongbok Kim int pf_mdw = (env->CP0_PWField >> CP0PF_MDW) & 0x3F; 726074cfcb4SYongbok Kim int pf_ptw = (env->CP0_PWField >> CP0PF_PTW) & 0x3F; 727074cfcb4SYongbok Kim int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; 728074cfcb4SYongbok Kim 729074cfcb4SYongbok Kim /* Indices computed from faulting address */ 730074cfcb4SYongbok Kim int gindex = (address >> pf_gdw) & ((1 << gdw) - 1); 731074cfcb4SYongbok Kim int uindex = (address >> pf_udw) & ((1 << udw) - 1); 732074cfcb4SYongbok Kim int mindex = (address >> pf_mdw) & ((1 << mdw) - 1); 733074cfcb4SYongbok Kim int ptindex = (address >> pf_ptw) & ((1 << ptw) - 1); 734074cfcb4SYongbok Kim 735074cfcb4SYongbok Kim /* Other HTW configs */ 736074cfcb4SYongbok Kim int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; 737453ba4f6SPhilippe Mathieu-Daudé MemOp directory_mop, leaf_mop; 738074cfcb4SYongbok Kim 739074cfcb4SYongbok Kim /* Offsets into tables */ 7400fe4cac5SPeter Maydell unsigned goffset, uoffset, moffset, ptoffset0, ptoffset1; 741074cfcb4SYongbok Kim 742074cfcb4SYongbok Kim /* Starting address - Page Table Base */ 743074cfcb4SYongbok Kim uint64_t vaddr = env->CP0_PWBase; 744074cfcb4SYongbok Kim 745074cfcb4SYongbok Kim uint64_t dir_entry; 746074cfcb4SYongbok Kim uint64_t paddr; 747074cfcb4SYongbok Kim int prot; 748074cfcb4SYongbok Kim int m; 749074cfcb4SYongbok Kim 750074cfcb4SYongbok Kim if (!(env->CP0_Config3 & (1 << CP0C3_PW))) { 751074cfcb4SYongbok Kim /* walker is unimplemented */ 752074cfcb4SYongbok Kim return false; 753074cfcb4SYongbok Kim } 754074cfcb4SYongbok Kim if (!(env->CP0_PWCtl & (1 << CP0PC_PWEN))) { 755074cfcb4SYongbok Kim /* walker is disabled */ 756074cfcb4SYongbok Kim return false; 757074cfcb4SYongbok Kim } 758074cfcb4SYongbok Kim if (!(gdw > 0 || udw > 0 || mdw > 0)) { 759074cfcb4SYongbok Kim /* no structure to walk */ 760074cfcb4SYongbok Kim return false; 761074cfcb4SYongbok Kim } 7620fe4cac5SPeter Maydell if (ptew > 1) { 763074cfcb4SYongbok Kim return false; 764074cfcb4SYongbok Kim } 765074cfcb4SYongbok Kim 7660fe4cac5SPeter Maydell /* HTW Shift values (depend on entry size) */ 767453ba4f6SPhilippe Mathieu-Daudé directory_mop = (hugepg && (ptew == 1)) ? native_op + 1 : native_op; 768453ba4f6SPhilippe Mathieu-Daudé leaf_mop = (ptew == 1) ? native_op + 1 : native_op; 7690fe4cac5SPeter Maydell 770453ba4f6SPhilippe Mathieu-Daudé goffset = gindex << directory_mop; 771453ba4f6SPhilippe Mathieu-Daudé uoffset = uindex << directory_mop; 772453ba4f6SPhilippe Mathieu-Daudé moffset = mindex << directory_mop; 773453ba4f6SPhilippe Mathieu-Daudé ptoffset0 = (ptindex >> 1) << (leaf_mop + 1); 774453ba4f6SPhilippe Mathieu-Daudé ptoffset1 = ptoffset0 | (1 << (leaf_mop)); 7750fe4cac5SPeter Maydell 776074cfcb4SYongbok Kim /* Global Directory */ 777074cfcb4SYongbok Kim if (gdw > 0) { 778074cfcb4SYongbok Kim vaddr |= goffset; 779074cfcb4SYongbok Kim switch (walk_directory(env, &vaddr, pf_gdw, &huge_page, &hgpg_gdhit, 78060a38a3aSPhilippe Mathieu-Daudé &pw_entrylo0, &pw_entrylo1, 781453ba4f6SPhilippe Mathieu-Daudé directory_mop, leaf_mop, ptw_mmu_idx)) 782074cfcb4SYongbok Kim { 783074cfcb4SYongbok Kim case 0: 784074cfcb4SYongbok Kim return false; 785074cfcb4SYongbok Kim case 1: 786074cfcb4SYongbok Kim goto refill; 787074cfcb4SYongbok Kim case 2: 788074cfcb4SYongbok Kim default: 789074cfcb4SYongbok Kim break; 790074cfcb4SYongbok Kim } 791074cfcb4SYongbok Kim } 792074cfcb4SYongbok Kim 793074cfcb4SYongbok Kim /* Upper directory */ 794074cfcb4SYongbok Kim if (udw > 0) { 795074cfcb4SYongbok Kim vaddr |= uoffset; 796074cfcb4SYongbok Kim switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhit, 79760a38a3aSPhilippe Mathieu-Daudé &pw_entrylo0, &pw_entrylo1, 798453ba4f6SPhilippe Mathieu-Daudé directory_mop, leaf_mop, ptw_mmu_idx)) 799074cfcb4SYongbok Kim { 800074cfcb4SYongbok Kim case 0: 801074cfcb4SYongbok Kim return false; 802074cfcb4SYongbok Kim case 1: 803074cfcb4SYongbok Kim goto refill; 804074cfcb4SYongbok Kim case 2: 805074cfcb4SYongbok Kim default: 806074cfcb4SYongbok Kim break; 807074cfcb4SYongbok Kim } 808074cfcb4SYongbok Kim } 809074cfcb4SYongbok Kim 810074cfcb4SYongbok Kim /* Middle directory */ 811074cfcb4SYongbok Kim if (mdw > 0) { 812074cfcb4SYongbok Kim vaddr |= moffset; 813074cfcb4SYongbok Kim switch (walk_directory(env, &vaddr, pf_mdw, &huge_page, &hgpg_mdhit, 81460a38a3aSPhilippe Mathieu-Daudé &pw_entrylo0, &pw_entrylo1, 815453ba4f6SPhilippe Mathieu-Daudé directory_mop, leaf_mop, ptw_mmu_idx)) 816074cfcb4SYongbok Kim { 817074cfcb4SYongbok Kim case 0: 818074cfcb4SYongbok Kim return false; 819074cfcb4SYongbok Kim case 1: 820074cfcb4SYongbok Kim goto refill; 821074cfcb4SYongbok Kim case 2: 822074cfcb4SYongbok Kim default: 823074cfcb4SYongbok Kim break; 824074cfcb4SYongbok Kim } 825074cfcb4SYongbok Kim } 826074cfcb4SYongbok Kim 827074cfcb4SYongbok Kim /* Leaf Level Page Table - First half of PTE pair */ 828074cfcb4SYongbok Kim vaddr |= ptoffset0; 829074cfcb4SYongbok Kim if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, 8304e999bf4SRichard Henderson ptw_mmu_idx) != TLBRET_MATCH) { 831074cfcb4SYongbok Kim return false; 832074cfcb4SYongbok Kim } 8337ce9760dSPhilippe Mathieu-Daudé if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { 834074cfcb4SYongbok Kim return false; 835074cfcb4SYongbok Kim } 836453ba4f6SPhilippe Mathieu-Daudé dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); 837074cfcb4SYongbok Kim pw_entrylo0 = dir_entry; 838074cfcb4SYongbok Kim 839074cfcb4SYongbok Kim /* Leaf Level Page Table - Second half of PTE pair */ 840074cfcb4SYongbok Kim vaddr |= ptoffset1; 841074cfcb4SYongbok Kim if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, 8424e999bf4SRichard Henderson ptw_mmu_idx) != TLBRET_MATCH) { 843074cfcb4SYongbok Kim return false; 844074cfcb4SYongbok Kim } 8457ce9760dSPhilippe Mathieu-Daudé if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { 846074cfcb4SYongbok Kim return false; 847074cfcb4SYongbok Kim } 848453ba4f6SPhilippe Mathieu-Daudé dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); 849074cfcb4SYongbok Kim pw_entrylo1 = dir_entry; 850074cfcb4SYongbok Kim 851074cfcb4SYongbok Kim refill: 852074cfcb4SYongbok Kim 853074cfcb4SYongbok Kim m = (1 << pf_ptw) - 1; 854074cfcb4SYongbok Kim 855074cfcb4SYongbok Kim if (huge_page) { 856074cfcb4SYongbok Kim switch (hgpg_bdhit << 3 | hgpg_gdhit << 2 | hgpg_udhit << 1 | 857074cfcb4SYongbok Kim hgpg_mdhit) 858074cfcb4SYongbok Kim { 859074cfcb4SYongbok Kim case 4: 860074cfcb4SYongbok Kim m = (1 << pf_gdw) - 1; 861074cfcb4SYongbok Kim if (pf_gdw & 1) { 862074cfcb4SYongbok Kim m >>= 1; 863074cfcb4SYongbok Kim } 864074cfcb4SYongbok Kim break; 865074cfcb4SYongbok Kim case 2: 866074cfcb4SYongbok Kim m = (1 << pf_udw) - 1; 867074cfcb4SYongbok Kim if (pf_udw & 1) { 868074cfcb4SYongbok Kim m >>= 1; 869074cfcb4SYongbok Kim } 870074cfcb4SYongbok Kim break; 871074cfcb4SYongbok Kim case 1: 872074cfcb4SYongbok Kim m = (1 << pf_mdw) - 1; 873074cfcb4SYongbok Kim if (pf_mdw & 1) { 874074cfcb4SYongbok Kim m >>= 1; 875074cfcb4SYongbok Kim } 876074cfcb4SYongbok Kim break; 877074cfcb4SYongbok Kim } 878074cfcb4SYongbok Kim } 879fca2817fSRichard Henderson pw_pagemask = m >> TARGET_PAGE_BITS; 880256ba771SRichard Henderson pw_pagemask = compute_pagemask(pw_pagemask << CP0PM_MASK); 881074cfcb4SYongbok Kim pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); 882074cfcb4SYongbok Kim { 883074cfcb4SYongbok Kim target_ulong tmp_entryhi = env->CP0_EntryHi; 884074cfcb4SYongbok Kim int32_t tmp_pagemask = env->CP0_PageMask; 885074cfcb4SYongbok Kim uint64_t tmp_entrylo0 = env->CP0_EntryLo0; 886074cfcb4SYongbok Kim uint64_t tmp_entrylo1 = env->CP0_EntryLo1; 887074cfcb4SYongbok Kim 888074cfcb4SYongbok Kim env->CP0_EntryHi = pw_entryhi; 889074cfcb4SYongbok Kim env->CP0_PageMask = pw_pagemask; 890074cfcb4SYongbok Kim env->CP0_EntryLo0 = pw_entrylo0; 891074cfcb4SYongbok Kim env->CP0_EntryLo1 = pw_entrylo1; 892074cfcb4SYongbok Kim 893074cfcb4SYongbok Kim /* 894074cfcb4SYongbok Kim * The hardware page walker inserts a page into the TLB in a manner 895074cfcb4SYongbok Kim * identical to a TLBWR instruction as executed by the software refill 896074cfcb4SYongbok Kim * handler. 897074cfcb4SYongbok Kim */ 898074cfcb4SYongbok Kim r4k_helper_tlbwr(env); 899074cfcb4SYongbok Kim 900074cfcb4SYongbok Kim env->CP0_EntryHi = tmp_entryhi; 901074cfcb4SYongbok Kim env->CP0_PageMask = tmp_pagemask; 902074cfcb4SYongbok Kim env->CP0_EntryLo0 = tmp_entrylo0; 903074cfcb4SYongbok Kim env->CP0_EntryLo1 = tmp_entrylo1; 904074cfcb4SYongbok Kim } 905074cfcb4SYongbok Kim return true; 906074cfcb4SYongbok Kim } 907074cfcb4SYongbok Kim #endif 908074cfcb4SYongbok Kim 909931d019fSRichard Henderson bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 910931d019fSRichard Henderson MMUAccessType access_type, int mmu_idx, 911931d019fSRichard Henderson bool probe, uintptr_t retaddr) 9121147e189SAurelien Jarno { 9134c44a980SPhilippe Mathieu-Daudé CPUMIPSState *env = cpu_env(cs); 914a8170e5eSAvi Kivity hwaddr physical; 9151147e189SAurelien Jarno int prot; 916995ffde9SRichard Henderson int ret = TLBRET_BADADDR; 9171147e189SAurelien Jarno 9181147e189SAurelien Jarno /* data access */ 919074cfcb4SYongbok Kim /* XXX: put correct access by using cpu_restore_state() correctly */ 920931d019fSRichard Henderson ret = get_physical_address(env, &physical, &prot, address, 921935c1034SPhilippe Mathieu-Daudé access_type, mmu_idx); 922def74c0cSPhilippe Mathieu-Daudé switch (ret) { 923def74c0cSPhilippe Mathieu-Daudé case TLBRET_MATCH: 924339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, 925883f2c59SPhilippe Mathieu-Daudé "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx 926def74c0cSPhilippe Mathieu-Daudé " prot %d\n", __func__, address, physical, prot); 927def74c0cSPhilippe Mathieu-Daudé break; 928def74c0cSPhilippe Mathieu-Daudé default: 929def74c0cSPhilippe Mathieu-Daudé qemu_log_mask(CPU_LOG_MMU, 930def74c0cSPhilippe Mathieu-Daudé "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, 931def74c0cSPhilippe Mathieu-Daudé ret); 932def74c0cSPhilippe Mathieu-Daudé break; 933def74c0cSPhilippe Mathieu-Daudé } 9341147e189SAurelien Jarno if (ret == TLBRET_MATCH) { 9350c591eb0SAndreas Färber tlb_set_page(cs, address & TARGET_PAGE_MASK, 9367353113fSJakub Jermář physical & TARGET_PAGE_MASK, prot, 937d4c430a8SPaul Brook mmu_idx, TARGET_PAGE_SIZE); 938931d019fSRichard Henderson return true; 939e38f4eb6SRichard Henderson } 940074cfcb4SYongbok Kim #if !defined(TARGET_MIPS64) 941074cfcb4SYongbok Kim if ((ret == TLBRET_NOMATCH) && (env->tlb->nb_tlb > 1)) { 942074cfcb4SYongbok Kim /* 943074cfcb4SYongbok Kim * Memory reads during hardware page table walking are performed 944074cfcb4SYongbok Kim * as if they were kernel-mode load instructions. 945074cfcb4SYongbok Kim */ 9464e999bf4SRichard Henderson int ptw_mmu_idx = (env->hflags & MIPS_HFLAG_ERL ? 9474e999bf4SRichard Henderson MMU_ERL_IDX : MMU_KERNEL_IDX); 9484e999bf4SRichard Henderson 9494e999bf4SRichard Henderson if (page_table_walk_refill(env, address, ptw_mmu_idx)) { 950931d019fSRichard Henderson ret = get_physical_address(env, &physical, &prot, address, 951935c1034SPhilippe Mathieu-Daudé access_type, mmu_idx); 952074cfcb4SYongbok Kim if (ret == TLBRET_MATCH) { 953074cfcb4SYongbok Kim tlb_set_page(cs, address & TARGET_PAGE_MASK, 9547353113fSJakub Jermář physical & TARGET_PAGE_MASK, prot, 955074cfcb4SYongbok Kim mmu_idx, TARGET_PAGE_SIZE); 956931d019fSRichard Henderson return true; 957074cfcb4SYongbok Kim } 958074cfcb4SYongbok Kim } 959074cfcb4SYongbok Kim } 960074cfcb4SYongbok Kim #endif 961931d019fSRichard Henderson if (probe) { 962931d019fSRichard Henderson return false; 963931d019fSRichard Henderson } 9646af0bf9cSbellard 965931d019fSRichard Henderson raise_mmu_exception(env, address, access_type, ret); 966931d019fSRichard Henderson do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr); 9676af0bf9cSbellard } 9686af0bf9cSbellard 969d7551eceSAleksandar Markovic hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, 9700debf140SPhilippe Mathieu-Daudé MMUAccessType access_type, uintptr_t retaddr) 97125b91e32SAurelien Jarno { 972a8170e5eSAvi Kivity hwaddr physical; 97325b91e32SAurelien Jarno int prot; 97425b91e32SAurelien Jarno int ret = 0; 9750debf140SPhilippe Mathieu-Daudé CPUState *cs = env_cpu(env); 97625b91e32SAurelien Jarno 97725b91e32SAurelien Jarno /* data access */ 97848b28c6aSPhilippe Mathieu-Daudé ret = get_physical_address(env, &physical, &prot, address, access_type, 9796ebf33c5SRichard Henderson mips_env_mmu_index(env)); 9800debf140SPhilippe Mathieu-Daudé if (ret == TLBRET_MATCH) { 98125b91e32SAurelien Jarno return physical; 98225b91e32SAurelien Jarno } 9830debf140SPhilippe Mathieu-Daudé 9840debf140SPhilippe Mathieu-Daudé raise_mmu_exception(env, address, access_type, ret); 9850debf140SPhilippe Mathieu-Daudé cpu_loop_exit_restore(cs, retaddr); 986c36bbb28SAurelien Jarno } 98725b91e32SAurelien Jarno 9887db13faeSAndreas Färber static void set_hflags_for_handler(CPUMIPSState *env) 989bbfa8f72SNathan Froyd { 990bbfa8f72SNathan Froyd /* Exception handlers are entered in 32-bit mode. */ 991bbfa8f72SNathan Froyd env->hflags &= ~(MIPS_HFLAG_M16); 992bbfa8f72SNathan Froyd /* ...except that microMIPS lets you choose. */ 993bbfa8f72SNathan Froyd if (env->insn_flags & ASE_MICROMIPS) { 994d7551eceSAleksandar Markovic env->hflags |= (!!(env->CP0_Config3 & 995d7551eceSAleksandar Markovic (1 << CP0C3_ISA_ON_EXC)) 996bbfa8f72SNathan Froyd << MIPS_HFLAG_M16_SHIFT); 997bbfa8f72SNathan Froyd } 998bbfa8f72SNathan Froyd } 999aea14095SLeon Alrae 1000aea14095SLeon Alrae static inline void set_badinstr_registers(CPUMIPSState *env) 1001aea14095SLeon Alrae { 10027a5f784aSStefan Markovic if (env->insn_flags & ISA_NANOMIPS32) { 10037a5f784aSStefan Markovic if (env->CP0_Config3 & (1 << CP0C3_BI)) { 10047a5f784aSStefan Markovic uint32_t instr = (cpu_lduw_code(env, env->active_tc.PC)) << 16; 10057a5f784aSStefan Markovic if ((instr & 0x10000000) == 0) { 10067a5f784aSStefan Markovic instr |= cpu_lduw_code(env, env->active_tc.PC + 2); 10077a5f784aSStefan Markovic } 10087a5f784aSStefan Markovic env->CP0_BadInstr = instr; 10097a5f784aSStefan Markovic 10107a5f784aSStefan Markovic if ((instr & 0xFC000000) == 0x60000000) { 10117a5f784aSStefan Markovic instr = cpu_lduw_code(env, env->active_tc.PC + 4) << 16; 10127a5f784aSStefan Markovic env->CP0_BadInstrX = instr; 10137a5f784aSStefan Markovic } 10147a5f784aSStefan Markovic } 10157a5f784aSStefan Markovic return; 10167a5f784aSStefan Markovic } 10177a5f784aSStefan Markovic 1018aea14095SLeon Alrae if (env->hflags & MIPS_HFLAG_M16) { 1019aea14095SLeon Alrae /* TODO: add BadInstr support for microMIPS */ 1020aea14095SLeon Alrae return; 1021aea14095SLeon Alrae } 1022aea14095SLeon Alrae if (env->CP0_Config3 & (1 << CP0C3_BI)) { 1023aea14095SLeon Alrae env->CP0_BadInstr = cpu_ldl_code(env, env->active_tc.PC); 1024aea14095SLeon Alrae } 1025aea14095SLeon Alrae if ((env->CP0_Config3 & (1 << CP0C3_BP)) && 1026aea14095SLeon Alrae (env->hflags & MIPS_HFLAG_BMASK)) { 1027aea14095SLeon Alrae env->CP0_BadInstrP = cpu_ldl_code(env, env->active_tc.PC - 4); 1028aea14095SLeon Alrae } 1029aea14095SLeon Alrae } 1030f9bd3d79SPhilippe Mathieu-Daudé 103197a8ea5aSAndreas Färber void mips_cpu_do_interrupt(CPUState *cs) 1032ca7c2b1bSths { 103397a8ea5aSAndreas Färber MIPSCPU *cpu = MIPS_CPU(cs); 103497a8ea5aSAndreas Färber CPUMIPSState *env = &cpu->env; 1035aea14095SLeon Alrae bool update_badinstr = 0; 1036aa328addSths target_ulong offset; 10376af0bf9cSbellard int cause = -1; 10386af0bf9cSbellard 1039c8557016SRichard Henderson if (qemu_loglevel_mask(CPU_LOG_INT) 1040c8557016SRichard Henderson && cs->exception_index != EXCP_EXT_INTERRUPT) { 1041c8557016SRichard Henderson qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx 1042c8557016SRichard Henderson " %s exception\n", 104390c429eeSPhilippe Mathieu-Daudé __func__, env->active_tc.PC, env->CP0_EPC, 104490c429eeSPhilippe Mathieu-Daudé mips_exception_name(cs->exception_index)); 10456af0bf9cSbellard } 104627103424SAndreas Färber if (cs->exception_index == EXCP_EXT_INTERRUPT && 104727103424SAndreas Färber (env->hflags & MIPS_HFLAG_DM)) { 104827103424SAndreas Färber cs->exception_index = EXCP_DINT; 104927103424SAndreas Färber } 10506af0bf9cSbellard offset = 0x180; 105127103424SAndreas Färber switch (cs->exception_index) { 10528ec7e3c5SRichard Henderson case EXCP_SEMIHOST: 10538ec7e3c5SRichard Henderson cs->exception_index = EXCP_NONE; 10548ec7e3c5SRichard Henderson mips_semihosting(env); 1055d44971e7SRichard Henderson env->active_tc.PC += env->error_code; 10568ec7e3c5SRichard Henderson return; 10576af0bf9cSbellard case EXCP_DSS: 10586af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DSS; 1059d7551eceSAleksandar Markovic /* 1060d7551eceSAleksandar Markovic * Debug single step cannot be raised inside a delay slot and 1061d7551eceSAleksandar Markovic * resume will always occur on the next instruction 1062d7551eceSAleksandar Markovic * (but we assume the pc has always been updated during 1063d7551eceSAleksandar Markovic * code translation). 1064d7551eceSAleksandar Markovic */ 106532188a03SNathan Froyd env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16); 10666af0bf9cSbellard goto enter_debug_mode; 10676af0bf9cSbellard case EXCP_DINT: 10686af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DINT; 10696af0bf9cSbellard goto set_DEPC; 10706af0bf9cSbellard case EXCP_DIB: 10716af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DIB; 10726af0bf9cSbellard goto set_DEPC; 10736af0bf9cSbellard case EXCP_DBp: 10746af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DBp; 1075c6c2c0fcSPavel Dovgalyuk /* Setup DExcCode - SDBBP instruction */ 1076d7551eceSAleksandar Markovic env->CP0_Debug = (env->CP0_Debug & ~(0x1fULL << CP0DB_DEC)) | 1077d7551eceSAleksandar Markovic (9 << CP0DB_DEC); 10786af0bf9cSbellard goto set_DEPC; 10796af0bf9cSbellard case EXCP_DDBS: 10806af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DDBS; 10816af0bf9cSbellard goto set_DEPC; 10826af0bf9cSbellard case EXCP_DDBL: 10836af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DDBL; 10846af0bf9cSbellard set_DEPC: 108532188a03SNathan Froyd env->CP0_DEPC = exception_resume_pc(env); 10864ad40f36Sbellard env->hflags &= ~MIPS_HFLAG_BMASK; 10876af0bf9cSbellard enter_debug_mode: 1088d9224450SMaciej W. Rozycki if (env->insn_flags & ISA_MIPS3) { 1089d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_64; 10902e211e0aSPhilippe Mathieu-Daudé if (!(env->insn_flags & ISA_MIPS_R6) || 10917871abb9SJames Hogan env->CP0_Status & (1 << CP0St_KX)) { 10927871abb9SJames Hogan env->hflags &= ~MIPS_HFLAG_AWRAP; 10937871abb9SJames Hogan } 1094d9224450SMaciej W. Rozycki } 1095d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_CP0; 1096623a930eSths env->hflags &= ~(MIPS_HFLAG_KSU); 10976af0bf9cSbellard /* EJTAG probe trap enable is not implemented... */ 1098d7551eceSAleksandar Markovic if (!(env->CP0_Status & (1 << CP0St_EXL))) { 1099f45cb2f4SPeter Maydell env->CP0_Cause &= ~(1U << CP0Ca_BD); 1100d7551eceSAleksandar Markovic } 110189777fd1SLeon Alrae env->active_tc.PC = env->exception_base + 0x480; 1102bbfa8f72SNathan Froyd set_hflags_for_handler(env); 11036af0bf9cSbellard break; 11046af0bf9cSbellard case EXCP_RESET: 1105fca1be7cSAndreas Färber cpu_reset(CPU(cpu)); 1106aa328addSths break; 11076af0bf9cSbellard case EXCP_SRESET: 110824c7b0e3Sths env->CP0_Status |= (1 << CP0St_SR); 11099d989c73SAurelien Jarno memset(env->CP0_WatchLo, 0, sizeof(env->CP0_WatchLo)); 11106af0bf9cSbellard goto set_error_EPC; 11116af0bf9cSbellard case EXCP_NMI: 111224c7b0e3Sths env->CP0_Status |= (1 << CP0St_NMI); 11136af0bf9cSbellard set_error_EPC: 111432188a03SNathan Froyd env->CP0_ErrorEPC = exception_resume_pc(env); 1115ecd78a0aSpbrook env->hflags &= ~MIPS_HFLAG_BMASK; 1116aa328addSths env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); 1117d9224450SMaciej W. Rozycki if (env->insn_flags & ISA_MIPS3) { 1118d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_64; 11192e211e0aSPhilippe Mathieu-Daudé if (!(env->insn_flags & ISA_MIPS_R6) || 11207871abb9SJames Hogan env->CP0_Status & (1 << CP0St_KX)) { 11217871abb9SJames Hogan env->hflags &= ~MIPS_HFLAG_AWRAP; 11227871abb9SJames Hogan } 1123d9224450SMaciej W. Rozycki } 1124d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_CP0; 1125623a930eSths env->hflags &= ~(MIPS_HFLAG_KSU); 1126d7551eceSAleksandar Markovic if (!(env->CP0_Status & (1 << CP0St_EXL))) { 1127f45cb2f4SPeter Maydell env->CP0_Cause &= ~(1U << CP0Ca_BD); 1128d7551eceSAleksandar Markovic } 112989777fd1SLeon Alrae env->active_tc.PC = env->exception_base; 1130bbfa8f72SNathan Froyd set_hflags_for_handler(env); 11316af0bf9cSbellard break; 11326af0bf9cSbellard case EXCP_EXT_INTERRUPT: 11336af0bf9cSbellard cause = 0; 1134da52a4dfSYongbok Kim if (env->CP0_Cause & (1 << CP0Ca_IV)) { 1135da52a4dfSYongbok Kim uint32_t spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & 0x1f; 1136da52a4dfSYongbok Kim 1137da52a4dfSYongbok Kim if ((env->CP0_Status & (1 << CP0St_BEV)) || spacing == 0) { 11386af0bf9cSbellard offset = 0x200; 1139138afb02SEdgar E. Iglesias } else { 1140da52a4dfSYongbok Kim uint32_t vector = 0; 1141da52a4dfSYongbok Kim uint32_t pending = (env->CP0_Cause & CP0Ca_IP_mask) >> CP0Ca_IP; 1142da52a4dfSYongbok Kim 1143da52a4dfSYongbok Kim if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { 1144d7551eceSAleksandar Markovic /* 1145d7551eceSAleksandar Markovic * For VEIC mode, the external interrupt controller feeds 1146d7551eceSAleksandar Markovic * the vector through the CP0Cause IP lines. 1147d7551eceSAleksandar Markovic */ 1148138afb02SEdgar E. Iglesias vector = pending; 1149da52a4dfSYongbok Kim } else { 1150d7551eceSAleksandar Markovic /* 1151d7551eceSAleksandar Markovic * Vectored Interrupts 1152d7551eceSAleksandar Markovic * Mask with Status.IM7-IM0 to get enabled interrupts. 1153d7551eceSAleksandar Markovic */ 1154da52a4dfSYongbok Kim pending &= (env->CP0_Status >> CP0St_IM) & 0xff; 1155da52a4dfSYongbok Kim /* Find the highest-priority interrupt. */ 1156da52a4dfSYongbok Kim while (pending >>= 1) { 1157da52a4dfSYongbok Kim vector++; 1158138afb02SEdgar E. Iglesias } 1159da52a4dfSYongbok Kim } 1160da52a4dfSYongbok Kim offset = 0x200 + (vector * (spacing << 5)); 1161da52a4dfSYongbok Kim } 1162138afb02SEdgar E. Iglesias } 11636af0bf9cSbellard goto set_EPC; 1164b67bfe8dSths case EXCP_LTLBL: 1165b67bfe8dSths cause = 1; 1166aea14095SLeon Alrae update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); 1167beb811bdSths goto set_EPC; 11686af0bf9cSbellard case EXCP_TLBL: 11696af0bf9cSbellard cause = 2; 1170aea14095SLeon Alrae update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); 1171aea14095SLeon Alrae if ((env->error_code & EXCP_TLB_NOMATCH) && 1172aea14095SLeon Alrae !(env->CP0_Status & (1 << CP0St_EXL))) { 1173d26bc211Sths #if defined(TARGET_MIPS64) 1174100ce988Sths int R = env->CP0_BadVAddr >> 62; 1175100ce988Sths int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; 1176100ce988Sths int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; 1177100ce988Sths 1178480e79aeSJames Hogan if ((R != 0 || UX) && (R != 3 || KX) && 1179480e79aeSJames Hogan (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { 1180100ce988Sths offset = 0x080; 1181480e79aeSJames Hogan } else { 1182100ce988Sths #endif 11836af0bf9cSbellard offset = 0x000; 1184480e79aeSJames Hogan #if defined(TARGET_MIPS64) 1185480e79aeSJames Hogan } 1186480e79aeSJames Hogan #endif 1187100ce988Sths } 11886af0bf9cSbellard goto set_EPC; 1189b67bfe8dSths case EXCP_TLBS: 1190b67bfe8dSths cause = 3; 1191aea14095SLeon Alrae update_badinstr = 1; 1192aea14095SLeon Alrae if ((env->error_code & EXCP_TLB_NOMATCH) && 1193aea14095SLeon Alrae !(env->CP0_Status & (1 << CP0St_EXL))) { 1194b67bfe8dSths #if defined(TARGET_MIPS64) 1195b67bfe8dSths int R = env->CP0_BadVAddr >> 62; 1196b67bfe8dSths int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; 1197b67bfe8dSths int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; 1198b67bfe8dSths 1199480e79aeSJames Hogan if ((R != 0 || UX) && (R != 3 || KX) && 1200480e79aeSJames Hogan (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { 1201b67bfe8dSths offset = 0x080; 1202480e79aeSJames Hogan } else { 1203b67bfe8dSths #endif 1204b67bfe8dSths offset = 0x000; 1205480e79aeSJames Hogan #if defined(TARGET_MIPS64) 1206480e79aeSJames Hogan } 1207480e79aeSJames Hogan #endif 1208b67bfe8dSths } 1209b67bfe8dSths goto set_EPC; 1210b67bfe8dSths case EXCP_AdEL: 1211b67bfe8dSths cause = 4; 1212aea14095SLeon Alrae update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); 1213b67bfe8dSths goto set_EPC; 1214b67bfe8dSths case EXCP_AdES: 1215b67bfe8dSths cause = 5; 1216aea14095SLeon Alrae update_badinstr = 1; 1217b67bfe8dSths goto set_EPC; 12186af0bf9cSbellard case EXCP_IBE: 12196af0bf9cSbellard cause = 6; 12206af0bf9cSbellard goto set_EPC; 12216af0bf9cSbellard case EXCP_DBE: 12226af0bf9cSbellard cause = 7; 12236af0bf9cSbellard goto set_EPC; 12246af0bf9cSbellard case EXCP_SYSCALL: 12256af0bf9cSbellard cause = 8; 1226aea14095SLeon Alrae update_badinstr = 1; 12276af0bf9cSbellard goto set_EPC; 12286af0bf9cSbellard case EXCP_BREAK: 12296af0bf9cSbellard cause = 9; 1230aea14095SLeon Alrae update_badinstr = 1; 12316af0bf9cSbellard goto set_EPC; 12326af0bf9cSbellard case EXCP_RI: 12336af0bf9cSbellard cause = 10; 1234aea14095SLeon Alrae update_badinstr = 1; 12356af0bf9cSbellard goto set_EPC; 12366af0bf9cSbellard case EXCP_CpU: 12376af0bf9cSbellard cause = 11; 1238aea14095SLeon Alrae update_badinstr = 1; 123939d51eb8Sths env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | 124039d51eb8Sths (env->error_code << CP0Ca_CE); 12416af0bf9cSbellard goto set_EPC; 12426af0bf9cSbellard case EXCP_OVERFLOW: 12436af0bf9cSbellard cause = 12; 1244aea14095SLeon Alrae update_badinstr = 1; 12456af0bf9cSbellard goto set_EPC; 12466af0bf9cSbellard case EXCP_TRAP: 12476af0bf9cSbellard cause = 13; 1248aea14095SLeon Alrae update_badinstr = 1; 12496af0bf9cSbellard goto set_EPC; 1250b10ac204SYongbok Kim case EXCP_MSAFPE: 1251b10ac204SYongbok Kim cause = 14; 1252b10ac204SYongbok Kim update_badinstr = 1; 1253b10ac204SYongbok Kim goto set_EPC; 12545a5012ecSths case EXCP_FPE: 12555a5012ecSths cause = 15; 1256aea14095SLeon Alrae update_badinstr = 1; 12575a5012ecSths goto set_EPC; 1258b67bfe8dSths case EXCP_C2E: 1259b67bfe8dSths cause = 18; 12606af0bf9cSbellard goto set_EPC; 126192ceb440SLeon Alrae case EXCP_TLBRI: 126292ceb440SLeon Alrae cause = 19; 1263aea14095SLeon Alrae update_badinstr = 1; 126492ceb440SLeon Alrae goto set_EPC; 126592ceb440SLeon Alrae case EXCP_TLBXI: 126692ceb440SLeon Alrae cause = 20; 126792ceb440SLeon Alrae goto set_EPC; 1268b10ac204SYongbok Kim case EXCP_MSADIS: 1269b10ac204SYongbok Kim cause = 21; 1270b10ac204SYongbok Kim update_badinstr = 1; 1271b10ac204SYongbok Kim goto set_EPC; 1272b67bfe8dSths case EXCP_MDMX: 1273b67bfe8dSths cause = 22; 1274b67bfe8dSths goto set_EPC; 1275b67bfe8dSths case EXCP_DWATCH: 1276b67bfe8dSths cause = 23; 127767cc32ebSVeres Lajos /* XXX: TODO: manage deferred watch exceptions */ 1278b67bfe8dSths goto set_EPC; 1279b67bfe8dSths case EXCP_MCHECK: 1280b67bfe8dSths cause = 24; 12816276c767Sths goto set_EPC; 12826276c767Sths case EXCP_THREAD: 12836276c767Sths cause = 25; 1284b67bfe8dSths goto set_EPC; 1285853c3240SJia Liu case EXCP_DSPDIS: 1286853c3240SJia Liu cause = 26; 1287853c3240SJia Liu goto set_EPC; 1288b67bfe8dSths case EXCP_CACHE: 1289b67bfe8dSths cause = 30; 1290b67bfe8dSths offset = 0x100; 12916af0bf9cSbellard set_EPC: 129224c7b0e3Sths if (!(env->CP0_Status & (1 << CP0St_EXL))) { 129332188a03SNathan Froyd env->CP0_EPC = exception_resume_pc(env); 1294aea14095SLeon Alrae if (update_badinstr) { 1295aea14095SLeon Alrae set_badinstr_registers(env); 1296aea14095SLeon Alrae } 12974ad40f36Sbellard if (env->hflags & MIPS_HFLAG_BMASK) { 1298f45cb2f4SPeter Maydell env->CP0_Cause |= (1U << CP0Ca_BD); 12996af0bf9cSbellard } else { 1300f45cb2f4SPeter Maydell env->CP0_Cause &= ~(1U << CP0Ca_BD); 13016af0bf9cSbellard } 130224c7b0e3Sths env->CP0_Status |= (1 << CP0St_EXL); 1303d9224450SMaciej W. Rozycki if (env->insn_flags & ISA_MIPS3) { 1304d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_64; 13052e211e0aSPhilippe Mathieu-Daudé if (!(env->insn_flags & ISA_MIPS_R6) || 13067871abb9SJames Hogan env->CP0_Status & (1 << CP0St_KX)) { 13077871abb9SJames Hogan env->hflags &= ~MIPS_HFLAG_AWRAP; 13087871abb9SJames Hogan } 1309d9224450SMaciej W. Rozycki } 1310d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_CP0; 1311623a930eSths env->hflags &= ~(MIPS_HFLAG_KSU); 131224c7b0e3Sths } 1313c53f4a62Sths env->hflags &= ~MIPS_HFLAG_BMASK; 1314aa328addSths if (env->CP0_Status & (1 << CP0St_BEV)) { 131589777fd1SLeon Alrae env->active_tc.PC = env->exception_base + 0x200; 131674dbf824SJames Hogan } else if (cause == 30 && !(env->CP0_Config3 & (1 << CP0C3_SC) && 131774dbf824SJames Hogan env->CP0_Config5 & (1 << CP0C5_CV))) { 131874dbf824SJames Hogan /* Force KSeg1 for cache errors */ 131967433345SJames Hogan env->active_tc.PC = KSEG1_BASE | (env->CP0_EBase & 0x1FFFF000); 1320aa328addSths } else { 132174dbf824SJames Hogan env->active_tc.PC = env->CP0_EBase & ~0xfff; 1322aa328addSths } 132374dbf824SJames Hogan 1324b5dc7732Sths env->active_tc.PC += offset; 1325bbfa8f72SNathan Froyd set_hflags_for_handler(env); 1326d7551eceSAleksandar Markovic env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | 1327d7551eceSAleksandar Markovic (cause << CP0Ca_EC); 13286af0bf9cSbellard break; 13296af0bf9cSbellard default: 1330c8557016SRichard Henderson abort(); 13316af0bf9cSbellard } 1332c8557016SRichard Henderson if (qemu_loglevel_mask(CPU_LOG_INT) 1333c8557016SRichard Henderson && cs->exception_index != EXCP_EXT_INTERRUPT) { 133493fcfe39Saliguori qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" 13353594c774Sths " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", 1336b5dc7732Sths __func__, env->active_tc.PC, env->CP0_EPC, cause, 13376af0bf9cSbellard env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, 13386af0bf9cSbellard env->CP0_DEPC); 13396af0bf9cSbellard } 134027103424SAndreas Färber cs->exception_index = EXCP_NONE; 13416af0bf9cSbellard } 13422ee4aed8Sbellard 13436eb66e08SPhilippe Mathieu-Daudé bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 13446eb66e08SPhilippe Mathieu-Daudé { 13456eb66e08SPhilippe Mathieu-Daudé if (interrupt_request & CPU_INTERRUPT_HARD) { 13464c44a980SPhilippe Mathieu-Daudé CPUMIPSState *env = cpu_env(cs); 13476eb66e08SPhilippe Mathieu-Daudé 13486eb66e08SPhilippe Mathieu-Daudé if (cpu_mips_hw_interrupts_enabled(env) && 13496eb66e08SPhilippe Mathieu-Daudé cpu_mips_hw_interrupts_pending(env)) { 13506eb66e08SPhilippe Mathieu-Daudé /* Raise it */ 13516eb66e08SPhilippe Mathieu-Daudé cs->exception_index = EXCP_EXT_INTERRUPT; 13526eb66e08SPhilippe Mathieu-Daudé env->error_code = 0; 13536eb66e08SPhilippe Mathieu-Daudé mips_cpu_do_interrupt(cs); 13546eb66e08SPhilippe Mathieu-Daudé return true; 13556eb66e08SPhilippe Mathieu-Daudé } 13566eb66e08SPhilippe Mathieu-Daudé } 13576eb66e08SPhilippe Mathieu-Daudé return false; 13586eb66e08SPhilippe Mathieu-Daudé } 13596eb66e08SPhilippe Mathieu-Daudé 13607db13faeSAndreas Färber void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra) 13612ee4aed8Sbellard { 13625a7330b3SRichard Henderson CPUState *cs = env_cpu(env); 1363c227f099SAnthony Liguori r4k_tlb_t *tlb; 13643b1c8be4Sths target_ulong addr; 13653b1c8be4Sths target_ulong end; 13662d72e7b0SPaul Burton uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 136799029be1SYongbok Kim uint32_t MMID = env->CP0_MemoryMapID; 136899029be1SYongbok Kim bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 136999029be1SYongbok Kim uint32_t tlb_mmid; 13703b1c8be4Sths target_ulong mask; 13712ee4aed8Sbellard 137299029be1SYongbok Kim MMID = mi ? MMID : (uint32_t) ASID; 137399029be1SYongbok Kim 1374ead9360eSths tlb = &env->tlb->mmu.r4k.tlb[idx]; 1375d7551eceSAleksandar Markovic /* 137699029be1SYongbok Kim * The qemu TLB is flushed when the ASID/MMID changes, so no need to 1377d7551eceSAleksandar Markovic * flush these entries again. 1378d7551eceSAleksandar Markovic */ 137999029be1SYongbok Kim tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 138099029be1SYongbok Kim if (tlb->G == 0 && tlb_mmid != MMID) { 13812ee4aed8Sbellard return; 13822ee4aed8Sbellard } 13832ee4aed8Sbellard 1384ead9360eSths if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) { 1385d7551eceSAleksandar Markovic /* 1386d7551eceSAleksandar Markovic * For tlbwr, we can shadow the discarded entry into 1387d7551eceSAleksandar Markovic * a new (fake) TLB entry, as long as the guest can not 1388d7551eceSAleksandar Markovic * tell that it's there. 1389d7551eceSAleksandar Markovic */ 1390ead9360eSths env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb; 1391ead9360eSths env->tlb->tlb_in_use++; 13922ee4aed8Sbellard return; 13932ee4aed8Sbellard } 13942ee4aed8Sbellard 13953b1c8be4Sths /* 1k pages are not supported. */ 1396f2e9ebefSths mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 13973b1c8be4Sths if (tlb->V0) { 1398f2e9ebefSths addr = tlb->VPN & ~mask; 1399d26bc211Sths #if defined(TARGET_MIPS64) 1400e034e2c3Sths if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { 1401100ce988Sths addr |= 0x3FFFFF0000000000ULL; 1402100ce988Sths } 1403100ce988Sths #endif 14043b1c8be4Sths end = addr | (mask >> 1); 14053b1c8be4Sths while (addr < end) { 140631b030d4SAndreas Färber tlb_flush_page(cs, addr); 14073b1c8be4Sths addr += TARGET_PAGE_SIZE; 14083b1c8be4Sths } 14093b1c8be4Sths } 14103b1c8be4Sths if (tlb->V1) { 1411f2e9ebefSths addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1); 1412d26bc211Sths #if defined(TARGET_MIPS64) 1413e034e2c3Sths if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { 1414100ce988Sths addr |= 0x3FFFFF0000000000ULL; 1415100ce988Sths } 1416100ce988Sths #endif 14173b1c8be4Sths end = addr | mask; 141853715e48Sths while (addr - 1 < end) { 141931b030d4SAndreas Färber tlb_flush_page(cs, addr); 14203b1c8be4Sths addr += TARGET_PAGE_SIZE; 14213b1c8be4Sths } 14223b1c8be4Sths } 14232ee4aed8Sbellard } 1424