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" 24*eb9b25c6SPhilippe Mathieu-Daudé #include "exec/cputlb.h" 2563c91552SPaolo Bonzini #include "exec/exec-all.h" 2674781c08SPhilippe Mathieu-Daudé #include "exec/page-protection.h" 27aea14095SLeon Alrae #include "exec/cpu_ldst.h" 28508127e2SPaolo Bonzini #include "exec/log.h" 296575529bSPhilippe Mathieu-Daudé #include "exec/helper-proto.h" 306575529bSPhilippe Mathieu-Daudé 316575529bSPhilippe Mathieu-Daudé /* TLB management */ 326575529bSPhilippe Mathieu-Daudé static void r4k_mips_tlb_flush_extra(CPUMIPSState *env, int first) 336575529bSPhilippe Mathieu-Daudé { 346575529bSPhilippe Mathieu-Daudé /* Discard entries from env->tlb[first] onwards. */ 356575529bSPhilippe Mathieu-Daudé while (env->tlb->tlb_in_use > first) { 366575529bSPhilippe Mathieu-Daudé r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0); 376575529bSPhilippe Mathieu-Daudé } 386575529bSPhilippe Mathieu-Daudé } 396575529bSPhilippe Mathieu-Daudé 406575529bSPhilippe Mathieu-Daudé static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo) 416575529bSPhilippe Mathieu-Daudé { 426575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 436575529bSPhilippe Mathieu-Daudé return extract64(entrylo, 6, 54); 446575529bSPhilippe Mathieu-Daudé #else 456575529bSPhilippe Mathieu-Daudé return extract64(entrylo, 6, 24) | /* PFN */ 466575529bSPhilippe Mathieu-Daudé (extract64(entrylo, 32, 32) << 24); /* PFNX */ 476575529bSPhilippe Mathieu-Daudé #endif 486575529bSPhilippe Mathieu-Daudé } 496575529bSPhilippe Mathieu-Daudé 506575529bSPhilippe Mathieu-Daudé static void r4k_fill_tlb(CPUMIPSState *env, int idx) 516575529bSPhilippe Mathieu-Daudé { 526575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 536575529bSPhilippe Mathieu-Daudé uint64_t mask = env->CP0_PageMask >> (TARGET_PAGE_BITS + 1); 546575529bSPhilippe Mathieu-Daudé 556575529bSPhilippe Mathieu-Daudé /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */ 566575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 576575529bSPhilippe Mathieu-Daudé if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) { 586575529bSPhilippe Mathieu-Daudé tlb->EHINV = 1; 596575529bSPhilippe Mathieu-Daudé return; 606575529bSPhilippe Mathieu-Daudé } 616575529bSPhilippe Mathieu-Daudé tlb->EHINV = 0; 626575529bSPhilippe Mathieu-Daudé tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); 636575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 646575529bSPhilippe Mathieu-Daudé tlb->VPN &= env->SEGMask; 656575529bSPhilippe Mathieu-Daudé #endif 666575529bSPhilippe Mathieu-Daudé tlb->ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 676575529bSPhilippe Mathieu-Daudé tlb->MMID = env->CP0_MemoryMapID; 686575529bSPhilippe Mathieu-Daudé tlb->PageMask = env->CP0_PageMask; 696575529bSPhilippe Mathieu-Daudé tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; 706575529bSPhilippe Mathieu-Daudé tlb->V0 = (env->CP0_EntryLo0 & 2) != 0; 716575529bSPhilippe Mathieu-Daudé tlb->D0 = (env->CP0_EntryLo0 & 4) != 0; 726575529bSPhilippe Mathieu-Daudé tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7; 736575529bSPhilippe Mathieu-Daudé tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1; 746575529bSPhilippe Mathieu-Daudé tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1; 756575529bSPhilippe Mathieu-Daudé tlb->PFN[0] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) & ~mask) << 12; 766575529bSPhilippe Mathieu-Daudé tlb->V1 = (env->CP0_EntryLo1 & 2) != 0; 776575529bSPhilippe Mathieu-Daudé tlb->D1 = (env->CP0_EntryLo1 & 4) != 0; 786575529bSPhilippe Mathieu-Daudé tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7; 796575529bSPhilippe Mathieu-Daudé tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1; 806575529bSPhilippe Mathieu-Daudé tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1; 816575529bSPhilippe Mathieu-Daudé tlb->PFN[1] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) & ~mask) << 12; 826575529bSPhilippe Mathieu-Daudé } 836575529bSPhilippe Mathieu-Daudé 846575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbinv(CPUMIPSState *env) 856575529bSPhilippe Mathieu-Daudé { 866575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 876575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 886575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 896575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 906575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 916575529bSPhilippe Mathieu-Daudé int idx; 926575529bSPhilippe Mathieu-Daudé 936575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 946575529bSPhilippe Mathieu-Daudé for (idx = 0; idx < env->tlb->nb_tlb; idx++) { 956575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 966575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 976575529bSPhilippe Mathieu-Daudé if (!tlb->G && tlb_mmid == MMID) { 986575529bSPhilippe Mathieu-Daudé tlb->EHINV = 1; 996575529bSPhilippe Mathieu-Daudé } 1006575529bSPhilippe Mathieu-Daudé } 1016575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 1026575529bSPhilippe Mathieu-Daudé } 1036575529bSPhilippe Mathieu-Daudé 1046575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbinvf(CPUMIPSState *env) 1056575529bSPhilippe Mathieu-Daudé { 1066575529bSPhilippe Mathieu-Daudé int idx; 1076575529bSPhilippe Mathieu-Daudé 1086575529bSPhilippe Mathieu-Daudé for (idx = 0; idx < env->tlb->nb_tlb; idx++) { 1096575529bSPhilippe Mathieu-Daudé env->tlb->mmu.r4k.tlb[idx].EHINV = 1; 1106575529bSPhilippe Mathieu-Daudé } 1116575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 1126575529bSPhilippe Mathieu-Daudé } 1136575529bSPhilippe Mathieu-Daudé 1146575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbwi(CPUMIPSState *env) 1156575529bSPhilippe Mathieu-Daudé { 1166575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 1176575529bSPhilippe Mathieu-Daudé target_ulong VPN; 1186575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 1196575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 1206575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 1216575529bSPhilippe Mathieu-Daudé bool EHINV, G, V0, D0, V1, D1, XI0, XI1, RI0, RI1; 1226575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 1236575529bSPhilippe Mathieu-Daudé int idx; 1246575529bSPhilippe Mathieu-Daudé 1256575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 1266575529bSPhilippe Mathieu-Daudé 1276575529bSPhilippe Mathieu-Daudé idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; 1286575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 1296575529bSPhilippe Mathieu-Daudé VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); 1306575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 1316575529bSPhilippe Mathieu-Daudé VPN &= env->SEGMask; 1326575529bSPhilippe Mathieu-Daudé #endif 1336575529bSPhilippe Mathieu-Daudé EHINV = (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) != 0; 1346575529bSPhilippe Mathieu-Daudé G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; 1356575529bSPhilippe Mathieu-Daudé V0 = (env->CP0_EntryLo0 & 2) != 0; 1366575529bSPhilippe Mathieu-Daudé D0 = (env->CP0_EntryLo0 & 4) != 0; 1376575529bSPhilippe Mathieu-Daudé XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) &1; 1386575529bSPhilippe Mathieu-Daudé RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) &1; 1396575529bSPhilippe Mathieu-Daudé V1 = (env->CP0_EntryLo1 & 2) != 0; 1406575529bSPhilippe Mathieu-Daudé D1 = (env->CP0_EntryLo1 & 4) != 0; 1416575529bSPhilippe Mathieu-Daudé XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) &1; 1426575529bSPhilippe Mathieu-Daudé RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) &1; 1436575529bSPhilippe Mathieu-Daudé 1446575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 1456575529bSPhilippe Mathieu-Daudé /* 1466575529bSPhilippe Mathieu-Daudé * Discard cached TLB entries, unless tlbwi is just upgrading access 1476575529bSPhilippe Mathieu-Daudé * permissions on the current entry. 1486575529bSPhilippe Mathieu-Daudé */ 1496575529bSPhilippe Mathieu-Daudé if (tlb->VPN != VPN || tlb_mmid != MMID || tlb->G != G || 1506575529bSPhilippe Mathieu-Daudé (!tlb->EHINV && EHINV) || 1516575529bSPhilippe Mathieu-Daudé (tlb->V0 && !V0) || (tlb->D0 && !D0) || 1526575529bSPhilippe Mathieu-Daudé (!tlb->XI0 && XI0) || (!tlb->RI0 && RI0) || 1536575529bSPhilippe Mathieu-Daudé (tlb->V1 && !V1) || (tlb->D1 && !D1) || 1546575529bSPhilippe Mathieu-Daudé (!tlb->XI1 && XI1) || (!tlb->RI1 && RI1)) { 1556575529bSPhilippe Mathieu-Daudé r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); 1566575529bSPhilippe Mathieu-Daudé } 1576575529bSPhilippe Mathieu-Daudé 1586575529bSPhilippe Mathieu-Daudé r4k_invalidate_tlb(env, idx, 0); 1596575529bSPhilippe Mathieu-Daudé r4k_fill_tlb(env, idx); 1606575529bSPhilippe Mathieu-Daudé } 1616575529bSPhilippe Mathieu-Daudé 1626575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbwr(CPUMIPSState *env) 1636575529bSPhilippe Mathieu-Daudé { 1646575529bSPhilippe Mathieu-Daudé int r = cpu_mips_get_random(env); 1656575529bSPhilippe Mathieu-Daudé 1666575529bSPhilippe Mathieu-Daudé r4k_invalidate_tlb(env, r, 1); 1676575529bSPhilippe Mathieu-Daudé r4k_fill_tlb(env, r); 1686575529bSPhilippe Mathieu-Daudé } 1696575529bSPhilippe Mathieu-Daudé 1706575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbp(CPUMIPSState *env) 1716575529bSPhilippe Mathieu-Daudé { 1726575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 1736575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 1746575529bSPhilippe Mathieu-Daudé target_ulong mask; 1756575529bSPhilippe Mathieu-Daudé target_ulong tag; 1766575529bSPhilippe Mathieu-Daudé target_ulong VPN; 1776575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 1786575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 1796575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 1806575529bSPhilippe Mathieu-Daudé int i; 1816575529bSPhilippe Mathieu-Daudé 1826575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 1836575529bSPhilippe Mathieu-Daudé for (i = 0; i < env->tlb->nb_tlb; i++) { 1846575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[i]; 1856575529bSPhilippe Mathieu-Daudé /* 1k pages are not supported. */ 1866575529bSPhilippe Mathieu-Daudé mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 1876575529bSPhilippe Mathieu-Daudé tag = env->CP0_EntryHi & ~mask; 1886575529bSPhilippe Mathieu-Daudé VPN = tlb->VPN & ~mask; 1896575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 1906575529bSPhilippe Mathieu-Daudé tag &= env->SEGMask; 1916575529bSPhilippe Mathieu-Daudé #endif 1926575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 1936575529bSPhilippe Mathieu-Daudé /* Check ASID/MMID, virtual page number & size */ 1946575529bSPhilippe Mathieu-Daudé if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { 1956575529bSPhilippe Mathieu-Daudé /* TLB match */ 1966575529bSPhilippe Mathieu-Daudé env->CP0_Index = i; 1976575529bSPhilippe Mathieu-Daudé break; 1986575529bSPhilippe Mathieu-Daudé } 1996575529bSPhilippe Mathieu-Daudé } 2006575529bSPhilippe Mathieu-Daudé if (i == env->tlb->nb_tlb) { 2016575529bSPhilippe Mathieu-Daudé /* No match. Discard any shadow entries, if any of them match. */ 2026575529bSPhilippe Mathieu-Daudé for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) { 2036575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[i]; 2046575529bSPhilippe Mathieu-Daudé /* 1k pages are not supported. */ 2056575529bSPhilippe Mathieu-Daudé mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 2066575529bSPhilippe Mathieu-Daudé tag = env->CP0_EntryHi & ~mask; 2076575529bSPhilippe Mathieu-Daudé VPN = tlb->VPN & ~mask; 2086575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 2096575529bSPhilippe Mathieu-Daudé tag &= env->SEGMask; 2106575529bSPhilippe Mathieu-Daudé #endif 2116575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 2126575529bSPhilippe Mathieu-Daudé /* Check ASID/MMID, virtual page number & size */ 2136575529bSPhilippe Mathieu-Daudé if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag) { 2146575529bSPhilippe Mathieu-Daudé r4k_mips_tlb_flush_extra(env, i); 2156575529bSPhilippe Mathieu-Daudé break; 2166575529bSPhilippe Mathieu-Daudé } 2176575529bSPhilippe Mathieu-Daudé } 2186575529bSPhilippe Mathieu-Daudé 2196575529bSPhilippe Mathieu-Daudé env->CP0_Index |= 0x80000000; 2206575529bSPhilippe Mathieu-Daudé } 2216575529bSPhilippe Mathieu-Daudé } 2226575529bSPhilippe Mathieu-Daudé 2236575529bSPhilippe Mathieu-Daudé static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn) 2246575529bSPhilippe Mathieu-Daudé { 2256575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 2266575529bSPhilippe Mathieu-Daudé return tlb_pfn << 6; 2276575529bSPhilippe Mathieu-Daudé #else 2286575529bSPhilippe Mathieu-Daudé return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */ 2296575529bSPhilippe Mathieu-Daudé (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */ 2306575529bSPhilippe Mathieu-Daudé #endif 2316575529bSPhilippe Mathieu-Daudé } 2326575529bSPhilippe Mathieu-Daudé 2336575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbr(CPUMIPSState *env) 2346575529bSPhilippe Mathieu-Daudé { 2356575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 2366575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 2376575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 2386575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 2396575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 2406575529bSPhilippe Mathieu-Daudé int idx; 2416575529bSPhilippe Mathieu-Daudé 2426575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 2436575529bSPhilippe Mathieu-Daudé idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; 2446575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 2456575529bSPhilippe Mathieu-Daudé 2466575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 2476575529bSPhilippe Mathieu-Daudé /* If this will change the current ASID/MMID, flush qemu's TLB. */ 2486575529bSPhilippe Mathieu-Daudé if (MMID != tlb_mmid) { 2496575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 2506575529bSPhilippe Mathieu-Daudé } 2516575529bSPhilippe Mathieu-Daudé 2526575529bSPhilippe Mathieu-Daudé r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); 2536575529bSPhilippe Mathieu-Daudé 2546575529bSPhilippe Mathieu-Daudé if (tlb->EHINV) { 2556575529bSPhilippe Mathieu-Daudé env->CP0_EntryHi = 1 << CP0EnHi_EHINV; 2566575529bSPhilippe Mathieu-Daudé env->CP0_PageMask = 0; 2576575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo0 = 0; 2586575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo1 = 0; 2596575529bSPhilippe Mathieu-Daudé } else { 2606575529bSPhilippe Mathieu-Daudé env->CP0_EntryHi = mi ? tlb->VPN : tlb->VPN | tlb->ASID; 2616575529bSPhilippe Mathieu-Daudé env->CP0_MemoryMapID = tlb->MMID; 2626575529bSPhilippe Mathieu-Daudé env->CP0_PageMask = tlb->PageMask; 2636575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | 2646575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->RI0 << CP0EnLo_RI) | 2656575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) | 2666575529bSPhilippe Mathieu-Daudé get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12); 2676575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | 2686575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->RI1 << CP0EnLo_RI) | 2696575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) | 2706575529bSPhilippe Mathieu-Daudé get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12); 2716575529bSPhilippe Mathieu-Daudé } 2726575529bSPhilippe Mathieu-Daudé } 2736575529bSPhilippe Mathieu-Daudé 2746575529bSPhilippe Mathieu-Daudé void helper_tlbwi(CPUMIPSState *env) 2756575529bSPhilippe Mathieu-Daudé { 2766575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbwi(env); 2776575529bSPhilippe Mathieu-Daudé } 2786575529bSPhilippe Mathieu-Daudé 2796575529bSPhilippe Mathieu-Daudé void helper_tlbwr(CPUMIPSState *env) 2806575529bSPhilippe Mathieu-Daudé { 2816575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbwr(env); 2826575529bSPhilippe Mathieu-Daudé } 2836575529bSPhilippe Mathieu-Daudé 2846575529bSPhilippe Mathieu-Daudé void helper_tlbp(CPUMIPSState *env) 2856575529bSPhilippe Mathieu-Daudé { 2866575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbp(env); 2876575529bSPhilippe Mathieu-Daudé } 2886575529bSPhilippe Mathieu-Daudé 2896575529bSPhilippe Mathieu-Daudé void helper_tlbr(CPUMIPSState *env) 2906575529bSPhilippe Mathieu-Daudé { 2916575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbr(env); 2926575529bSPhilippe Mathieu-Daudé } 2936575529bSPhilippe Mathieu-Daudé 2946575529bSPhilippe Mathieu-Daudé void helper_tlbinv(CPUMIPSState *env) 2956575529bSPhilippe Mathieu-Daudé { 2966575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbinv(env); 2976575529bSPhilippe Mathieu-Daudé } 2986575529bSPhilippe Mathieu-Daudé 2996575529bSPhilippe Mathieu-Daudé void helper_tlbinvf(CPUMIPSState *env) 3006575529bSPhilippe Mathieu-Daudé { 3016575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbinvf(env); 3026575529bSPhilippe Mathieu-Daudé } 3036575529bSPhilippe Mathieu-Daudé 3046575529bSPhilippe Mathieu-Daudé static void global_invalidate_tlb(CPUMIPSState *env, 3056575529bSPhilippe Mathieu-Daudé uint32_t invMsgVPN2, 3066575529bSPhilippe Mathieu-Daudé uint8_t invMsgR, 3076575529bSPhilippe Mathieu-Daudé uint32_t invMsgMMid, 3086575529bSPhilippe Mathieu-Daudé bool invAll, 3096575529bSPhilippe Mathieu-Daudé bool invVAMMid, 3106575529bSPhilippe Mathieu-Daudé bool invMMid, 3116575529bSPhilippe Mathieu-Daudé bool invVA) 3126575529bSPhilippe Mathieu-Daudé { 3136575529bSPhilippe Mathieu-Daudé 3146575529bSPhilippe Mathieu-Daudé int idx; 3156575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 3166575529bSPhilippe Mathieu-Daudé bool VAMatch; 3176575529bSPhilippe Mathieu-Daudé bool MMidMatch; 3186575529bSPhilippe Mathieu-Daudé 3196575529bSPhilippe Mathieu-Daudé for (idx = 0; idx < env->tlb->nb_tlb; idx++) { 3206575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 3216575529bSPhilippe Mathieu-Daudé VAMatch = 3226575529bSPhilippe Mathieu-Daudé (((tlb->VPN & ~tlb->PageMask) == (invMsgVPN2 & ~tlb->PageMask)) 3236575529bSPhilippe Mathieu-Daudé #ifdef TARGET_MIPS64 3246575529bSPhilippe Mathieu-Daudé && 3256575529bSPhilippe Mathieu-Daudé (extract64(env->CP0_EntryHi, 62, 2) == invMsgR) 3266575529bSPhilippe Mathieu-Daudé #endif 3276575529bSPhilippe Mathieu-Daudé ); 3286575529bSPhilippe Mathieu-Daudé MMidMatch = tlb->MMID == invMsgMMid; 3296575529bSPhilippe Mathieu-Daudé if ((invAll && (idx > env->CP0_Wired)) || 3306575529bSPhilippe Mathieu-Daudé (VAMatch && invVAMMid && (tlb->G || MMidMatch)) || 3316575529bSPhilippe Mathieu-Daudé (VAMatch && invVA) || 3326575529bSPhilippe Mathieu-Daudé (MMidMatch && !(tlb->G) && invMMid)) { 3336575529bSPhilippe Mathieu-Daudé tlb->EHINV = 1; 3346575529bSPhilippe Mathieu-Daudé } 3356575529bSPhilippe Mathieu-Daudé } 3366575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 3376575529bSPhilippe Mathieu-Daudé } 3386575529bSPhilippe Mathieu-Daudé 3396575529bSPhilippe Mathieu-Daudé void helper_ginvt(CPUMIPSState *env, target_ulong arg, uint32_t type) 3406575529bSPhilippe Mathieu-Daudé { 3416575529bSPhilippe Mathieu-Daudé bool invAll = type == 0; 3426575529bSPhilippe Mathieu-Daudé bool invVA = type == 1; 3436575529bSPhilippe Mathieu-Daudé bool invMMid = type == 2; 3446575529bSPhilippe Mathieu-Daudé bool invVAMMid = type == 3; 3456575529bSPhilippe Mathieu-Daudé uint32_t invMsgVPN2 = arg & (TARGET_PAGE_MASK << 1); 3466575529bSPhilippe Mathieu-Daudé uint8_t invMsgR = 0; 3476575529bSPhilippe Mathieu-Daudé uint32_t invMsgMMid = env->CP0_MemoryMapID; 3486575529bSPhilippe Mathieu-Daudé CPUState *other_cs = first_cpu; 3496575529bSPhilippe Mathieu-Daudé 3506575529bSPhilippe Mathieu-Daudé #ifdef TARGET_MIPS64 3516575529bSPhilippe Mathieu-Daudé invMsgR = extract64(arg, 62, 2); 3526575529bSPhilippe Mathieu-Daudé #endif 3536575529bSPhilippe Mathieu-Daudé 3546575529bSPhilippe Mathieu-Daudé CPU_FOREACH(other_cs) { 3556575529bSPhilippe Mathieu-Daudé MIPSCPU *other_cpu = MIPS_CPU(other_cs); 3566575529bSPhilippe Mathieu-Daudé global_invalidate_tlb(&other_cpu->env, invMsgVPN2, invMsgR, invMsgMMid, 3576575529bSPhilippe Mathieu-Daudé invAll, invVAMMid, invMMid, invVA); 3586575529bSPhilippe Mathieu-Daudé } 3596575529bSPhilippe Mathieu-Daudé } 3606af0bf9cSbellard 36129929e34Sths /* no MMU emulation */ 362f3185ec2SPhilippe Mathieu-Daudé static int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, 363edbd4992SPhilippe Mathieu-Daudé target_ulong address, MMUAccessType access_type) 36429929e34Sths { 36529929e34Sths *physical = address; 3667353113fSJakub Jermář *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 36729929e34Sths return TLBRET_MATCH; 36829929e34Sths } 36929929e34Sths 37029929e34Sths /* fixed mapping MMU emulation */ 371f3185ec2SPhilippe Mathieu-Daudé static int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, 372f3185ec2SPhilippe Mathieu-Daudé int *prot, target_ulong address, 373f3185ec2SPhilippe Mathieu-Daudé MMUAccessType access_type) 37429929e34Sths { 37529929e34Sths if (address <= (int32_t)0x7FFFFFFFUL) { 376d7551eceSAleksandar Markovic if (!(env->CP0_Status & (1 << CP0St_ERL))) { 37729929e34Sths *physical = address + 0x40000000UL; 378d7551eceSAleksandar Markovic } else { 37929929e34Sths *physical = address; 380d7551eceSAleksandar Markovic } 381d7551eceSAleksandar Markovic } else if (address <= (int32_t)0xBFFFFFFFUL) { 38229929e34Sths *physical = address & 0x1FFFFFFF; 383d7551eceSAleksandar Markovic } else { 38429929e34Sths *physical = address; 385d7551eceSAleksandar Markovic } 38629929e34Sths 3877353113fSJakub Jermář *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 38829929e34Sths return TLBRET_MATCH; 38929929e34Sths } 39029929e34Sths 39129929e34Sths /* MIPS32/MIPS64 R4000-style MMU emulation */ 392f3185ec2SPhilippe Mathieu-Daudé static int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, 393edbd4992SPhilippe Mathieu-Daudé target_ulong address, MMUAccessType access_type) 3946af0bf9cSbellard { 3952d72e7b0SPaul Burton uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 39699029be1SYongbok Kim uint32_t MMID = env->CP0_MemoryMapID; 39799029be1SYongbok Kim bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 39899029be1SYongbok Kim uint32_t tlb_mmid; 3993b1c8be4Sths int i; 4006af0bf9cSbellard 40199029be1SYongbok Kim MMID = mi ? MMID : (uint32_t) ASID; 40299029be1SYongbok Kim 403ead9360eSths for (i = 0; i < env->tlb->tlb_in_use; i++) { 404c227f099SAnthony Liguori r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; 4053b1c8be4Sths /* 1k pages are not supported. */ 406f2e9ebefSths target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 4073b1c8be4Sths target_ulong tag = address & ~mask; 408f2e9ebefSths target_ulong VPN = tlb->VPN & ~mask; 409d26bc211Sths #if defined(TARGET_MIPS64) 410e034e2c3Sths tag &= env->SEGMask; 411100ce988Sths #endif 4123b1c8be4Sths 41399029be1SYongbok Kim /* Check ASID/MMID, virtual page number & size */ 41499029be1SYongbok Kim tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 41599029be1SYongbok Kim if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { 4166af0bf9cSbellard /* TLB match */ 417f2e9ebefSths int n = !!(address & mask & ~(mask >> 1)); 4186af0bf9cSbellard /* Check access rights */ 4192fb58b73SLeon Alrae if (!(n ? tlb->V1 : tlb->V0)) { 42043057ab1Sbellard return TLBRET_INVALID; 4212fb58b73SLeon Alrae } 422edbd4992SPhilippe Mathieu-Daudé if (access_type == MMU_INST_FETCH && (n ? tlb->XI1 : tlb->XI0)) { 4232fb58b73SLeon Alrae return TLBRET_XI; 4242fb58b73SLeon Alrae } 425edbd4992SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_LOAD && (n ? tlb->RI1 : tlb->RI0)) { 4262fb58b73SLeon Alrae return TLBRET_RI; 4272fb58b73SLeon Alrae } 428edbd4992SPhilippe Mathieu-Daudé if (access_type != MMU_DATA_STORE || (n ? tlb->D1 : tlb->D0)) { 4293b1c8be4Sths *physical = tlb->PFN[n] | (address & (mask >> 1)); 4309fb63ac2Sbellard *prot = PAGE_READ; 431d7551eceSAleksandar Markovic if (n ? tlb->D1 : tlb->D0) { 4329fb63ac2Sbellard *prot |= PAGE_WRITE; 433d7551eceSAleksandar Markovic } 4347353113fSJakub Jermář if (!(n ? tlb->XI1 : tlb->XI0)) { 4357353113fSJakub Jermář *prot |= PAGE_EXEC; 4367353113fSJakub Jermář } 43743057ab1Sbellard return TLBRET_MATCH; 4386af0bf9cSbellard } 43943057ab1Sbellard return TLBRET_DIRTY; 4406af0bf9cSbellard } 4416af0bf9cSbellard } 44243057ab1Sbellard return TLBRET_NOMATCH; 4436af0bf9cSbellard } 4446af0bf9cSbellard 445f2c5b39eSPhilippe Mathieu-Daudé static void no_mmu_init(CPUMIPSState *env, const mips_def_t *def) 446f2c5b39eSPhilippe Mathieu-Daudé { 447f2c5b39eSPhilippe Mathieu-Daudé env->tlb->nb_tlb = 1; 448f2c5b39eSPhilippe Mathieu-Daudé env->tlb->map_address = &no_mmu_map_address; 449f2c5b39eSPhilippe Mathieu-Daudé } 450f2c5b39eSPhilippe Mathieu-Daudé 451f2c5b39eSPhilippe Mathieu-Daudé static void fixed_mmu_init(CPUMIPSState *env, const mips_def_t *def) 452f2c5b39eSPhilippe Mathieu-Daudé { 453f2c5b39eSPhilippe Mathieu-Daudé env->tlb->nb_tlb = 1; 454f2c5b39eSPhilippe Mathieu-Daudé env->tlb->map_address = &fixed_mmu_map_address; 455f2c5b39eSPhilippe Mathieu-Daudé } 456f2c5b39eSPhilippe Mathieu-Daudé 457f2c5b39eSPhilippe Mathieu-Daudé static void r4k_mmu_init(CPUMIPSState *env, const mips_def_t *def) 458f2c5b39eSPhilippe Mathieu-Daudé { 459f2c5b39eSPhilippe Mathieu-Daudé env->tlb->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63); 460f2c5b39eSPhilippe Mathieu-Daudé env->tlb->map_address = &r4k_map_address; 461f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbwi = r4k_helper_tlbwi; 462f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbwr = r4k_helper_tlbwr; 463f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbp = r4k_helper_tlbp; 464f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbr = r4k_helper_tlbr; 465f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbinv = r4k_helper_tlbinv; 466f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbinvf = r4k_helper_tlbinvf; 467f2c5b39eSPhilippe Mathieu-Daudé } 468f2c5b39eSPhilippe Mathieu-Daudé 469f2c5b39eSPhilippe Mathieu-Daudé void mmu_init(CPUMIPSState *env, const mips_def_t *def) 470f2c5b39eSPhilippe Mathieu-Daudé { 471f2c5b39eSPhilippe Mathieu-Daudé env->tlb = g_malloc0(sizeof(CPUMIPSTLBContext)); 472f2c5b39eSPhilippe Mathieu-Daudé 473f2c5b39eSPhilippe Mathieu-Daudé switch (def->mmu_type) { 474f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_NONE: 475f2c5b39eSPhilippe Mathieu-Daudé no_mmu_init(env, def); 476f2c5b39eSPhilippe Mathieu-Daudé break; 477f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R4000: 478f2c5b39eSPhilippe Mathieu-Daudé r4k_mmu_init(env, def); 479f2c5b39eSPhilippe Mathieu-Daudé break; 480f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_FMT: 481f2c5b39eSPhilippe Mathieu-Daudé fixed_mmu_init(env, def); 482f2c5b39eSPhilippe Mathieu-Daudé break; 483f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R3000: 484f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R6000: 485f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R8000: 486f2c5b39eSPhilippe Mathieu-Daudé default: 487f2c5b39eSPhilippe Mathieu-Daudé cpu_abort(env_cpu(env), "MMU type not supported\n"); 488f2c5b39eSPhilippe Mathieu-Daudé } 489f2c5b39eSPhilippe Mathieu-Daudé } 490f2c5b39eSPhilippe Mathieu-Daudé 491d10eb08fSAlex Bennée void cpu_mips_tlb_flush(CPUMIPSState *env) 492e6623d88SPaolo Bonzini { 493e6623d88SPaolo Bonzini /* Flush qemu's TLB and discard all shadowed entries. */ 4945a7330b3SRichard Henderson tlb_flush(env_cpu(env)); 495e6623d88SPaolo Bonzini env->tlb->tlb_in_use = env->tlb->nb_tlb; 496e6623d88SPaolo Bonzini } 497e6623d88SPaolo Bonzini 4987db13faeSAndreas Färber static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, 499ca354f00SPhilippe Mathieu-Daudé MMUAccessType access_type, int tlb_error) 5006af0bf9cSbellard { 5015a7330b3SRichard Henderson CPUState *cs = env_cpu(env); 5026af0bf9cSbellard int exception = 0, error_code = 0; 5036af0bf9cSbellard 504ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_INST_FETCH) { 505aea14095SLeon Alrae error_code |= EXCP_INST_NOTAVAIL; 506aea14095SLeon Alrae } 507aea14095SLeon Alrae 5081147e189SAurelien Jarno switch (tlb_error) { 5096af0bf9cSbellard default: 51043057ab1Sbellard case TLBRET_BADADDR: 5116af0bf9cSbellard /* Reference to kernel address from user mode or supervisor mode */ 5126af0bf9cSbellard /* Reference to supervisor address from user mode */ 513ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_STORE) { 5146af0bf9cSbellard exception = EXCP_AdES; 5159f6bcedbSLeon Alrae } else { 5166af0bf9cSbellard exception = EXCP_AdEL; 5179f6bcedbSLeon Alrae } 5186af0bf9cSbellard break; 51943057ab1Sbellard case TLBRET_NOMATCH: 5206af0bf9cSbellard /* No TLB match for a mapped address */ 521ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_STORE) { 5226af0bf9cSbellard exception = EXCP_TLBS; 5239f6bcedbSLeon Alrae } else { 5246af0bf9cSbellard exception = EXCP_TLBL; 5259f6bcedbSLeon Alrae } 526aea14095SLeon Alrae error_code |= EXCP_TLB_NOMATCH; 5276af0bf9cSbellard break; 52843057ab1Sbellard case TLBRET_INVALID: 5296af0bf9cSbellard /* TLB match with no valid bit */ 530ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_STORE) { 5316af0bf9cSbellard exception = EXCP_TLBS; 5329f6bcedbSLeon Alrae } else { 5336af0bf9cSbellard exception = EXCP_TLBL; 5349f6bcedbSLeon Alrae } 5356af0bf9cSbellard break; 53643057ab1Sbellard case TLBRET_DIRTY: 5376af0bf9cSbellard /* TLB match but 'D' bit is cleared */ 5386af0bf9cSbellard exception = EXCP_LTLBL; 5396af0bf9cSbellard break; 54092ceb440SLeon Alrae case TLBRET_XI: 54192ceb440SLeon Alrae /* Execute-Inhibit Exception */ 54292ceb440SLeon Alrae if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { 54392ceb440SLeon Alrae exception = EXCP_TLBXI; 54492ceb440SLeon Alrae } else { 54592ceb440SLeon Alrae exception = EXCP_TLBL; 54692ceb440SLeon Alrae } 54792ceb440SLeon Alrae break; 54892ceb440SLeon Alrae case TLBRET_RI: 54992ceb440SLeon Alrae /* Read-Inhibit Exception */ 55092ceb440SLeon Alrae if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { 55192ceb440SLeon Alrae exception = EXCP_TLBRI; 55292ceb440SLeon Alrae } else { 55392ceb440SLeon Alrae exception = EXCP_TLBL; 55492ceb440SLeon Alrae } 55592ceb440SLeon Alrae break; 5566af0bf9cSbellard } 5576af0bf9cSbellard /* Raise exception */ 558e807bcc1SYongbok Kim if (!(env->hflags & MIPS_HFLAG_DM)) { 5596af0bf9cSbellard env->CP0_BadVAddr = address; 560e807bcc1SYongbok Kim } 561100ce988Sths env->CP0_Context = (env->CP0_Context & ~0x007fffff) | 5624ad40f36Sbellard ((address >> 9) & 0x007ffff0); 5636ec98bd7SPaul Burton env->CP0_EntryHi = (env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask) | 564701074a6SLeon Alrae (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) | 5656ec98bd7SPaul Burton (address & (TARGET_PAGE_MASK << 1)); 566d26bc211Sths #if defined(TARGET_MIPS64) 567e034e2c3Sths env->CP0_EntryHi &= env->SEGMask; 56860270f85SYongbok Kim env->CP0_XContext = 569d7551eceSAleksandar Markovic (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | /* PTEBase */ 570d7551eceSAleksandar Markovic (extract64(address, 62, 2) << (env->SEGBITS - 9)) | /* R */ 571d7551eceSAleksandar Markovic (extract64(address, 13, env->SEGBITS - 13) << 4); /* BadVPN2 */ 572100ce988Sths #endif 57327103424SAndreas Färber cs->exception_index = exception; 5746af0bf9cSbellard env->error_code = error_code; 5751147e189SAurelien Jarno } 5761147e189SAurelien Jarno 577074cfcb4SYongbok Kim #if !defined(TARGET_MIPS64) 578074cfcb4SYongbok Kim 579074cfcb4SYongbok Kim /* 580074cfcb4SYongbok Kim * Perform hardware page table walk 581074cfcb4SYongbok Kim * 582074cfcb4SYongbok Kim * Memory accesses are performed using the KERNEL privilege level. 583074cfcb4SYongbok Kim * Synchronous exceptions detected on memory accesses cause a silent exit 584074cfcb4SYongbok Kim * from page table walking, resulting in a TLB or XTLB Refill exception. 585074cfcb4SYongbok Kim * 586074cfcb4SYongbok Kim * Implementations are not required to support page table walk memory 587074cfcb4SYongbok Kim * accesses from mapped memory regions. When an unsupported access is 588074cfcb4SYongbok Kim * attempted, a silent exit is taken, resulting in a TLB or XTLB Refill 589074cfcb4SYongbok Kim * exception. 590074cfcb4SYongbok Kim * 591074cfcb4SYongbok Kim * Note that if an exception is caused by AddressTranslation or LoadMemory 592074cfcb4SYongbok Kim * functions, the exception is not taken, a silent exit is taken, 593074cfcb4SYongbok Kim * resulting in a TLB or XTLB Refill exception. 594074cfcb4SYongbok Kim */ 595074cfcb4SYongbok Kim 596453ba4f6SPhilippe Mathieu-Daudé static bool get_pte(CPUMIPSState *env, uint64_t vaddr, MemOp op, 5977ce9760dSPhilippe Mathieu-Daudé uint64_t *pte, unsigned ptw_mmu_idx) 598074cfcb4SYongbok Kim { 5997ce9760dSPhilippe Mathieu-Daudé MemOpIdx oi; 6007ce9760dSPhilippe Mathieu-Daudé 601453ba4f6SPhilippe Mathieu-Daudé if ((vaddr & (memop_size(op) - 1)) != 0) { 602074cfcb4SYongbok Kim return false; 603074cfcb4SYongbok Kim } 6047ce9760dSPhilippe Mathieu-Daudé 6052cf8226fSPhilippe Mathieu-Daudé oi = make_memop_idx(op | mo_endian_env(env), ptw_mmu_idx); 606453ba4f6SPhilippe Mathieu-Daudé if (op == MO_64) { 60744017c66SPhilippe Mathieu-Daudé *pte = cpu_ldq_mmu(env, vaddr, oi, 0); 608074cfcb4SYongbok Kim } else { 60944017c66SPhilippe Mathieu-Daudé *pte = cpu_ldl_mmu(env, vaddr, oi, 0); 610074cfcb4SYongbok Kim } 6117ce9760dSPhilippe Mathieu-Daudé 612074cfcb4SYongbok Kim return true; 613074cfcb4SYongbok Kim } 614074cfcb4SYongbok Kim 615074cfcb4SYongbok Kim static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, 616453ba4f6SPhilippe Mathieu-Daudé MemOp op, int ptei) 617074cfcb4SYongbok Kim { 618453ba4f6SPhilippe Mathieu-Daudé unsigned entry_size = memop_size(op) << 3; 619074cfcb4SYongbok Kim uint64_t result = entry; 620074cfcb4SYongbok Kim uint64_t rixi; 621074cfcb4SYongbok Kim if (ptei > entry_size) { 622074cfcb4SYongbok Kim ptei -= 32; 623074cfcb4SYongbok Kim } 624074cfcb4SYongbok Kim result >>= (ptei - 2); 625074cfcb4SYongbok Kim rixi = result & 3; 626074cfcb4SYongbok Kim result >>= 2; 627074cfcb4SYongbok Kim result |= rixi << CP0EnLo_XI; 628074cfcb4SYongbok Kim return result; 629074cfcb4SYongbok Kim } 630074cfcb4SYongbok Kim 631074cfcb4SYongbok Kim static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, 632074cfcb4SYongbok Kim int directory_index, bool *huge_page, bool *hgpg_directory_hit, 63360a38a3aSPhilippe Mathieu-Daudé uint64_t *pw_entrylo0, uint64_t *pw_entrylo1, 634453ba4f6SPhilippe Mathieu-Daudé MemOp directory_mop, MemOp leaf_mop, int ptw_mmu_idx) 635074cfcb4SYongbok Kim { 636074cfcb4SYongbok Kim int dph = (env->CP0_PWCtl >> CP0PC_DPH) & 0x1; 637074cfcb4SYongbok Kim int psn = (env->CP0_PWCtl >> CP0PC_PSN) & 0x3F; 638074cfcb4SYongbok Kim int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; 639074cfcb4SYongbok Kim int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; 640074cfcb4SYongbok Kim uint64_t entry; 641074cfcb4SYongbok Kim uint64_t paddr; 642074cfcb4SYongbok Kim int prot; 643074cfcb4SYongbok Kim uint64_t lsb = 0; 644074cfcb4SYongbok Kim uint64_t w = 0; 645074cfcb4SYongbok Kim 646074cfcb4SYongbok Kim if (get_physical_address(env, &paddr, &prot, *vaddr, MMU_DATA_LOAD, 6474e999bf4SRichard Henderson ptw_mmu_idx) != TLBRET_MATCH) { 648074cfcb4SYongbok Kim /* wrong base address */ 649074cfcb4SYongbok Kim return 0; 650074cfcb4SYongbok Kim } 6517ce9760dSPhilippe Mathieu-Daudé if (!get_pte(env, *vaddr, directory_mop, &entry, ptw_mmu_idx)) { 652074cfcb4SYongbok Kim return 0; 653074cfcb4SYongbok Kim } 654074cfcb4SYongbok Kim 655074cfcb4SYongbok Kim if ((entry & (1 << psn)) && hugepg) { 656074cfcb4SYongbok Kim *huge_page = true; 657074cfcb4SYongbok Kim *hgpg_directory_hit = true; 658453ba4f6SPhilippe Mathieu-Daudé entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); 659074cfcb4SYongbok Kim w = directory_index - 1; 660074cfcb4SYongbok Kim if (directory_index & 0x1) { 661074cfcb4SYongbok Kim /* Generate adjacent page from same PTE for odd TLB page */ 6622838b1d6SPhilippe Mathieu-Daudé lsb = BIT_ULL(w) >> 6; 663074cfcb4SYongbok Kim *pw_entrylo0 = entry & ~lsb; /* even page */ 664074cfcb4SYongbok Kim *pw_entrylo1 = entry | lsb; /* odd page */ 665074cfcb4SYongbok Kim } else if (dph) { 666453ba4f6SPhilippe Mathieu-Daudé int oddpagebit = 1 << leaf_mop; 667074cfcb4SYongbok Kim uint64_t vaddr2 = *vaddr ^ oddpagebit; 668074cfcb4SYongbok Kim if (*vaddr & oddpagebit) { 669074cfcb4SYongbok Kim *pw_entrylo1 = entry; 670074cfcb4SYongbok Kim } else { 671074cfcb4SYongbok Kim *pw_entrylo0 = entry; 672074cfcb4SYongbok Kim } 673074cfcb4SYongbok Kim if (get_physical_address(env, &paddr, &prot, vaddr2, MMU_DATA_LOAD, 6744e999bf4SRichard Henderson ptw_mmu_idx) != TLBRET_MATCH) { 675074cfcb4SYongbok Kim return 0; 676074cfcb4SYongbok Kim } 6777ce9760dSPhilippe Mathieu-Daudé if (!get_pte(env, vaddr2, leaf_mop, &entry, ptw_mmu_idx)) { 678074cfcb4SYongbok Kim return 0; 679074cfcb4SYongbok Kim } 680453ba4f6SPhilippe Mathieu-Daudé entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); 681074cfcb4SYongbok Kim if (*vaddr & oddpagebit) { 682074cfcb4SYongbok Kim *pw_entrylo0 = entry; 683074cfcb4SYongbok Kim } else { 684074cfcb4SYongbok Kim *pw_entrylo1 = entry; 685074cfcb4SYongbok Kim } 686074cfcb4SYongbok Kim } else { 687074cfcb4SYongbok Kim return 0; 688074cfcb4SYongbok Kim } 689074cfcb4SYongbok Kim return 1; 690074cfcb4SYongbok Kim } else { 691074cfcb4SYongbok Kim *vaddr = entry; 692074cfcb4SYongbok Kim return 2; 693074cfcb4SYongbok Kim } 694074cfcb4SYongbok Kim } 695074cfcb4SYongbok Kim 696bca3763bSPhilippe Mathieu-Daudé static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, 6974e999bf4SRichard Henderson int ptw_mmu_idx) 698074cfcb4SYongbok Kim { 699074cfcb4SYongbok Kim int gdw = (env->CP0_PWSize >> CP0PS_GDW) & 0x3F; 700074cfcb4SYongbok Kim int udw = (env->CP0_PWSize >> CP0PS_UDW) & 0x3F; 701074cfcb4SYongbok Kim int mdw = (env->CP0_PWSize >> CP0PS_MDW) & 0x3F; 702074cfcb4SYongbok Kim int ptw = (env->CP0_PWSize >> CP0PS_PTW) & 0x3F; 703074cfcb4SYongbok Kim int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; 704074cfcb4SYongbok Kim 705074cfcb4SYongbok Kim /* Initial values */ 706074cfcb4SYongbok Kim bool huge_page = false; 707074cfcb4SYongbok Kim bool hgpg_bdhit = false; 708074cfcb4SYongbok Kim bool hgpg_gdhit = false; 709074cfcb4SYongbok Kim bool hgpg_udhit = false; 710074cfcb4SYongbok Kim bool hgpg_mdhit = false; 711074cfcb4SYongbok Kim 712074cfcb4SYongbok Kim int32_t pw_pagemask = 0; 713074cfcb4SYongbok Kim target_ulong pw_entryhi = 0; 714074cfcb4SYongbok Kim uint64_t pw_entrylo0 = 0; 715074cfcb4SYongbok Kim uint64_t pw_entrylo1 = 0; 716074cfcb4SYongbok Kim 717074cfcb4SYongbok Kim /* Native pointer size */ 718074cfcb4SYongbok Kim /*For the 32-bit architectures, this bit is fixed to 0.*/ 719453ba4f6SPhilippe Mathieu-Daudé MemOp native_op = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? MO_32 : MO_64; 720074cfcb4SYongbok Kim 721074cfcb4SYongbok Kim /* Indices from PWField */ 722074cfcb4SYongbok Kim int pf_gdw = (env->CP0_PWField >> CP0PF_GDW) & 0x3F; 723074cfcb4SYongbok Kim int pf_udw = (env->CP0_PWField >> CP0PF_UDW) & 0x3F; 724074cfcb4SYongbok Kim int pf_mdw = (env->CP0_PWField >> CP0PF_MDW) & 0x3F; 725074cfcb4SYongbok Kim int pf_ptw = (env->CP0_PWField >> CP0PF_PTW) & 0x3F; 726074cfcb4SYongbok Kim int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; 727074cfcb4SYongbok Kim 728074cfcb4SYongbok Kim /* Indices computed from faulting address */ 729074cfcb4SYongbok Kim int gindex = (address >> pf_gdw) & ((1 << gdw) - 1); 730074cfcb4SYongbok Kim int uindex = (address >> pf_udw) & ((1 << udw) - 1); 731074cfcb4SYongbok Kim int mindex = (address >> pf_mdw) & ((1 << mdw) - 1); 732074cfcb4SYongbok Kim int ptindex = (address >> pf_ptw) & ((1 << ptw) - 1); 733074cfcb4SYongbok Kim 734074cfcb4SYongbok Kim /* Other HTW configs */ 735074cfcb4SYongbok Kim int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; 736453ba4f6SPhilippe Mathieu-Daudé MemOp directory_mop, leaf_mop; 737074cfcb4SYongbok Kim 738074cfcb4SYongbok Kim /* Offsets into tables */ 7390fe4cac5SPeter Maydell unsigned goffset, uoffset, moffset, ptoffset0, ptoffset1; 740074cfcb4SYongbok Kim 741074cfcb4SYongbok Kim /* Starting address - Page Table Base */ 742074cfcb4SYongbok Kim uint64_t vaddr = env->CP0_PWBase; 743074cfcb4SYongbok Kim 744074cfcb4SYongbok Kim uint64_t dir_entry; 745074cfcb4SYongbok Kim uint64_t paddr; 746074cfcb4SYongbok Kim int prot; 747074cfcb4SYongbok Kim int m; 748074cfcb4SYongbok Kim 749074cfcb4SYongbok Kim if (!(env->CP0_Config3 & (1 << CP0C3_PW))) { 750074cfcb4SYongbok Kim /* walker is unimplemented */ 751074cfcb4SYongbok Kim return false; 752074cfcb4SYongbok Kim } 753074cfcb4SYongbok Kim if (!(env->CP0_PWCtl & (1 << CP0PC_PWEN))) { 754074cfcb4SYongbok Kim /* walker is disabled */ 755074cfcb4SYongbok Kim return false; 756074cfcb4SYongbok Kim } 757074cfcb4SYongbok Kim if (!(gdw > 0 || udw > 0 || mdw > 0)) { 758074cfcb4SYongbok Kim /* no structure to walk */ 759074cfcb4SYongbok Kim return false; 760074cfcb4SYongbok Kim } 7610fe4cac5SPeter Maydell if (ptew > 1) { 762074cfcb4SYongbok Kim return false; 763074cfcb4SYongbok Kim } 764074cfcb4SYongbok Kim 7650fe4cac5SPeter Maydell /* HTW Shift values (depend on entry size) */ 766453ba4f6SPhilippe Mathieu-Daudé directory_mop = (hugepg && (ptew == 1)) ? native_op + 1 : native_op; 767453ba4f6SPhilippe Mathieu-Daudé leaf_mop = (ptew == 1) ? native_op + 1 : native_op; 7680fe4cac5SPeter Maydell 769453ba4f6SPhilippe Mathieu-Daudé goffset = gindex << directory_mop; 770453ba4f6SPhilippe Mathieu-Daudé uoffset = uindex << directory_mop; 771453ba4f6SPhilippe Mathieu-Daudé moffset = mindex << directory_mop; 772453ba4f6SPhilippe Mathieu-Daudé ptoffset0 = (ptindex >> 1) << (leaf_mop + 1); 773453ba4f6SPhilippe Mathieu-Daudé ptoffset1 = ptoffset0 | (1 << (leaf_mop)); 7740fe4cac5SPeter Maydell 775074cfcb4SYongbok Kim /* Global Directory */ 776074cfcb4SYongbok Kim if (gdw > 0) { 777074cfcb4SYongbok Kim vaddr |= goffset; 778074cfcb4SYongbok Kim switch (walk_directory(env, &vaddr, pf_gdw, &huge_page, &hgpg_gdhit, 77960a38a3aSPhilippe Mathieu-Daudé &pw_entrylo0, &pw_entrylo1, 780453ba4f6SPhilippe Mathieu-Daudé directory_mop, leaf_mop, ptw_mmu_idx)) 781074cfcb4SYongbok Kim { 782074cfcb4SYongbok Kim case 0: 783074cfcb4SYongbok Kim return false; 784074cfcb4SYongbok Kim case 1: 785074cfcb4SYongbok Kim goto refill; 786074cfcb4SYongbok Kim case 2: 787074cfcb4SYongbok Kim default: 788074cfcb4SYongbok Kim break; 789074cfcb4SYongbok Kim } 790074cfcb4SYongbok Kim } 791074cfcb4SYongbok Kim 792074cfcb4SYongbok Kim /* Upper directory */ 793074cfcb4SYongbok Kim if (udw > 0) { 794074cfcb4SYongbok Kim vaddr |= uoffset; 795074cfcb4SYongbok Kim switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhit, 79660a38a3aSPhilippe Mathieu-Daudé &pw_entrylo0, &pw_entrylo1, 797453ba4f6SPhilippe Mathieu-Daudé directory_mop, leaf_mop, ptw_mmu_idx)) 798074cfcb4SYongbok Kim { 799074cfcb4SYongbok Kim case 0: 800074cfcb4SYongbok Kim return false; 801074cfcb4SYongbok Kim case 1: 802074cfcb4SYongbok Kim goto refill; 803074cfcb4SYongbok Kim case 2: 804074cfcb4SYongbok Kim default: 805074cfcb4SYongbok Kim break; 806074cfcb4SYongbok Kim } 807074cfcb4SYongbok Kim } 808074cfcb4SYongbok Kim 809074cfcb4SYongbok Kim /* Middle directory */ 810074cfcb4SYongbok Kim if (mdw > 0) { 811074cfcb4SYongbok Kim vaddr |= moffset; 812074cfcb4SYongbok Kim switch (walk_directory(env, &vaddr, pf_mdw, &huge_page, &hgpg_mdhit, 81360a38a3aSPhilippe Mathieu-Daudé &pw_entrylo0, &pw_entrylo1, 814453ba4f6SPhilippe Mathieu-Daudé directory_mop, leaf_mop, ptw_mmu_idx)) 815074cfcb4SYongbok Kim { 816074cfcb4SYongbok Kim case 0: 817074cfcb4SYongbok Kim return false; 818074cfcb4SYongbok Kim case 1: 819074cfcb4SYongbok Kim goto refill; 820074cfcb4SYongbok Kim case 2: 821074cfcb4SYongbok Kim default: 822074cfcb4SYongbok Kim break; 823074cfcb4SYongbok Kim } 824074cfcb4SYongbok Kim } 825074cfcb4SYongbok Kim 826074cfcb4SYongbok Kim /* Leaf Level Page Table - First half of PTE pair */ 827074cfcb4SYongbok Kim vaddr |= ptoffset0; 828074cfcb4SYongbok Kim if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, 8294e999bf4SRichard Henderson ptw_mmu_idx) != TLBRET_MATCH) { 830074cfcb4SYongbok Kim return false; 831074cfcb4SYongbok Kim } 8327ce9760dSPhilippe Mathieu-Daudé if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { 833074cfcb4SYongbok Kim return false; 834074cfcb4SYongbok Kim } 835453ba4f6SPhilippe Mathieu-Daudé dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); 836074cfcb4SYongbok Kim pw_entrylo0 = dir_entry; 837074cfcb4SYongbok Kim 838074cfcb4SYongbok Kim /* Leaf Level Page Table - Second half of PTE pair */ 839074cfcb4SYongbok Kim vaddr |= ptoffset1; 840074cfcb4SYongbok Kim if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, 8414e999bf4SRichard Henderson ptw_mmu_idx) != TLBRET_MATCH) { 842074cfcb4SYongbok Kim return false; 843074cfcb4SYongbok Kim } 8447ce9760dSPhilippe Mathieu-Daudé if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { 845074cfcb4SYongbok Kim return false; 846074cfcb4SYongbok Kim } 847453ba4f6SPhilippe Mathieu-Daudé dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); 848074cfcb4SYongbok Kim pw_entrylo1 = dir_entry; 849074cfcb4SYongbok Kim 850074cfcb4SYongbok Kim refill: 851074cfcb4SYongbok Kim 852074cfcb4SYongbok Kim m = (1 << pf_ptw) - 1; 853074cfcb4SYongbok Kim 854074cfcb4SYongbok Kim if (huge_page) { 855074cfcb4SYongbok Kim switch (hgpg_bdhit << 3 | hgpg_gdhit << 2 | hgpg_udhit << 1 | 856074cfcb4SYongbok Kim hgpg_mdhit) 857074cfcb4SYongbok Kim { 858074cfcb4SYongbok Kim case 4: 859074cfcb4SYongbok Kim m = (1 << pf_gdw) - 1; 860074cfcb4SYongbok Kim if (pf_gdw & 1) { 861074cfcb4SYongbok Kim m >>= 1; 862074cfcb4SYongbok Kim } 863074cfcb4SYongbok Kim break; 864074cfcb4SYongbok Kim case 2: 865074cfcb4SYongbok Kim m = (1 << pf_udw) - 1; 866074cfcb4SYongbok Kim if (pf_udw & 1) { 867074cfcb4SYongbok Kim m >>= 1; 868074cfcb4SYongbok Kim } 869074cfcb4SYongbok Kim break; 870074cfcb4SYongbok Kim case 1: 871074cfcb4SYongbok Kim m = (1 << pf_mdw) - 1; 872074cfcb4SYongbok Kim if (pf_mdw & 1) { 873074cfcb4SYongbok Kim m >>= 1; 874074cfcb4SYongbok Kim } 875074cfcb4SYongbok Kim break; 876074cfcb4SYongbok Kim } 877074cfcb4SYongbok Kim } 878547b9b17SPhilippe Mathieu-Daudé pw_pagemask = m >> TARGET_PAGE_BITS_MIN; 879547b9b17SPhilippe Mathieu-Daudé update_pagemask(env, pw_pagemask << CP0PM_MASK, &pw_pagemask); 880074cfcb4SYongbok Kim pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); 881074cfcb4SYongbok Kim { 882074cfcb4SYongbok Kim target_ulong tmp_entryhi = env->CP0_EntryHi; 883074cfcb4SYongbok Kim int32_t tmp_pagemask = env->CP0_PageMask; 884074cfcb4SYongbok Kim uint64_t tmp_entrylo0 = env->CP0_EntryLo0; 885074cfcb4SYongbok Kim uint64_t tmp_entrylo1 = env->CP0_EntryLo1; 886074cfcb4SYongbok Kim 887074cfcb4SYongbok Kim env->CP0_EntryHi = pw_entryhi; 888074cfcb4SYongbok Kim env->CP0_PageMask = pw_pagemask; 889074cfcb4SYongbok Kim env->CP0_EntryLo0 = pw_entrylo0; 890074cfcb4SYongbok Kim env->CP0_EntryLo1 = pw_entrylo1; 891074cfcb4SYongbok Kim 892074cfcb4SYongbok Kim /* 893074cfcb4SYongbok Kim * The hardware page walker inserts a page into the TLB in a manner 894074cfcb4SYongbok Kim * identical to a TLBWR instruction as executed by the software refill 895074cfcb4SYongbok Kim * handler. 896074cfcb4SYongbok Kim */ 897074cfcb4SYongbok Kim r4k_helper_tlbwr(env); 898074cfcb4SYongbok Kim 899074cfcb4SYongbok Kim env->CP0_EntryHi = tmp_entryhi; 900074cfcb4SYongbok Kim env->CP0_PageMask = tmp_pagemask; 901074cfcb4SYongbok Kim env->CP0_EntryLo0 = tmp_entrylo0; 902074cfcb4SYongbok Kim env->CP0_EntryLo1 = tmp_entrylo1; 903074cfcb4SYongbok Kim } 904074cfcb4SYongbok Kim return true; 905074cfcb4SYongbok Kim } 906074cfcb4SYongbok Kim #endif 907074cfcb4SYongbok Kim 908931d019fSRichard Henderson bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 909931d019fSRichard Henderson MMUAccessType access_type, int mmu_idx, 910931d019fSRichard Henderson bool probe, uintptr_t retaddr) 9111147e189SAurelien Jarno { 9124c44a980SPhilippe Mathieu-Daudé CPUMIPSState *env = cpu_env(cs); 913a8170e5eSAvi Kivity hwaddr physical; 9141147e189SAurelien Jarno int prot; 915995ffde9SRichard Henderson int ret = TLBRET_BADADDR; 9161147e189SAurelien Jarno 9171147e189SAurelien Jarno /* data access */ 918074cfcb4SYongbok Kim /* XXX: put correct access by using cpu_restore_state() correctly */ 919931d019fSRichard Henderson ret = get_physical_address(env, &physical, &prot, address, 920935c1034SPhilippe Mathieu-Daudé access_type, mmu_idx); 921def74c0cSPhilippe Mathieu-Daudé switch (ret) { 922def74c0cSPhilippe Mathieu-Daudé case TLBRET_MATCH: 923339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, 924883f2c59SPhilippe Mathieu-Daudé "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx 925def74c0cSPhilippe Mathieu-Daudé " prot %d\n", __func__, address, physical, prot); 926def74c0cSPhilippe Mathieu-Daudé break; 927def74c0cSPhilippe Mathieu-Daudé default: 928def74c0cSPhilippe Mathieu-Daudé qemu_log_mask(CPU_LOG_MMU, 929def74c0cSPhilippe Mathieu-Daudé "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, 930def74c0cSPhilippe Mathieu-Daudé ret); 931def74c0cSPhilippe Mathieu-Daudé break; 932def74c0cSPhilippe Mathieu-Daudé } 9331147e189SAurelien Jarno if (ret == TLBRET_MATCH) { 9340c591eb0SAndreas Färber tlb_set_page(cs, address & TARGET_PAGE_MASK, 9357353113fSJakub Jermář physical & TARGET_PAGE_MASK, prot, 936d4c430a8SPaul Brook mmu_idx, TARGET_PAGE_SIZE); 937931d019fSRichard Henderson return true; 938e38f4eb6SRichard Henderson } 939074cfcb4SYongbok Kim #if !defined(TARGET_MIPS64) 940074cfcb4SYongbok Kim if ((ret == TLBRET_NOMATCH) && (env->tlb->nb_tlb > 1)) { 941074cfcb4SYongbok Kim /* 942074cfcb4SYongbok Kim * Memory reads during hardware page table walking are performed 943074cfcb4SYongbok Kim * as if they were kernel-mode load instructions. 944074cfcb4SYongbok Kim */ 9454e999bf4SRichard Henderson int ptw_mmu_idx = (env->hflags & MIPS_HFLAG_ERL ? 9464e999bf4SRichard Henderson MMU_ERL_IDX : MMU_KERNEL_IDX); 9474e999bf4SRichard Henderson 9484e999bf4SRichard Henderson if (page_table_walk_refill(env, address, ptw_mmu_idx)) { 949931d019fSRichard Henderson ret = get_physical_address(env, &physical, &prot, address, 950935c1034SPhilippe Mathieu-Daudé access_type, mmu_idx); 951074cfcb4SYongbok Kim if (ret == TLBRET_MATCH) { 952074cfcb4SYongbok Kim tlb_set_page(cs, address & TARGET_PAGE_MASK, 9537353113fSJakub Jermář physical & TARGET_PAGE_MASK, prot, 954074cfcb4SYongbok Kim mmu_idx, TARGET_PAGE_SIZE); 955931d019fSRichard Henderson return true; 956074cfcb4SYongbok Kim } 957074cfcb4SYongbok Kim } 958074cfcb4SYongbok Kim } 959074cfcb4SYongbok Kim #endif 960931d019fSRichard Henderson if (probe) { 961931d019fSRichard Henderson return false; 962931d019fSRichard Henderson } 9636af0bf9cSbellard 964931d019fSRichard Henderson raise_mmu_exception(env, address, access_type, ret); 965931d019fSRichard Henderson do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr); 9666af0bf9cSbellard } 9676af0bf9cSbellard 968d7551eceSAleksandar Markovic hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, 9690debf140SPhilippe Mathieu-Daudé MMUAccessType access_type, uintptr_t retaddr) 97025b91e32SAurelien Jarno { 971a8170e5eSAvi Kivity hwaddr physical; 97225b91e32SAurelien Jarno int prot; 97325b91e32SAurelien Jarno int ret = 0; 9740debf140SPhilippe Mathieu-Daudé CPUState *cs = env_cpu(env); 97525b91e32SAurelien Jarno 97625b91e32SAurelien Jarno /* data access */ 97748b28c6aSPhilippe Mathieu-Daudé ret = get_physical_address(env, &physical, &prot, address, access_type, 9786ebf33c5SRichard Henderson mips_env_mmu_index(env)); 9790debf140SPhilippe Mathieu-Daudé if (ret == TLBRET_MATCH) { 98025b91e32SAurelien Jarno return physical; 98125b91e32SAurelien Jarno } 9820debf140SPhilippe Mathieu-Daudé 9830debf140SPhilippe Mathieu-Daudé raise_mmu_exception(env, address, access_type, ret); 9840debf140SPhilippe Mathieu-Daudé cpu_loop_exit_restore(cs, retaddr); 985c36bbb28SAurelien Jarno } 98625b91e32SAurelien Jarno 9877db13faeSAndreas Färber static void set_hflags_for_handler(CPUMIPSState *env) 988bbfa8f72SNathan Froyd { 989bbfa8f72SNathan Froyd /* Exception handlers are entered in 32-bit mode. */ 990bbfa8f72SNathan Froyd env->hflags &= ~(MIPS_HFLAG_M16); 991bbfa8f72SNathan Froyd /* ...except that microMIPS lets you choose. */ 992bbfa8f72SNathan Froyd if (env->insn_flags & ASE_MICROMIPS) { 993d7551eceSAleksandar Markovic env->hflags |= (!!(env->CP0_Config3 & 994d7551eceSAleksandar Markovic (1 << CP0C3_ISA_ON_EXC)) 995bbfa8f72SNathan Froyd << MIPS_HFLAG_M16_SHIFT); 996bbfa8f72SNathan Froyd } 997bbfa8f72SNathan Froyd } 998aea14095SLeon Alrae 999aea14095SLeon Alrae static inline void set_badinstr_registers(CPUMIPSState *env) 1000aea14095SLeon Alrae { 10017a5f784aSStefan Markovic if (env->insn_flags & ISA_NANOMIPS32) { 10027a5f784aSStefan Markovic if (env->CP0_Config3 & (1 << CP0C3_BI)) { 10037a5f784aSStefan Markovic uint32_t instr = (cpu_lduw_code(env, env->active_tc.PC)) << 16; 10047a5f784aSStefan Markovic if ((instr & 0x10000000) == 0) { 10057a5f784aSStefan Markovic instr |= cpu_lduw_code(env, env->active_tc.PC + 2); 10067a5f784aSStefan Markovic } 10077a5f784aSStefan Markovic env->CP0_BadInstr = instr; 10087a5f784aSStefan Markovic 10097a5f784aSStefan Markovic if ((instr & 0xFC000000) == 0x60000000) { 10107a5f784aSStefan Markovic instr = cpu_lduw_code(env, env->active_tc.PC + 4) << 16; 10117a5f784aSStefan Markovic env->CP0_BadInstrX = instr; 10127a5f784aSStefan Markovic } 10137a5f784aSStefan Markovic } 10147a5f784aSStefan Markovic return; 10157a5f784aSStefan Markovic } 10167a5f784aSStefan Markovic 1017aea14095SLeon Alrae if (env->hflags & MIPS_HFLAG_M16) { 1018aea14095SLeon Alrae /* TODO: add BadInstr support for microMIPS */ 1019aea14095SLeon Alrae return; 1020aea14095SLeon Alrae } 1021aea14095SLeon Alrae if (env->CP0_Config3 & (1 << CP0C3_BI)) { 1022aea14095SLeon Alrae env->CP0_BadInstr = cpu_ldl_code(env, env->active_tc.PC); 1023aea14095SLeon Alrae } 1024aea14095SLeon Alrae if ((env->CP0_Config3 & (1 << CP0C3_BP)) && 1025aea14095SLeon Alrae (env->hflags & MIPS_HFLAG_BMASK)) { 1026aea14095SLeon Alrae env->CP0_BadInstrP = cpu_ldl_code(env, env->active_tc.PC - 4); 1027aea14095SLeon Alrae } 1028aea14095SLeon Alrae } 1029f9bd3d79SPhilippe Mathieu-Daudé 103097a8ea5aSAndreas Färber void mips_cpu_do_interrupt(CPUState *cs) 1031ca7c2b1bSths { 103297a8ea5aSAndreas Färber MIPSCPU *cpu = MIPS_CPU(cs); 103397a8ea5aSAndreas Färber CPUMIPSState *env = &cpu->env; 1034aea14095SLeon Alrae bool update_badinstr = 0; 1035aa328addSths target_ulong offset; 10366af0bf9cSbellard int cause = -1; 10376af0bf9cSbellard 1038c8557016SRichard Henderson if (qemu_loglevel_mask(CPU_LOG_INT) 1039c8557016SRichard Henderson && cs->exception_index != EXCP_EXT_INTERRUPT) { 1040c8557016SRichard Henderson qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx 1041c8557016SRichard Henderson " %s exception\n", 104290c429eeSPhilippe Mathieu-Daudé __func__, env->active_tc.PC, env->CP0_EPC, 104390c429eeSPhilippe Mathieu-Daudé mips_exception_name(cs->exception_index)); 10446af0bf9cSbellard } 104527103424SAndreas Färber if (cs->exception_index == EXCP_EXT_INTERRUPT && 104627103424SAndreas Färber (env->hflags & MIPS_HFLAG_DM)) { 104727103424SAndreas Färber cs->exception_index = EXCP_DINT; 104827103424SAndreas Färber } 10496af0bf9cSbellard offset = 0x180; 105027103424SAndreas Färber switch (cs->exception_index) { 10518ec7e3c5SRichard Henderson case EXCP_SEMIHOST: 10528ec7e3c5SRichard Henderson cs->exception_index = EXCP_NONE; 10538ec7e3c5SRichard Henderson mips_semihosting(env); 1054d44971e7SRichard Henderson env->active_tc.PC += env->error_code; 10558ec7e3c5SRichard Henderson return; 10566af0bf9cSbellard case EXCP_DSS: 10576af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DSS; 1058d7551eceSAleksandar Markovic /* 1059d7551eceSAleksandar Markovic * Debug single step cannot be raised inside a delay slot and 1060d7551eceSAleksandar Markovic * resume will always occur on the next instruction 1061d7551eceSAleksandar Markovic * (but we assume the pc has always been updated during 1062d7551eceSAleksandar Markovic * code translation). 1063d7551eceSAleksandar Markovic */ 106432188a03SNathan Froyd env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16); 10656af0bf9cSbellard goto enter_debug_mode; 10666af0bf9cSbellard case EXCP_DINT: 10676af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DINT; 10686af0bf9cSbellard goto set_DEPC; 10696af0bf9cSbellard case EXCP_DIB: 10706af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DIB; 10716af0bf9cSbellard goto set_DEPC; 10726af0bf9cSbellard case EXCP_DBp: 10736af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DBp; 1074c6c2c0fcSPavel Dovgalyuk /* Setup DExcCode - SDBBP instruction */ 1075d7551eceSAleksandar Markovic env->CP0_Debug = (env->CP0_Debug & ~(0x1fULL << CP0DB_DEC)) | 1076d7551eceSAleksandar Markovic (9 << CP0DB_DEC); 10776af0bf9cSbellard goto set_DEPC; 10786af0bf9cSbellard case EXCP_DDBS: 10796af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DDBS; 10806af0bf9cSbellard goto set_DEPC; 10816af0bf9cSbellard case EXCP_DDBL: 10826af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DDBL; 10836af0bf9cSbellard set_DEPC: 108432188a03SNathan Froyd env->CP0_DEPC = exception_resume_pc(env); 10854ad40f36Sbellard env->hflags &= ~MIPS_HFLAG_BMASK; 10866af0bf9cSbellard enter_debug_mode: 1087d9224450SMaciej W. Rozycki if (env->insn_flags & ISA_MIPS3) { 1088d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_64; 10892e211e0aSPhilippe Mathieu-Daudé if (!(env->insn_flags & ISA_MIPS_R6) || 10907871abb9SJames Hogan env->CP0_Status & (1 << CP0St_KX)) { 10917871abb9SJames Hogan env->hflags &= ~MIPS_HFLAG_AWRAP; 10927871abb9SJames Hogan } 1093d9224450SMaciej W. Rozycki } 1094d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_CP0; 1095623a930eSths env->hflags &= ~(MIPS_HFLAG_KSU); 10966af0bf9cSbellard /* EJTAG probe trap enable is not implemented... */ 1097d7551eceSAleksandar Markovic if (!(env->CP0_Status & (1 << CP0St_EXL))) { 1098f45cb2f4SPeter Maydell env->CP0_Cause &= ~(1U << CP0Ca_BD); 1099d7551eceSAleksandar Markovic } 110089777fd1SLeon Alrae env->active_tc.PC = env->exception_base + 0x480; 1101bbfa8f72SNathan Froyd set_hflags_for_handler(env); 11026af0bf9cSbellard break; 11036af0bf9cSbellard case EXCP_RESET: 1104fca1be7cSAndreas Färber cpu_reset(CPU(cpu)); 1105aa328addSths break; 11066af0bf9cSbellard case EXCP_SRESET: 110724c7b0e3Sths env->CP0_Status |= (1 << CP0St_SR); 11089d989c73SAurelien Jarno memset(env->CP0_WatchLo, 0, sizeof(env->CP0_WatchLo)); 11096af0bf9cSbellard goto set_error_EPC; 11106af0bf9cSbellard case EXCP_NMI: 111124c7b0e3Sths env->CP0_Status |= (1 << CP0St_NMI); 11126af0bf9cSbellard set_error_EPC: 111332188a03SNathan Froyd env->CP0_ErrorEPC = exception_resume_pc(env); 1114ecd78a0aSpbrook env->hflags &= ~MIPS_HFLAG_BMASK; 1115aa328addSths env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); 1116d9224450SMaciej W. Rozycki if (env->insn_flags & ISA_MIPS3) { 1117d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_64; 11182e211e0aSPhilippe Mathieu-Daudé if (!(env->insn_flags & ISA_MIPS_R6) || 11197871abb9SJames Hogan env->CP0_Status & (1 << CP0St_KX)) { 11207871abb9SJames Hogan env->hflags &= ~MIPS_HFLAG_AWRAP; 11217871abb9SJames Hogan } 1122d9224450SMaciej W. Rozycki } 1123d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_CP0; 1124623a930eSths env->hflags &= ~(MIPS_HFLAG_KSU); 1125d7551eceSAleksandar Markovic if (!(env->CP0_Status & (1 << CP0St_EXL))) { 1126f45cb2f4SPeter Maydell env->CP0_Cause &= ~(1U << CP0Ca_BD); 1127d7551eceSAleksandar Markovic } 112889777fd1SLeon Alrae env->active_tc.PC = env->exception_base; 1129bbfa8f72SNathan Froyd set_hflags_for_handler(env); 11306af0bf9cSbellard break; 11316af0bf9cSbellard case EXCP_EXT_INTERRUPT: 11326af0bf9cSbellard cause = 0; 1133da52a4dfSYongbok Kim if (env->CP0_Cause & (1 << CP0Ca_IV)) { 1134da52a4dfSYongbok Kim uint32_t spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & 0x1f; 1135da52a4dfSYongbok Kim 1136da52a4dfSYongbok Kim if ((env->CP0_Status & (1 << CP0St_BEV)) || spacing == 0) { 11376af0bf9cSbellard offset = 0x200; 1138138afb02SEdgar E. Iglesias } else { 1139da52a4dfSYongbok Kim uint32_t vector = 0; 1140da52a4dfSYongbok Kim uint32_t pending = (env->CP0_Cause & CP0Ca_IP_mask) >> CP0Ca_IP; 1141da52a4dfSYongbok Kim 1142da52a4dfSYongbok Kim if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { 1143d7551eceSAleksandar Markovic /* 1144d7551eceSAleksandar Markovic * For VEIC mode, the external interrupt controller feeds 1145d7551eceSAleksandar Markovic * the vector through the CP0Cause IP lines. 1146d7551eceSAleksandar Markovic */ 1147138afb02SEdgar E. Iglesias vector = pending; 1148da52a4dfSYongbok Kim } else { 1149d7551eceSAleksandar Markovic /* 1150d7551eceSAleksandar Markovic * Vectored Interrupts 1151d7551eceSAleksandar Markovic * Mask with Status.IM7-IM0 to get enabled interrupts. 1152d7551eceSAleksandar Markovic */ 1153da52a4dfSYongbok Kim pending &= (env->CP0_Status >> CP0St_IM) & 0xff; 1154da52a4dfSYongbok Kim /* Find the highest-priority interrupt. */ 1155da52a4dfSYongbok Kim while (pending >>= 1) { 1156da52a4dfSYongbok Kim vector++; 1157138afb02SEdgar E. Iglesias } 1158da52a4dfSYongbok Kim } 1159da52a4dfSYongbok Kim offset = 0x200 + (vector * (spacing << 5)); 1160da52a4dfSYongbok Kim } 1161138afb02SEdgar E. Iglesias } 11626af0bf9cSbellard goto set_EPC; 1163b67bfe8dSths case EXCP_LTLBL: 1164b67bfe8dSths cause = 1; 1165aea14095SLeon Alrae update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); 1166beb811bdSths goto set_EPC; 11676af0bf9cSbellard case EXCP_TLBL: 11686af0bf9cSbellard cause = 2; 1169aea14095SLeon Alrae update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); 1170aea14095SLeon Alrae if ((env->error_code & EXCP_TLB_NOMATCH) && 1171aea14095SLeon Alrae !(env->CP0_Status & (1 << CP0St_EXL))) { 1172d26bc211Sths #if defined(TARGET_MIPS64) 1173100ce988Sths int R = env->CP0_BadVAddr >> 62; 1174100ce988Sths int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; 1175100ce988Sths int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; 1176100ce988Sths 1177480e79aeSJames Hogan if ((R != 0 || UX) && (R != 3 || KX) && 1178480e79aeSJames Hogan (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { 1179100ce988Sths offset = 0x080; 1180480e79aeSJames Hogan } else { 1181100ce988Sths #endif 11826af0bf9cSbellard offset = 0x000; 1183480e79aeSJames Hogan #if defined(TARGET_MIPS64) 1184480e79aeSJames Hogan } 1185480e79aeSJames Hogan #endif 1186100ce988Sths } 11876af0bf9cSbellard goto set_EPC; 1188b67bfe8dSths case EXCP_TLBS: 1189b67bfe8dSths cause = 3; 1190aea14095SLeon Alrae update_badinstr = 1; 1191aea14095SLeon Alrae if ((env->error_code & EXCP_TLB_NOMATCH) && 1192aea14095SLeon Alrae !(env->CP0_Status & (1 << CP0St_EXL))) { 1193b67bfe8dSths #if defined(TARGET_MIPS64) 1194b67bfe8dSths int R = env->CP0_BadVAddr >> 62; 1195b67bfe8dSths int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; 1196b67bfe8dSths int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; 1197b67bfe8dSths 1198480e79aeSJames Hogan if ((R != 0 || UX) && (R != 3 || KX) && 1199480e79aeSJames Hogan (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { 1200b67bfe8dSths offset = 0x080; 1201480e79aeSJames Hogan } else { 1202b67bfe8dSths #endif 1203b67bfe8dSths offset = 0x000; 1204480e79aeSJames Hogan #if defined(TARGET_MIPS64) 1205480e79aeSJames Hogan } 1206480e79aeSJames Hogan #endif 1207b67bfe8dSths } 1208b67bfe8dSths goto set_EPC; 1209b67bfe8dSths case EXCP_AdEL: 1210b67bfe8dSths cause = 4; 1211aea14095SLeon Alrae update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); 1212b67bfe8dSths goto set_EPC; 1213b67bfe8dSths case EXCP_AdES: 1214b67bfe8dSths cause = 5; 1215aea14095SLeon Alrae update_badinstr = 1; 1216b67bfe8dSths goto set_EPC; 12176af0bf9cSbellard case EXCP_IBE: 12186af0bf9cSbellard cause = 6; 12196af0bf9cSbellard goto set_EPC; 12206af0bf9cSbellard case EXCP_DBE: 12216af0bf9cSbellard cause = 7; 12226af0bf9cSbellard goto set_EPC; 12236af0bf9cSbellard case EXCP_SYSCALL: 12246af0bf9cSbellard cause = 8; 1225aea14095SLeon Alrae update_badinstr = 1; 12266af0bf9cSbellard goto set_EPC; 12276af0bf9cSbellard case EXCP_BREAK: 12286af0bf9cSbellard cause = 9; 1229aea14095SLeon Alrae update_badinstr = 1; 12306af0bf9cSbellard goto set_EPC; 12316af0bf9cSbellard case EXCP_RI: 12326af0bf9cSbellard cause = 10; 1233aea14095SLeon Alrae update_badinstr = 1; 12346af0bf9cSbellard goto set_EPC; 12356af0bf9cSbellard case EXCP_CpU: 12366af0bf9cSbellard cause = 11; 1237aea14095SLeon Alrae update_badinstr = 1; 123839d51eb8Sths env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | 123939d51eb8Sths (env->error_code << CP0Ca_CE); 12406af0bf9cSbellard goto set_EPC; 12416af0bf9cSbellard case EXCP_OVERFLOW: 12426af0bf9cSbellard cause = 12; 1243aea14095SLeon Alrae update_badinstr = 1; 12446af0bf9cSbellard goto set_EPC; 12456af0bf9cSbellard case EXCP_TRAP: 12466af0bf9cSbellard cause = 13; 1247aea14095SLeon Alrae update_badinstr = 1; 12486af0bf9cSbellard goto set_EPC; 1249b10ac204SYongbok Kim case EXCP_MSAFPE: 1250b10ac204SYongbok Kim cause = 14; 1251b10ac204SYongbok Kim update_badinstr = 1; 1252b10ac204SYongbok Kim goto set_EPC; 12535a5012ecSths case EXCP_FPE: 12545a5012ecSths cause = 15; 1255aea14095SLeon Alrae update_badinstr = 1; 12565a5012ecSths goto set_EPC; 1257b67bfe8dSths case EXCP_C2E: 1258b67bfe8dSths cause = 18; 12596af0bf9cSbellard goto set_EPC; 126092ceb440SLeon Alrae case EXCP_TLBRI: 126192ceb440SLeon Alrae cause = 19; 1262aea14095SLeon Alrae update_badinstr = 1; 126392ceb440SLeon Alrae goto set_EPC; 126492ceb440SLeon Alrae case EXCP_TLBXI: 126592ceb440SLeon Alrae cause = 20; 126692ceb440SLeon Alrae goto set_EPC; 1267b10ac204SYongbok Kim case EXCP_MSADIS: 1268b10ac204SYongbok Kim cause = 21; 1269b10ac204SYongbok Kim update_badinstr = 1; 1270b10ac204SYongbok Kim goto set_EPC; 1271b67bfe8dSths case EXCP_MDMX: 1272b67bfe8dSths cause = 22; 1273b67bfe8dSths goto set_EPC; 1274b67bfe8dSths case EXCP_DWATCH: 1275b67bfe8dSths cause = 23; 127667cc32ebSVeres Lajos /* XXX: TODO: manage deferred watch exceptions */ 1277b67bfe8dSths goto set_EPC; 1278b67bfe8dSths case EXCP_MCHECK: 1279b67bfe8dSths cause = 24; 12806276c767Sths goto set_EPC; 12816276c767Sths case EXCP_THREAD: 12826276c767Sths cause = 25; 1283b67bfe8dSths goto set_EPC; 1284853c3240SJia Liu case EXCP_DSPDIS: 1285853c3240SJia Liu cause = 26; 1286853c3240SJia Liu goto set_EPC; 1287b67bfe8dSths case EXCP_CACHE: 1288b67bfe8dSths cause = 30; 1289b67bfe8dSths offset = 0x100; 12906af0bf9cSbellard set_EPC: 129124c7b0e3Sths if (!(env->CP0_Status & (1 << CP0St_EXL))) { 129232188a03SNathan Froyd env->CP0_EPC = exception_resume_pc(env); 1293aea14095SLeon Alrae if (update_badinstr) { 1294aea14095SLeon Alrae set_badinstr_registers(env); 1295aea14095SLeon Alrae } 12964ad40f36Sbellard if (env->hflags & MIPS_HFLAG_BMASK) { 1297f45cb2f4SPeter Maydell env->CP0_Cause |= (1U << CP0Ca_BD); 12986af0bf9cSbellard } else { 1299f45cb2f4SPeter Maydell env->CP0_Cause &= ~(1U << CP0Ca_BD); 13006af0bf9cSbellard } 130124c7b0e3Sths env->CP0_Status |= (1 << CP0St_EXL); 1302d9224450SMaciej W. Rozycki if (env->insn_flags & ISA_MIPS3) { 1303d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_64; 13042e211e0aSPhilippe Mathieu-Daudé if (!(env->insn_flags & ISA_MIPS_R6) || 13057871abb9SJames Hogan env->CP0_Status & (1 << CP0St_KX)) { 13067871abb9SJames Hogan env->hflags &= ~MIPS_HFLAG_AWRAP; 13077871abb9SJames Hogan } 1308d9224450SMaciej W. Rozycki } 1309d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_CP0; 1310623a930eSths env->hflags &= ~(MIPS_HFLAG_KSU); 131124c7b0e3Sths } 1312c53f4a62Sths env->hflags &= ~MIPS_HFLAG_BMASK; 1313aa328addSths if (env->CP0_Status & (1 << CP0St_BEV)) { 131489777fd1SLeon Alrae env->active_tc.PC = env->exception_base + 0x200; 131574dbf824SJames Hogan } else if (cause == 30 && !(env->CP0_Config3 & (1 << CP0C3_SC) && 131674dbf824SJames Hogan env->CP0_Config5 & (1 << CP0C5_CV))) { 131774dbf824SJames Hogan /* Force KSeg1 for cache errors */ 131867433345SJames Hogan env->active_tc.PC = KSEG1_BASE | (env->CP0_EBase & 0x1FFFF000); 1319aa328addSths } else { 132074dbf824SJames Hogan env->active_tc.PC = env->CP0_EBase & ~0xfff; 1321aa328addSths } 132274dbf824SJames Hogan 1323b5dc7732Sths env->active_tc.PC += offset; 1324bbfa8f72SNathan Froyd set_hflags_for_handler(env); 1325d7551eceSAleksandar Markovic env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | 1326d7551eceSAleksandar Markovic (cause << CP0Ca_EC); 13276af0bf9cSbellard break; 13286af0bf9cSbellard default: 1329c8557016SRichard Henderson abort(); 13306af0bf9cSbellard } 1331c8557016SRichard Henderson if (qemu_loglevel_mask(CPU_LOG_INT) 1332c8557016SRichard Henderson && cs->exception_index != EXCP_EXT_INTERRUPT) { 133393fcfe39Saliguori qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" 13343594c774Sths " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", 1335b5dc7732Sths __func__, env->active_tc.PC, env->CP0_EPC, cause, 13366af0bf9cSbellard env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, 13376af0bf9cSbellard env->CP0_DEPC); 13386af0bf9cSbellard } 133927103424SAndreas Färber cs->exception_index = EXCP_NONE; 13406af0bf9cSbellard } 13412ee4aed8Sbellard 13426eb66e08SPhilippe Mathieu-Daudé bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 13436eb66e08SPhilippe Mathieu-Daudé { 13446eb66e08SPhilippe Mathieu-Daudé if (interrupt_request & CPU_INTERRUPT_HARD) { 13454c44a980SPhilippe Mathieu-Daudé CPUMIPSState *env = cpu_env(cs); 13466eb66e08SPhilippe Mathieu-Daudé 13476eb66e08SPhilippe Mathieu-Daudé if (cpu_mips_hw_interrupts_enabled(env) && 13486eb66e08SPhilippe Mathieu-Daudé cpu_mips_hw_interrupts_pending(env)) { 13496eb66e08SPhilippe Mathieu-Daudé /* Raise it */ 13506eb66e08SPhilippe Mathieu-Daudé cs->exception_index = EXCP_EXT_INTERRUPT; 13516eb66e08SPhilippe Mathieu-Daudé env->error_code = 0; 13526eb66e08SPhilippe Mathieu-Daudé mips_cpu_do_interrupt(cs); 13536eb66e08SPhilippe Mathieu-Daudé return true; 13546eb66e08SPhilippe Mathieu-Daudé } 13556eb66e08SPhilippe Mathieu-Daudé } 13566eb66e08SPhilippe Mathieu-Daudé return false; 13576eb66e08SPhilippe Mathieu-Daudé } 13586eb66e08SPhilippe Mathieu-Daudé 13597db13faeSAndreas Färber void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra) 13602ee4aed8Sbellard { 13615a7330b3SRichard Henderson CPUState *cs = env_cpu(env); 1362c227f099SAnthony Liguori r4k_tlb_t *tlb; 13633b1c8be4Sths target_ulong addr; 13643b1c8be4Sths target_ulong end; 13652d72e7b0SPaul Burton uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 136699029be1SYongbok Kim uint32_t MMID = env->CP0_MemoryMapID; 136799029be1SYongbok Kim bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 136899029be1SYongbok Kim uint32_t tlb_mmid; 13693b1c8be4Sths target_ulong mask; 13702ee4aed8Sbellard 137199029be1SYongbok Kim MMID = mi ? MMID : (uint32_t) ASID; 137299029be1SYongbok Kim 1373ead9360eSths tlb = &env->tlb->mmu.r4k.tlb[idx]; 1374d7551eceSAleksandar Markovic /* 137599029be1SYongbok Kim * The qemu TLB is flushed when the ASID/MMID changes, so no need to 1376d7551eceSAleksandar Markovic * flush these entries again. 1377d7551eceSAleksandar Markovic */ 137899029be1SYongbok Kim tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 137999029be1SYongbok Kim if (tlb->G == 0 && tlb_mmid != MMID) { 13802ee4aed8Sbellard return; 13812ee4aed8Sbellard } 13822ee4aed8Sbellard 1383ead9360eSths if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) { 1384d7551eceSAleksandar Markovic /* 1385d7551eceSAleksandar Markovic * For tlbwr, we can shadow the discarded entry into 1386d7551eceSAleksandar Markovic * a new (fake) TLB entry, as long as the guest can not 1387d7551eceSAleksandar Markovic * tell that it's there. 1388d7551eceSAleksandar Markovic */ 1389ead9360eSths env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb; 1390ead9360eSths env->tlb->tlb_in_use++; 13912ee4aed8Sbellard return; 13922ee4aed8Sbellard } 13932ee4aed8Sbellard 13943b1c8be4Sths /* 1k pages are not supported. */ 1395f2e9ebefSths mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 13963b1c8be4Sths if (tlb->V0) { 1397f2e9ebefSths addr = tlb->VPN & ~mask; 1398d26bc211Sths #if defined(TARGET_MIPS64) 1399e034e2c3Sths if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { 1400100ce988Sths addr |= 0x3FFFFF0000000000ULL; 1401100ce988Sths } 1402100ce988Sths #endif 14033b1c8be4Sths end = addr | (mask >> 1); 14043b1c8be4Sths while (addr < end) { 140531b030d4SAndreas Färber tlb_flush_page(cs, addr); 14063b1c8be4Sths addr += TARGET_PAGE_SIZE; 14073b1c8be4Sths } 14083b1c8be4Sths } 14093b1c8be4Sths if (tlb->V1) { 1410f2e9ebefSths addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1); 1411d26bc211Sths #if defined(TARGET_MIPS64) 1412e034e2c3Sths if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { 1413100ce988Sths addr |= 0x3FFFFF0000000000ULL; 1414100ce988Sths } 1415100ce988Sths #endif 14163b1c8be4Sths end = addr | mask; 141753715e48Sths while (addr - 1 < end) { 141831b030d4SAndreas Färber tlb_flush_page(cs, addr); 14193b1c8be4Sths addr += TARGET_PAGE_SIZE; 14203b1c8be4Sths } 14213b1c8be4Sths } 14222ee4aed8Sbellard } 1423