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" 2463c91552SPaolo Bonzini #include "exec/exec-all.h" 25aea14095SLeon Alrae #include "exec/cpu_ldst.h" 26508127e2SPaolo Bonzini #include "exec/log.h" 27d3d93c6cSJames Hogan #include "hw/mips/cpudevs.h" 286575529bSPhilippe Mathieu-Daudé #include "exec/helper-proto.h" 296575529bSPhilippe Mathieu-Daudé 306575529bSPhilippe Mathieu-Daudé /* TLB management */ 316575529bSPhilippe Mathieu-Daudé static void r4k_mips_tlb_flush_extra(CPUMIPSState *env, int first) 326575529bSPhilippe Mathieu-Daudé { 336575529bSPhilippe Mathieu-Daudé /* Discard entries from env->tlb[first] onwards. */ 346575529bSPhilippe Mathieu-Daudé while (env->tlb->tlb_in_use > first) { 356575529bSPhilippe Mathieu-Daudé r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0); 366575529bSPhilippe Mathieu-Daudé } 376575529bSPhilippe Mathieu-Daudé } 386575529bSPhilippe Mathieu-Daudé 396575529bSPhilippe Mathieu-Daudé static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo) 406575529bSPhilippe Mathieu-Daudé { 416575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 426575529bSPhilippe Mathieu-Daudé return extract64(entrylo, 6, 54); 436575529bSPhilippe Mathieu-Daudé #else 446575529bSPhilippe Mathieu-Daudé return extract64(entrylo, 6, 24) | /* PFN */ 456575529bSPhilippe Mathieu-Daudé (extract64(entrylo, 32, 32) << 24); /* PFNX */ 466575529bSPhilippe Mathieu-Daudé #endif 476575529bSPhilippe Mathieu-Daudé } 486575529bSPhilippe Mathieu-Daudé 496575529bSPhilippe Mathieu-Daudé static void r4k_fill_tlb(CPUMIPSState *env, int idx) 506575529bSPhilippe Mathieu-Daudé { 516575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 526575529bSPhilippe Mathieu-Daudé uint64_t mask = env->CP0_PageMask >> (TARGET_PAGE_BITS + 1); 536575529bSPhilippe Mathieu-Daudé 546575529bSPhilippe Mathieu-Daudé /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */ 556575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 566575529bSPhilippe Mathieu-Daudé if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) { 576575529bSPhilippe Mathieu-Daudé tlb->EHINV = 1; 586575529bSPhilippe Mathieu-Daudé return; 596575529bSPhilippe Mathieu-Daudé } 606575529bSPhilippe Mathieu-Daudé tlb->EHINV = 0; 616575529bSPhilippe Mathieu-Daudé tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); 626575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 636575529bSPhilippe Mathieu-Daudé tlb->VPN &= env->SEGMask; 646575529bSPhilippe Mathieu-Daudé #endif 656575529bSPhilippe Mathieu-Daudé tlb->ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 666575529bSPhilippe Mathieu-Daudé tlb->MMID = env->CP0_MemoryMapID; 676575529bSPhilippe Mathieu-Daudé tlb->PageMask = env->CP0_PageMask; 686575529bSPhilippe Mathieu-Daudé tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; 696575529bSPhilippe Mathieu-Daudé tlb->V0 = (env->CP0_EntryLo0 & 2) != 0; 706575529bSPhilippe Mathieu-Daudé tlb->D0 = (env->CP0_EntryLo0 & 4) != 0; 716575529bSPhilippe Mathieu-Daudé tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7; 726575529bSPhilippe Mathieu-Daudé tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1; 736575529bSPhilippe Mathieu-Daudé tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1; 746575529bSPhilippe Mathieu-Daudé tlb->PFN[0] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) & ~mask) << 12; 756575529bSPhilippe Mathieu-Daudé tlb->V1 = (env->CP0_EntryLo1 & 2) != 0; 766575529bSPhilippe Mathieu-Daudé tlb->D1 = (env->CP0_EntryLo1 & 4) != 0; 776575529bSPhilippe Mathieu-Daudé tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7; 786575529bSPhilippe Mathieu-Daudé tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1; 796575529bSPhilippe Mathieu-Daudé tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1; 806575529bSPhilippe Mathieu-Daudé tlb->PFN[1] = (get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) & ~mask) << 12; 816575529bSPhilippe Mathieu-Daudé } 826575529bSPhilippe Mathieu-Daudé 836575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbinv(CPUMIPSState *env) 846575529bSPhilippe Mathieu-Daudé { 856575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 866575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 876575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 886575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 896575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 906575529bSPhilippe Mathieu-Daudé int idx; 916575529bSPhilippe Mathieu-Daudé 926575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 936575529bSPhilippe Mathieu-Daudé for (idx = 0; idx < env->tlb->nb_tlb; idx++) { 946575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 956575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 966575529bSPhilippe Mathieu-Daudé if (!tlb->G && tlb_mmid == MMID) { 976575529bSPhilippe Mathieu-Daudé tlb->EHINV = 1; 986575529bSPhilippe Mathieu-Daudé } 996575529bSPhilippe Mathieu-Daudé } 1006575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 1016575529bSPhilippe Mathieu-Daudé } 1026575529bSPhilippe Mathieu-Daudé 1036575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbinvf(CPUMIPSState *env) 1046575529bSPhilippe Mathieu-Daudé { 1056575529bSPhilippe Mathieu-Daudé int idx; 1066575529bSPhilippe Mathieu-Daudé 1076575529bSPhilippe Mathieu-Daudé for (idx = 0; idx < env->tlb->nb_tlb; idx++) { 1086575529bSPhilippe Mathieu-Daudé env->tlb->mmu.r4k.tlb[idx].EHINV = 1; 1096575529bSPhilippe Mathieu-Daudé } 1106575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 1116575529bSPhilippe Mathieu-Daudé } 1126575529bSPhilippe Mathieu-Daudé 1136575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbwi(CPUMIPSState *env) 1146575529bSPhilippe Mathieu-Daudé { 1156575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 1166575529bSPhilippe Mathieu-Daudé target_ulong VPN; 1176575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 1186575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 1196575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 1206575529bSPhilippe Mathieu-Daudé bool EHINV, G, V0, D0, V1, D1, XI0, XI1, RI0, RI1; 1216575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 1226575529bSPhilippe Mathieu-Daudé int idx; 1236575529bSPhilippe Mathieu-Daudé 1246575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 1256575529bSPhilippe Mathieu-Daudé 1266575529bSPhilippe Mathieu-Daudé idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; 1276575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 1286575529bSPhilippe Mathieu-Daudé VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); 1296575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 1306575529bSPhilippe Mathieu-Daudé VPN &= env->SEGMask; 1316575529bSPhilippe Mathieu-Daudé #endif 1326575529bSPhilippe Mathieu-Daudé EHINV = (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) != 0; 1336575529bSPhilippe Mathieu-Daudé G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; 1346575529bSPhilippe Mathieu-Daudé V0 = (env->CP0_EntryLo0 & 2) != 0; 1356575529bSPhilippe Mathieu-Daudé D0 = (env->CP0_EntryLo0 & 4) != 0; 1366575529bSPhilippe Mathieu-Daudé XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) &1; 1376575529bSPhilippe Mathieu-Daudé RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) &1; 1386575529bSPhilippe Mathieu-Daudé V1 = (env->CP0_EntryLo1 & 2) != 0; 1396575529bSPhilippe Mathieu-Daudé D1 = (env->CP0_EntryLo1 & 4) != 0; 1406575529bSPhilippe Mathieu-Daudé XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) &1; 1416575529bSPhilippe Mathieu-Daudé RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) &1; 1426575529bSPhilippe Mathieu-Daudé 1436575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 1446575529bSPhilippe Mathieu-Daudé /* 1456575529bSPhilippe Mathieu-Daudé * Discard cached TLB entries, unless tlbwi is just upgrading access 1466575529bSPhilippe Mathieu-Daudé * permissions on the current entry. 1476575529bSPhilippe Mathieu-Daudé */ 1486575529bSPhilippe Mathieu-Daudé if (tlb->VPN != VPN || tlb_mmid != MMID || tlb->G != G || 1496575529bSPhilippe Mathieu-Daudé (!tlb->EHINV && EHINV) || 1506575529bSPhilippe Mathieu-Daudé (tlb->V0 && !V0) || (tlb->D0 && !D0) || 1516575529bSPhilippe Mathieu-Daudé (!tlb->XI0 && XI0) || (!tlb->RI0 && RI0) || 1526575529bSPhilippe Mathieu-Daudé (tlb->V1 && !V1) || (tlb->D1 && !D1) || 1536575529bSPhilippe Mathieu-Daudé (!tlb->XI1 && XI1) || (!tlb->RI1 && RI1)) { 1546575529bSPhilippe Mathieu-Daudé r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); 1556575529bSPhilippe Mathieu-Daudé } 1566575529bSPhilippe Mathieu-Daudé 1576575529bSPhilippe Mathieu-Daudé r4k_invalidate_tlb(env, idx, 0); 1586575529bSPhilippe Mathieu-Daudé r4k_fill_tlb(env, idx); 1596575529bSPhilippe Mathieu-Daudé } 1606575529bSPhilippe Mathieu-Daudé 1616575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbwr(CPUMIPSState *env) 1626575529bSPhilippe Mathieu-Daudé { 1636575529bSPhilippe Mathieu-Daudé int r = cpu_mips_get_random(env); 1646575529bSPhilippe Mathieu-Daudé 1656575529bSPhilippe Mathieu-Daudé r4k_invalidate_tlb(env, r, 1); 1666575529bSPhilippe Mathieu-Daudé r4k_fill_tlb(env, r); 1676575529bSPhilippe Mathieu-Daudé } 1686575529bSPhilippe Mathieu-Daudé 1696575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbp(CPUMIPSState *env) 1706575529bSPhilippe Mathieu-Daudé { 1716575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 1726575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 1736575529bSPhilippe Mathieu-Daudé target_ulong mask; 1746575529bSPhilippe Mathieu-Daudé target_ulong tag; 1756575529bSPhilippe Mathieu-Daudé target_ulong VPN; 1766575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 1776575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 1786575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 1796575529bSPhilippe Mathieu-Daudé int i; 1806575529bSPhilippe Mathieu-Daudé 1816575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 1826575529bSPhilippe Mathieu-Daudé for (i = 0; i < env->tlb->nb_tlb; i++) { 1836575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[i]; 1846575529bSPhilippe Mathieu-Daudé /* 1k pages are not supported. */ 1856575529bSPhilippe Mathieu-Daudé mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 1866575529bSPhilippe Mathieu-Daudé tag = env->CP0_EntryHi & ~mask; 1876575529bSPhilippe Mathieu-Daudé VPN = tlb->VPN & ~mask; 1886575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 1896575529bSPhilippe Mathieu-Daudé tag &= env->SEGMask; 1906575529bSPhilippe Mathieu-Daudé #endif 1916575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 1926575529bSPhilippe Mathieu-Daudé /* Check ASID/MMID, virtual page number & size */ 1936575529bSPhilippe Mathieu-Daudé if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { 1946575529bSPhilippe Mathieu-Daudé /* TLB match */ 1956575529bSPhilippe Mathieu-Daudé env->CP0_Index = i; 1966575529bSPhilippe Mathieu-Daudé break; 1976575529bSPhilippe Mathieu-Daudé } 1986575529bSPhilippe Mathieu-Daudé } 1996575529bSPhilippe Mathieu-Daudé if (i == env->tlb->nb_tlb) { 2006575529bSPhilippe Mathieu-Daudé /* No match. Discard any shadow entries, if any of them match. */ 2016575529bSPhilippe Mathieu-Daudé for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) { 2026575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[i]; 2036575529bSPhilippe Mathieu-Daudé /* 1k pages are not supported. */ 2046575529bSPhilippe Mathieu-Daudé mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 2056575529bSPhilippe Mathieu-Daudé tag = env->CP0_EntryHi & ~mask; 2066575529bSPhilippe Mathieu-Daudé VPN = tlb->VPN & ~mask; 2076575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 2086575529bSPhilippe Mathieu-Daudé tag &= env->SEGMask; 2096575529bSPhilippe Mathieu-Daudé #endif 2106575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 2116575529bSPhilippe Mathieu-Daudé /* Check ASID/MMID, virtual page number & size */ 2126575529bSPhilippe Mathieu-Daudé if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag) { 2136575529bSPhilippe Mathieu-Daudé r4k_mips_tlb_flush_extra(env, i); 2146575529bSPhilippe Mathieu-Daudé break; 2156575529bSPhilippe Mathieu-Daudé } 2166575529bSPhilippe Mathieu-Daudé } 2176575529bSPhilippe Mathieu-Daudé 2186575529bSPhilippe Mathieu-Daudé env->CP0_Index |= 0x80000000; 2196575529bSPhilippe Mathieu-Daudé } 2206575529bSPhilippe Mathieu-Daudé } 2216575529bSPhilippe Mathieu-Daudé 2226575529bSPhilippe Mathieu-Daudé static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn) 2236575529bSPhilippe Mathieu-Daudé { 2246575529bSPhilippe Mathieu-Daudé #if defined(TARGET_MIPS64) 2256575529bSPhilippe Mathieu-Daudé return tlb_pfn << 6; 2266575529bSPhilippe Mathieu-Daudé #else 2276575529bSPhilippe Mathieu-Daudé return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */ 2286575529bSPhilippe Mathieu-Daudé (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */ 2296575529bSPhilippe Mathieu-Daudé #endif 2306575529bSPhilippe Mathieu-Daudé } 2316575529bSPhilippe Mathieu-Daudé 2326575529bSPhilippe Mathieu-Daudé static void r4k_helper_tlbr(CPUMIPSState *env) 2336575529bSPhilippe Mathieu-Daudé { 2346575529bSPhilippe Mathieu-Daudé bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 2356575529bSPhilippe Mathieu-Daudé uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 2366575529bSPhilippe Mathieu-Daudé uint32_t MMID = env->CP0_MemoryMapID; 2376575529bSPhilippe Mathieu-Daudé uint32_t tlb_mmid; 2386575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 2396575529bSPhilippe Mathieu-Daudé int idx; 2406575529bSPhilippe Mathieu-Daudé 2416575529bSPhilippe Mathieu-Daudé MMID = mi ? MMID : (uint32_t) ASID; 2426575529bSPhilippe Mathieu-Daudé idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; 2436575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 2446575529bSPhilippe Mathieu-Daudé 2456575529bSPhilippe Mathieu-Daudé tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 2466575529bSPhilippe Mathieu-Daudé /* If this will change the current ASID/MMID, flush qemu's TLB. */ 2476575529bSPhilippe Mathieu-Daudé if (MMID != tlb_mmid) { 2486575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 2496575529bSPhilippe Mathieu-Daudé } 2506575529bSPhilippe Mathieu-Daudé 2516575529bSPhilippe Mathieu-Daudé r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); 2526575529bSPhilippe Mathieu-Daudé 2536575529bSPhilippe Mathieu-Daudé if (tlb->EHINV) { 2546575529bSPhilippe Mathieu-Daudé env->CP0_EntryHi = 1 << CP0EnHi_EHINV; 2556575529bSPhilippe Mathieu-Daudé env->CP0_PageMask = 0; 2566575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo0 = 0; 2576575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo1 = 0; 2586575529bSPhilippe Mathieu-Daudé } else { 2596575529bSPhilippe Mathieu-Daudé env->CP0_EntryHi = mi ? tlb->VPN : tlb->VPN | tlb->ASID; 2606575529bSPhilippe Mathieu-Daudé env->CP0_MemoryMapID = tlb->MMID; 2616575529bSPhilippe Mathieu-Daudé env->CP0_PageMask = tlb->PageMask; 2626575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | 2636575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->RI0 << CP0EnLo_RI) | 2646575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) | 2656575529bSPhilippe Mathieu-Daudé get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12); 2666575529bSPhilippe Mathieu-Daudé env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | 2676575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->RI1 << CP0EnLo_RI) | 2686575529bSPhilippe Mathieu-Daudé ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) | 2696575529bSPhilippe Mathieu-Daudé get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12); 2706575529bSPhilippe Mathieu-Daudé } 2716575529bSPhilippe Mathieu-Daudé } 2726575529bSPhilippe Mathieu-Daudé 2736575529bSPhilippe Mathieu-Daudé void helper_tlbwi(CPUMIPSState *env) 2746575529bSPhilippe Mathieu-Daudé { 2756575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbwi(env); 2766575529bSPhilippe Mathieu-Daudé } 2776575529bSPhilippe Mathieu-Daudé 2786575529bSPhilippe Mathieu-Daudé void helper_tlbwr(CPUMIPSState *env) 2796575529bSPhilippe Mathieu-Daudé { 2806575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbwr(env); 2816575529bSPhilippe Mathieu-Daudé } 2826575529bSPhilippe Mathieu-Daudé 2836575529bSPhilippe Mathieu-Daudé void helper_tlbp(CPUMIPSState *env) 2846575529bSPhilippe Mathieu-Daudé { 2856575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbp(env); 2866575529bSPhilippe Mathieu-Daudé } 2876575529bSPhilippe Mathieu-Daudé 2886575529bSPhilippe Mathieu-Daudé void helper_tlbr(CPUMIPSState *env) 2896575529bSPhilippe Mathieu-Daudé { 2906575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbr(env); 2916575529bSPhilippe Mathieu-Daudé } 2926575529bSPhilippe Mathieu-Daudé 2936575529bSPhilippe Mathieu-Daudé void helper_tlbinv(CPUMIPSState *env) 2946575529bSPhilippe Mathieu-Daudé { 2956575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbinv(env); 2966575529bSPhilippe Mathieu-Daudé } 2976575529bSPhilippe Mathieu-Daudé 2986575529bSPhilippe Mathieu-Daudé void helper_tlbinvf(CPUMIPSState *env) 2996575529bSPhilippe Mathieu-Daudé { 3006575529bSPhilippe Mathieu-Daudé env->tlb->helper_tlbinvf(env); 3016575529bSPhilippe Mathieu-Daudé } 3026575529bSPhilippe Mathieu-Daudé 3036575529bSPhilippe Mathieu-Daudé static void global_invalidate_tlb(CPUMIPSState *env, 3046575529bSPhilippe Mathieu-Daudé uint32_t invMsgVPN2, 3056575529bSPhilippe Mathieu-Daudé uint8_t invMsgR, 3066575529bSPhilippe Mathieu-Daudé uint32_t invMsgMMid, 3076575529bSPhilippe Mathieu-Daudé bool invAll, 3086575529bSPhilippe Mathieu-Daudé bool invVAMMid, 3096575529bSPhilippe Mathieu-Daudé bool invMMid, 3106575529bSPhilippe Mathieu-Daudé bool invVA) 3116575529bSPhilippe Mathieu-Daudé { 3126575529bSPhilippe Mathieu-Daudé 3136575529bSPhilippe Mathieu-Daudé int idx; 3146575529bSPhilippe Mathieu-Daudé r4k_tlb_t *tlb; 3156575529bSPhilippe Mathieu-Daudé bool VAMatch; 3166575529bSPhilippe Mathieu-Daudé bool MMidMatch; 3176575529bSPhilippe Mathieu-Daudé 3186575529bSPhilippe Mathieu-Daudé for (idx = 0; idx < env->tlb->nb_tlb; idx++) { 3196575529bSPhilippe Mathieu-Daudé tlb = &env->tlb->mmu.r4k.tlb[idx]; 3206575529bSPhilippe Mathieu-Daudé VAMatch = 3216575529bSPhilippe Mathieu-Daudé (((tlb->VPN & ~tlb->PageMask) == (invMsgVPN2 & ~tlb->PageMask)) 3226575529bSPhilippe Mathieu-Daudé #ifdef TARGET_MIPS64 3236575529bSPhilippe Mathieu-Daudé && 3246575529bSPhilippe Mathieu-Daudé (extract64(env->CP0_EntryHi, 62, 2) == invMsgR) 3256575529bSPhilippe Mathieu-Daudé #endif 3266575529bSPhilippe Mathieu-Daudé ); 3276575529bSPhilippe Mathieu-Daudé MMidMatch = tlb->MMID == invMsgMMid; 3286575529bSPhilippe Mathieu-Daudé if ((invAll && (idx > env->CP0_Wired)) || 3296575529bSPhilippe Mathieu-Daudé (VAMatch && invVAMMid && (tlb->G || MMidMatch)) || 3306575529bSPhilippe Mathieu-Daudé (VAMatch && invVA) || 3316575529bSPhilippe Mathieu-Daudé (MMidMatch && !(tlb->G) && invMMid)) { 3326575529bSPhilippe Mathieu-Daudé tlb->EHINV = 1; 3336575529bSPhilippe Mathieu-Daudé } 3346575529bSPhilippe Mathieu-Daudé } 3356575529bSPhilippe Mathieu-Daudé cpu_mips_tlb_flush(env); 3366575529bSPhilippe Mathieu-Daudé } 3376575529bSPhilippe Mathieu-Daudé 3386575529bSPhilippe Mathieu-Daudé void helper_ginvt(CPUMIPSState *env, target_ulong arg, uint32_t type) 3396575529bSPhilippe Mathieu-Daudé { 3406575529bSPhilippe Mathieu-Daudé bool invAll = type == 0; 3416575529bSPhilippe Mathieu-Daudé bool invVA = type == 1; 3426575529bSPhilippe Mathieu-Daudé bool invMMid = type == 2; 3436575529bSPhilippe Mathieu-Daudé bool invVAMMid = type == 3; 3446575529bSPhilippe Mathieu-Daudé uint32_t invMsgVPN2 = arg & (TARGET_PAGE_MASK << 1); 3456575529bSPhilippe Mathieu-Daudé uint8_t invMsgR = 0; 3466575529bSPhilippe Mathieu-Daudé uint32_t invMsgMMid = env->CP0_MemoryMapID; 3476575529bSPhilippe Mathieu-Daudé CPUState *other_cs = first_cpu; 3486575529bSPhilippe Mathieu-Daudé 3496575529bSPhilippe Mathieu-Daudé #ifdef TARGET_MIPS64 3506575529bSPhilippe Mathieu-Daudé invMsgR = extract64(arg, 62, 2); 3516575529bSPhilippe Mathieu-Daudé #endif 3526575529bSPhilippe Mathieu-Daudé 3536575529bSPhilippe Mathieu-Daudé CPU_FOREACH(other_cs) { 3546575529bSPhilippe Mathieu-Daudé MIPSCPU *other_cpu = MIPS_CPU(other_cs); 3556575529bSPhilippe Mathieu-Daudé global_invalidate_tlb(&other_cpu->env, invMsgVPN2, invMsgR, invMsgMMid, 3566575529bSPhilippe Mathieu-Daudé invAll, invVAMMid, invMMid, invVA); 3576575529bSPhilippe Mathieu-Daudé } 3586575529bSPhilippe Mathieu-Daudé } 3596af0bf9cSbellard 36029929e34Sths /* no MMU emulation */ 361f3185ec2SPhilippe Mathieu-Daudé static int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, 362edbd4992SPhilippe Mathieu-Daudé target_ulong address, MMUAccessType access_type) 36329929e34Sths { 36429929e34Sths *physical = address; 3657353113fSJakub Jermář *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 36629929e34Sths return TLBRET_MATCH; 36729929e34Sths } 36829929e34Sths 36929929e34Sths /* fixed mapping MMU emulation */ 370f3185ec2SPhilippe Mathieu-Daudé static int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, 371f3185ec2SPhilippe Mathieu-Daudé int *prot, target_ulong address, 372f3185ec2SPhilippe Mathieu-Daudé MMUAccessType access_type) 37329929e34Sths { 37429929e34Sths if (address <= (int32_t)0x7FFFFFFFUL) { 375d7551eceSAleksandar Markovic if (!(env->CP0_Status & (1 << CP0St_ERL))) { 37629929e34Sths *physical = address + 0x40000000UL; 377d7551eceSAleksandar Markovic } else { 37829929e34Sths *physical = address; 379d7551eceSAleksandar Markovic } 380d7551eceSAleksandar Markovic } else if (address <= (int32_t)0xBFFFFFFFUL) { 38129929e34Sths *physical = address & 0x1FFFFFFF; 382d7551eceSAleksandar Markovic } else { 38329929e34Sths *physical = address; 384d7551eceSAleksandar Markovic } 38529929e34Sths 3867353113fSJakub Jermář *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; 38729929e34Sths return TLBRET_MATCH; 38829929e34Sths } 38929929e34Sths 39029929e34Sths /* MIPS32/MIPS64 R4000-style MMU emulation */ 391f3185ec2SPhilippe Mathieu-Daudé static int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot, 392edbd4992SPhilippe Mathieu-Daudé target_ulong address, MMUAccessType access_type) 3936af0bf9cSbellard { 3942d72e7b0SPaul Burton uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 39599029be1SYongbok Kim uint32_t MMID = env->CP0_MemoryMapID; 39699029be1SYongbok Kim bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 39799029be1SYongbok Kim uint32_t tlb_mmid; 3983b1c8be4Sths int i; 3996af0bf9cSbellard 40099029be1SYongbok Kim MMID = mi ? MMID : (uint32_t) ASID; 40199029be1SYongbok Kim 402ead9360eSths for (i = 0; i < env->tlb->tlb_in_use; i++) { 403c227f099SAnthony Liguori r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; 4043b1c8be4Sths /* 1k pages are not supported. */ 405f2e9ebefSths target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 4063b1c8be4Sths target_ulong tag = address & ~mask; 407f2e9ebefSths target_ulong VPN = tlb->VPN & ~mask; 408d26bc211Sths #if defined(TARGET_MIPS64) 409e034e2c3Sths tag &= env->SEGMask; 410100ce988Sths #endif 4113b1c8be4Sths 41299029be1SYongbok Kim /* Check ASID/MMID, virtual page number & size */ 41399029be1SYongbok Kim tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 41499029be1SYongbok Kim if ((tlb->G == 1 || tlb_mmid == MMID) && VPN == tag && !tlb->EHINV) { 4156af0bf9cSbellard /* TLB match */ 416f2e9ebefSths int n = !!(address & mask & ~(mask >> 1)); 4176af0bf9cSbellard /* Check access rights */ 4182fb58b73SLeon Alrae if (!(n ? tlb->V1 : tlb->V0)) { 41943057ab1Sbellard return TLBRET_INVALID; 4202fb58b73SLeon Alrae } 421edbd4992SPhilippe Mathieu-Daudé if (access_type == MMU_INST_FETCH && (n ? tlb->XI1 : tlb->XI0)) { 4222fb58b73SLeon Alrae return TLBRET_XI; 4232fb58b73SLeon Alrae } 424edbd4992SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_LOAD && (n ? tlb->RI1 : tlb->RI0)) { 4252fb58b73SLeon Alrae return TLBRET_RI; 4262fb58b73SLeon Alrae } 427edbd4992SPhilippe Mathieu-Daudé if (access_type != MMU_DATA_STORE || (n ? tlb->D1 : tlb->D0)) { 4283b1c8be4Sths *physical = tlb->PFN[n] | (address & (mask >> 1)); 4299fb63ac2Sbellard *prot = PAGE_READ; 430d7551eceSAleksandar Markovic if (n ? tlb->D1 : tlb->D0) { 4319fb63ac2Sbellard *prot |= PAGE_WRITE; 432d7551eceSAleksandar Markovic } 4337353113fSJakub Jermář if (!(n ? tlb->XI1 : tlb->XI0)) { 4347353113fSJakub Jermář *prot |= PAGE_EXEC; 4357353113fSJakub Jermář } 43643057ab1Sbellard return TLBRET_MATCH; 4376af0bf9cSbellard } 43843057ab1Sbellard return TLBRET_DIRTY; 4396af0bf9cSbellard } 4406af0bf9cSbellard } 44143057ab1Sbellard return TLBRET_NOMATCH; 4426af0bf9cSbellard } 4436af0bf9cSbellard 444f2c5b39eSPhilippe Mathieu-Daudé static void no_mmu_init(CPUMIPSState *env, const mips_def_t *def) 445f2c5b39eSPhilippe Mathieu-Daudé { 446f2c5b39eSPhilippe Mathieu-Daudé env->tlb->nb_tlb = 1; 447f2c5b39eSPhilippe Mathieu-Daudé env->tlb->map_address = &no_mmu_map_address; 448f2c5b39eSPhilippe Mathieu-Daudé } 449f2c5b39eSPhilippe Mathieu-Daudé 450f2c5b39eSPhilippe Mathieu-Daudé static void fixed_mmu_init(CPUMIPSState *env, const mips_def_t *def) 451f2c5b39eSPhilippe Mathieu-Daudé { 452f2c5b39eSPhilippe Mathieu-Daudé env->tlb->nb_tlb = 1; 453f2c5b39eSPhilippe Mathieu-Daudé env->tlb->map_address = &fixed_mmu_map_address; 454f2c5b39eSPhilippe Mathieu-Daudé } 455f2c5b39eSPhilippe Mathieu-Daudé 456f2c5b39eSPhilippe Mathieu-Daudé static void r4k_mmu_init(CPUMIPSState *env, const mips_def_t *def) 457f2c5b39eSPhilippe Mathieu-Daudé { 458f2c5b39eSPhilippe Mathieu-Daudé env->tlb->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63); 459f2c5b39eSPhilippe Mathieu-Daudé env->tlb->map_address = &r4k_map_address; 460f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbwi = r4k_helper_tlbwi; 461f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbwr = r4k_helper_tlbwr; 462f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbp = r4k_helper_tlbp; 463f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbr = r4k_helper_tlbr; 464f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbinv = r4k_helper_tlbinv; 465f2c5b39eSPhilippe Mathieu-Daudé env->tlb->helper_tlbinvf = r4k_helper_tlbinvf; 466f2c5b39eSPhilippe Mathieu-Daudé } 467f2c5b39eSPhilippe Mathieu-Daudé 468f2c5b39eSPhilippe Mathieu-Daudé void mmu_init(CPUMIPSState *env, const mips_def_t *def) 469f2c5b39eSPhilippe Mathieu-Daudé { 470f2c5b39eSPhilippe Mathieu-Daudé env->tlb = g_malloc0(sizeof(CPUMIPSTLBContext)); 471f2c5b39eSPhilippe Mathieu-Daudé 472f2c5b39eSPhilippe Mathieu-Daudé switch (def->mmu_type) { 473f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_NONE: 474f2c5b39eSPhilippe Mathieu-Daudé no_mmu_init(env, def); 475f2c5b39eSPhilippe Mathieu-Daudé break; 476f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R4000: 477f2c5b39eSPhilippe Mathieu-Daudé r4k_mmu_init(env, def); 478f2c5b39eSPhilippe Mathieu-Daudé break; 479f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_FMT: 480f2c5b39eSPhilippe Mathieu-Daudé fixed_mmu_init(env, def); 481f2c5b39eSPhilippe Mathieu-Daudé break; 482f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R3000: 483f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R6000: 484f2c5b39eSPhilippe Mathieu-Daudé case MMU_TYPE_R8000: 485f2c5b39eSPhilippe Mathieu-Daudé default: 486f2c5b39eSPhilippe Mathieu-Daudé cpu_abort(env_cpu(env), "MMU type not supported\n"); 487f2c5b39eSPhilippe Mathieu-Daudé } 488f2c5b39eSPhilippe Mathieu-Daudé } 489f2c5b39eSPhilippe Mathieu-Daudé 490d10eb08fSAlex Bennée void cpu_mips_tlb_flush(CPUMIPSState *env) 491e6623d88SPaolo Bonzini { 492e6623d88SPaolo Bonzini /* Flush qemu's TLB and discard all shadowed entries. */ 4935a7330b3SRichard Henderson tlb_flush(env_cpu(env)); 494e6623d88SPaolo Bonzini env->tlb->tlb_in_use = env->tlb->nb_tlb; 495e6623d88SPaolo Bonzini } 496e6623d88SPaolo Bonzini 4977db13faeSAndreas Färber static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, 498ca354f00SPhilippe Mathieu-Daudé MMUAccessType access_type, int tlb_error) 4996af0bf9cSbellard { 5005a7330b3SRichard Henderson CPUState *cs = env_cpu(env); 5016af0bf9cSbellard int exception = 0, error_code = 0; 5026af0bf9cSbellard 503ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_INST_FETCH) { 504aea14095SLeon Alrae error_code |= EXCP_INST_NOTAVAIL; 505aea14095SLeon Alrae } 506aea14095SLeon Alrae 5071147e189SAurelien Jarno switch (tlb_error) { 5086af0bf9cSbellard default: 50943057ab1Sbellard case TLBRET_BADADDR: 5106af0bf9cSbellard /* Reference to kernel address from user mode or supervisor mode */ 5116af0bf9cSbellard /* Reference to supervisor address from user mode */ 512ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_STORE) { 5136af0bf9cSbellard exception = EXCP_AdES; 5149f6bcedbSLeon Alrae } else { 5156af0bf9cSbellard exception = EXCP_AdEL; 5169f6bcedbSLeon Alrae } 5176af0bf9cSbellard break; 51843057ab1Sbellard case TLBRET_NOMATCH: 5196af0bf9cSbellard /* No TLB match for a mapped address */ 520ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_STORE) { 5216af0bf9cSbellard exception = EXCP_TLBS; 5229f6bcedbSLeon Alrae } else { 5236af0bf9cSbellard exception = EXCP_TLBL; 5249f6bcedbSLeon Alrae } 525aea14095SLeon Alrae error_code |= EXCP_TLB_NOMATCH; 5266af0bf9cSbellard break; 52743057ab1Sbellard case TLBRET_INVALID: 5286af0bf9cSbellard /* TLB match with no valid bit */ 529ca354f00SPhilippe Mathieu-Daudé if (access_type == MMU_DATA_STORE) { 5306af0bf9cSbellard exception = EXCP_TLBS; 5319f6bcedbSLeon Alrae } else { 5326af0bf9cSbellard exception = EXCP_TLBL; 5339f6bcedbSLeon Alrae } 5346af0bf9cSbellard break; 53543057ab1Sbellard case TLBRET_DIRTY: 5366af0bf9cSbellard /* TLB match but 'D' bit is cleared */ 5376af0bf9cSbellard exception = EXCP_LTLBL; 5386af0bf9cSbellard break; 53992ceb440SLeon Alrae case TLBRET_XI: 54092ceb440SLeon Alrae /* Execute-Inhibit Exception */ 54192ceb440SLeon Alrae if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { 54292ceb440SLeon Alrae exception = EXCP_TLBXI; 54392ceb440SLeon Alrae } else { 54492ceb440SLeon Alrae exception = EXCP_TLBL; 54592ceb440SLeon Alrae } 54692ceb440SLeon Alrae break; 54792ceb440SLeon Alrae case TLBRET_RI: 54892ceb440SLeon Alrae /* Read-Inhibit Exception */ 54992ceb440SLeon Alrae if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { 55092ceb440SLeon Alrae exception = EXCP_TLBRI; 55192ceb440SLeon Alrae } else { 55292ceb440SLeon Alrae exception = EXCP_TLBL; 55392ceb440SLeon Alrae } 55492ceb440SLeon Alrae break; 5556af0bf9cSbellard } 5566af0bf9cSbellard /* Raise exception */ 557e807bcc1SYongbok Kim if (!(env->hflags & MIPS_HFLAG_DM)) { 5586af0bf9cSbellard env->CP0_BadVAddr = address; 559e807bcc1SYongbok Kim } 560100ce988Sths env->CP0_Context = (env->CP0_Context & ~0x007fffff) | 5614ad40f36Sbellard ((address >> 9) & 0x007ffff0); 5626ec98bd7SPaul Burton env->CP0_EntryHi = (env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask) | 563701074a6SLeon Alrae (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) | 5646ec98bd7SPaul Burton (address & (TARGET_PAGE_MASK << 1)); 565d26bc211Sths #if defined(TARGET_MIPS64) 566e034e2c3Sths env->CP0_EntryHi &= env->SEGMask; 56760270f85SYongbok Kim env->CP0_XContext = 568d7551eceSAleksandar Markovic (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | /* PTEBase */ 569d7551eceSAleksandar Markovic (extract64(address, 62, 2) << (env->SEGBITS - 9)) | /* R */ 570d7551eceSAleksandar Markovic (extract64(address, 13, env->SEGBITS - 13) << 4); /* BadVPN2 */ 571100ce988Sths #endif 57227103424SAndreas Färber cs->exception_index = exception; 5736af0bf9cSbellard env->error_code = error_code; 5741147e189SAurelien Jarno } 5751147e189SAurelien Jarno 576074cfcb4SYongbok Kim #if !defined(TARGET_MIPS64) 577074cfcb4SYongbok Kim 578074cfcb4SYongbok Kim /* 579074cfcb4SYongbok Kim * Perform hardware page table walk 580074cfcb4SYongbok Kim * 581074cfcb4SYongbok Kim * Memory accesses are performed using the KERNEL privilege level. 582074cfcb4SYongbok Kim * Synchronous exceptions detected on memory accesses cause a silent exit 583074cfcb4SYongbok Kim * from page table walking, resulting in a TLB or XTLB Refill exception. 584074cfcb4SYongbok Kim * 585074cfcb4SYongbok Kim * Implementations are not required to support page table walk memory 586074cfcb4SYongbok Kim * accesses from mapped memory regions. When an unsupported access is 587074cfcb4SYongbok Kim * attempted, a silent exit is taken, resulting in a TLB or XTLB Refill 588074cfcb4SYongbok Kim * exception. 589074cfcb4SYongbok Kim * 590074cfcb4SYongbok Kim * Note that if an exception is caused by AddressTranslation or LoadMemory 591074cfcb4SYongbok Kim * functions, the exception is not taken, a silent exit is taken, 592074cfcb4SYongbok Kim * resulting in a TLB or XTLB Refill exception. 593074cfcb4SYongbok Kim */ 594074cfcb4SYongbok Kim 595074cfcb4SYongbok Kim static bool get_pte(CPUMIPSState *env, uint64_t vaddr, int entry_size, 596074cfcb4SYongbok Kim uint64_t *pte) 597074cfcb4SYongbok Kim { 598074cfcb4SYongbok Kim if ((vaddr & ((entry_size >> 3) - 1)) != 0) { 599074cfcb4SYongbok Kim return false; 600074cfcb4SYongbok Kim } 601074cfcb4SYongbok Kim if (entry_size == 64) { 602074cfcb4SYongbok Kim *pte = cpu_ldq_code(env, vaddr); 603074cfcb4SYongbok Kim } else { 604074cfcb4SYongbok Kim *pte = cpu_ldl_code(env, vaddr); 605074cfcb4SYongbok Kim } 606074cfcb4SYongbok Kim return true; 607074cfcb4SYongbok Kim } 608074cfcb4SYongbok Kim 609074cfcb4SYongbok Kim static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, 610074cfcb4SYongbok Kim int entry_size, int ptei) 611074cfcb4SYongbok Kim { 612074cfcb4SYongbok Kim uint64_t result = entry; 613074cfcb4SYongbok Kim uint64_t rixi; 614074cfcb4SYongbok Kim if (ptei > entry_size) { 615074cfcb4SYongbok Kim ptei -= 32; 616074cfcb4SYongbok Kim } 617074cfcb4SYongbok Kim result >>= (ptei - 2); 618074cfcb4SYongbok Kim rixi = result & 3; 619074cfcb4SYongbok Kim result >>= 2; 620074cfcb4SYongbok Kim result |= rixi << CP0EnLo_XI; 621074cfcb4SYongbok Kim return result; 622074cfcb4SYongbok Kim } 623074cfcb4SYongbok Kim 624074cfcb4SYongbok Kim static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, 625074cfcb4SYongbok Kim int directory_index, bool *huge_page, bool *hgpg_directory_hit, 626074cfcb4SYongbok Kim uint64_t *pw_entrylo0, uint64_t *pw_entrylo1) 627074cfcb4SYongbok Kim { 628074cfcb4SYongbok Kim int dph = (env->CP0_PWCtl >> CP0PC_DPH) & 0x1; 629074cfcb4SYongbok Kim int psn = (env->CP0_PWCtl >> CP0PC_PSN) & 0x3F; 630074cfcb4SYongbok Kim int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; 631074cfcb4SYongbok Kim int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; 632074cfcb4SYongbok Kim int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; 633074cfcb4SYongbok Kim int native_shift = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? 2 : 3; 634074cfcb4SYongbok Kim int directory_shift = (ptew > 1) ? -1 : 635074cfcb4SYongbok Kim (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; 636074cfcb4SYongbok Kim int leaf_shift = (ptew > 1) ? -1 : 637074cfcb4SYongbok Kim (ptew == 1) ? native_shift + 1 : native_shift; 638074cfcb4SYongbok Kim uint32_t direntry_size = 1 << (directory_shift + 3); 639074cfcb4SYongbok Kim uint32_t leafentry_size = 1 << (leaf_shift + 3); 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, 647935c1034SPhilippe Mathieu-Daudé cpu_mmu_index(env, false)) != 648074cfcb4SYongbok Kim TLBRET_MATCH) { 649074cfcb4SYongbok Kim /* wrong base address */ 650074cfcb4SYongbok Kim return 0; 651074cfcb4SYongbok Kim } 652074cfcb4SYongbok Kim if (!get_pte(env, *vaddr, direntry_size, &entry)) { 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; 659074cfcb4SYongbok Kim entry = get_tlb_entry_layout(env, entry, leafentry_size, 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) { 667074cfcb4SYongbok Kim int oddpagebit = 1 << leaf_shift; 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, 675935c1034SPhilippe Mathieu-Daudé cpu_mmu_index(env, false)) != 676074cfcb4SYongbok Kim TLBRET_MATCH) { 677074cfcb4SYongbok Kim return 0; 678074cfcb4SYongbok Kim } 679074cfcb4SYongbok Kim if (!get_pte(env, vaddr2, leafentry_size, &entry)) { 680074cfcb4SYongbok Kim return 0; 681074cfcb4SYongbok Kim } 682074cfcb4SYongbok Kim entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); 683074cfcb4SYongbok Kim if (*vaddr & oddpagebit) { 684074cfcb4SYongbok Kim *pw_entrylo0 = entry; 685074cfcb4SYongbok Kim } else { 686074cfcb4SYongbok Kim *pw_entrylo1 = entry; 687074cfcb4SYongbok Kim } 688074cfcb4SYongbok Kim } else { 689074cfcb4SYongbok Kim return 0; 690074cfcb4SYongbok Kim } 691074cfcb4SYongbok Kim return 1; 692074cfcb4SYongbok Kim } else { 693074cfcb4SYongbok Kim *vaddr = entry; 694074cfcb4SYongbok Kim return 2; 695074cfcb4SYongbok Kim } 696074cfcb4SYongbok Kim } 697074cfcb4SYongbok Kim 698bca3763bSPhilippe Mathieu-Daudé static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, 699074cfcb4SYongbok Kim int mmu_idx) 700074cfcb4SYongbok Kim { 701074cfcb4SYongbok Kim int gdw = (env->CP0_PWSize >> CP0PS_GDW) & 0x3F; 702074cfcb4SYongbok Kim int udw = (env->CP0_PWSize >> CP0PS_UDW) & 0x3F; 703074cfcb4SYongbok Kim int mdw = (env->CP0_PWSize >> CP0PS_MDW) & 0x3F; 704074cfcb4SYongbok Kim int ptw = (env->CP0_PWSize >> CP0PS_PTW) & 0x3F; 705074cfcb4SYongbok Kim int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; 706074cfcb4SYongbok Kim 707074cfcb4SYongbok Kim /* Initial values */ 708074cfcb4SYongbok Kim bool huge_page = false; 709074cfcb4SYongbok Kim bool hgpg_bdhit = false; 710074cfcb4SYongbok Kim bool hgpg_gdhit = false; 711074cfcb4SYongbok Kim bool hgpg_udhit = false; 712074cfcb4SYongbok Kim bool hgpg_mdhit = false; 713074cfcb4SYongbok Kim 714074cfcb4SYongbok Kim int32_t pw_pagemask = 0; 715074cfcb4SYongbok Kim target_ulong pw_entryhi = 0; 716074cfcb4SYongbok Kim uint64_t pw_entrylo0 = 0; 717074cfcb4SYongbok Kim uint64_t pw_entrylo1 = 0; 718074cfcb4SYongbok Kim 719074cfcb4SYongbok Kim /* Native pointer size */ 720074cfcb4SYongbok Kim /*For the 32-bit architectures, this bit is fixed to 0.*/ 721074cfcb4SYongbok Kim int native_shift = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? 2 : 3; 722074cfcb4SYongbok Kim 723074cfcb4SYongbok Kim /* Indices from PWField */ 724074cfcb4SYongbok Kim int pf_gdw = (env->CP0_PWField >> CP0PF_GDW) & 0x3F; 725074cfcb4SYongbok Kim int pf_udw = (env->CP0_PWField >> CP0PF_UDW) & 0x3F; 726074cfcb4SYongbok Kim int pf_mdw = (env->CP0_PWField >> CP0PF_MDW) & 0x3F; 727074cfcb4SYongbok Kim int pf_ptw = (env->CP0_PWField >> CP0PF_PTW) & 0x3F; 728074cfcb4SYongbok Kim int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; 729074cfcb4SYongbok Kim 730074cfcb4SYongbok Kim /* Indices computed from faulting address */ 731074cfcb4SYongbok Kim int gindex = (address >> pf_gdw) & ((1 << gdw) - 1); 732074cfcb4SYongbok Kim int uindex = (address >> pf_udw) & ((1 << udw) - 1); 733074cfcb4SYongbok Kim int mindex = (address >> pf_mdw) & ((1 << mdw) - 1); 734074cfcb4SYongbok Kim int ptindex = (address >> pf_ptw) & ((1 << ptw) - 1); 735074cfcb4SYongbok Kim 736074cfcb4SYongbok Kim /* Other HTW configs */ 737074cfcb4SYongbok Kim int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; 738074cfcb4SYongbok Kim 739074cfcb4SYongbok Kim /* HTW Shift values (depend on entry size) */ 740074cfcb4SYongbok Kim int directory_shift = (ptew > 1) ? -1 : 741074cfcb4SYongbok Kim (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; 742074cfcb4SYongbok Kim int leaf_shift = (ptew > 1) ? -1 : 743074cfcb4SYongbok Kim (ptew == 1) ? native_shift + 1 : native_shift; 744074cfcb4SYongbok Kim 745074cfcb4SYongbok Kim /* Offsets into tables */ 746074cfcb4SYongbok Kim int goffset = gindex << directory_shift; 747074cfcb4SYongbok Kim int uoffset = uindex << directory_shift; 748074cfcb4SYongbok Kim int moffset = mindex << directory_shift; 749074cfcb4SYongbok Kim int ptoffset0 = (ptindex >> 1) << (leaf_shift + 1); 750074cfcb4SYongbok Kim int ptoffset1 = ptoffset0 | (1 << (leaf_shift)); 751074cfcb4SYongbok Kim 752074cfcb4SYongbok Kim uint32_t leafentry_size = 1 << (leaf_shift + 3); 753074cfcb4SYongbok Kim 754074cfcb4SYongbok Kim /* Starting address - Page Table Base */ 755074cfcb4SYongbok Kim uint64_t vaddr = env->CP0_PWBase; 756074cfcb4SYongbok Kim 757074cfcb4SYongbok Kim uint64_t dir_entry; 758074cfcb4SYongbok Kim uint64_t paddr; 759074cfcb4SYongbok Kim int prot; 760074cfcb4SYongbok Kim int m; 761074cfcb4SYongbok Kim 762074cfcb4SYongbok Kim if (!(env->CP0_Config3 & (1 << CP0C3_PW))) { 763074cfcb4SYongbok Kim /* walker is unimplemented */ 764074cfcb4SYongbok Kim return false; 765074cfcb4SYongbok Kim } 766074cfcb4SYongbok Kim if (!(env->CP0_PWCtl & (1 << CP0PC_PWEN))) { 767074cfcb4SYongbok Kim /* walker is disabled */ 768074cfcb4SYongbok Kim return false; 769074cfcb4SYongbok Kim } 770074cfcb4SYongbok Kim if (!(gdw > 0 || udw > 0 || mdw > 0)) { 771074cfcb4SYongbok Kim /* no structure to walk */ 772074cfcb4SYongbok Kim return false; 773074cfcb4SYongbok Kim } 774074cfcb4SYongbok Kim if ((directory_shift == -1) || (leaf_shift == -1)) { 775074cfcb4SYongbok Kim return false; 776074cfcb4SYongbok Kim } 777074cfcb4SYongbok Kim 778074cfcb4SYongbok Kim /* Global Directory */ 779074cfcb4SYongbok Kim if (gdw > 0) { 780074cfcb4SYongbok Kim vaddr |= goffset; 781074cfcb4SYongbok Kim switch (walk_directory(env, &vaddr, pf_gdw, &huge_page, &hgpg_gdhit, 782074cfcb4SYongbok Kim &pw_entrylo0, &pw_entrylo1)) 783074cfcb4SYongbok Kim { 784074cfcb4SYongbok Kim case 0: 785074cfcb4SYongbok Kim return false; 786074cfcb4SYongbok Kim case 1: 787074cfcb4SYongbok Kim goto refill; 788074cfcb4SYongbok Kim case 2: 789074cfcb4SYongbok Kim default: 790074cfcb4SYongbok Kim break; 791074cfcb4SYongbok Kim } 792074cfcb4SYongbok Kim } 793074cfcb4SYongbok Kim 794074cfcb4SYongbok Kim /* Upper directory */ 795074cfcb4SYongbok Kim if (udw > 0) { 796074cfcb4SYongbok Kim vaddr |= uoffset; 797074cfcb4SYongbok Kim switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhit, 798074cfcb4SYongbok Kim &pw_entrylo0, &pw_entrylo1)) 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, 814074cfcb4SYongbok Kim &pw_entrylo0, &pw_entrylo1)) 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, 829935c1034SPhilippe Mathieu-Daudé cpu_mmu_index(env, false)) != 830074cfcb4SYongbok Kim TLBRET_MATCH) { 831074cfcb4SYongbok Kim return false; 832074cfcb4SYongbok Kim } 833074cfcb4SYongbok Kim if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { 834074cfcb4SYongbok Kim return false; 835074cfcb4SYongbok Kim } 836074cfcb4SYongbok Kim dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, 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, 842935c1034SPhilippe Mathieu-Daudé cpu_mmu_index(env, false)) != 843074cfcb4SYongbok Kim TLBRET_MATCH) { 844074cfcb4SYongbok Kim return false; 845074cfcb4SYongbok Kim } 846074cfcb4SYongbok Kim if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { 847074cfcb4SYongbok Kim return false; 848074cfcb4SYongbok Kim } 849074cfcb4SYongbok Kim dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); 850074cfcb4SYongbok Kim pw_entrylo1 = dir_entry; 851074cfcb4SYongbok Kim 852074cfcb4SYongbok Kim refill: 853074cfcb4SYongbok Kim 854074cfcb4SYongbok Kim m = (1 << pf_ptw) - 1; 855074cfcb4SYongbok Kim 856074cfcb4SYongbok Kim if (huge_page) { 857074cfcb4SYongbok Kim switch (hgpg_bdhit << 3 | hgpg_gdhit << 2 | hgpg_udhit << 1 | 858074cfcb4SYongbok Kim hgpg_mdhit) 859074cfcb4SYongbok Kim { 860074cfcb4SYongbok Kim case 4: 861074cfcb4SYongbok Kim m = (1 << pf_gdw) - 1; 862074cfcb4SYongbok Kim if (pf_gdw & 1) { 863074cfcb4SYongbok Kim m >>= 1; 864074cfcb4SYongbok Kim } 865074cfcb4SYongbok Kim break; 866074cfcb4SYongbok Kim case 2: 867074cfcb4SYongbok Kim m = (1 << pf_udw) - 1; 868074cfcb4SYongbok Kim if (pf_udw & 1) { 869074cfcb4SYongbok Kim m >>= 1; 870074cfcb4SYongbok Kim } 871074cfcb4SYongbok Kim break; 872074cfcb4SYongbok Kim case 1: 873074cfcb4SYongbok Kim m = (1 << pf_mdw) - 1; 874074cfcb4SYongbok Kim if (pf_mdw & 1) { 875074cfcb4SYongbok Kim m >>= 1; 876074cfcb4SYongbok Kim } 877074cfcb4SYongbok Kim break; 878074cfcb4SYongbok Kim } 879074cfcb4SYongbok Kim } 880547b9b17SPhilippe Mathieu-Daudé pw_pagemask = m >> TARGET_PAGE_BITS_MIN; 881547b9b17SPhilippe Mathieu-Daudé update_pagemask(env, pw_pagemask << CP0PM_MASK, &pw_pagemask); 882074cfcb4SYongbok Kim pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); 883074cfcb4SYongbok Kim { 884074cfcb4SYongbok Kim target_ulong tmp_entryhi = env->CP0_EntryHi; 885074cfcb4SYongbok Kim int32_t tmp_pagemask = env->CP0_PageMask; 886074cfcb4SYongbok Kim uint64_t tmp_entrylo0 = env->CP0_EntryLo0; 887074cfcb4SYongbok Kim uint64_t tmp_entrylo1 = env->CP0_EntryLo1; 888074cfcb4SYongbok Kim 889074cfcb4SYongbok Kim env->CP0_EntryHi = pw_entryhi; 890074cfcb4SYongbok Kim env->CP0_PageMask = pw_pagemask; 891074cfcb4SYongbok Kim env->CP0_EntryLo0 = pw_entrylo0; 892074cfcb4SYongbok Kim env->CP0_EntryLo1 = pw_entrylo1; 893074cfcb4SYongbok Kim 894074cfcb4SYongbok Kim /* 895074cfcb4SYongbok Kim * The hardware page walker inserts a page into the TLB in a manner 896074cfcb4SYongbok Kim * identical to a TLBWR instruction as executed by the software refill 897074cfcb4SYongbok Kim * handler. 898074cfcb4SYongbok Kim */ 899074cfcb4SYongbok Kim r4k_helper_tlbwr(env); 900074cfcb4SYongbok Kim 901074cfcb4SYongbok Kim env->CP0_EntryHi = tmp_entryhi; 902074cfcb4SYongbok Kim env->CP0_PageMask = tmp_pagemask; 903074cfcb4SYongbok Kim env->CP0_EntryLo0 = tmp_entrylo0; 904074cfcb4SYongbok Kim env->CP0_EntryLo1 = tmp_entrylo1; 905074cfcb4SYongbok Kim } 906074cfcb4SYongbok Kim return true; 907074cfcb4SYongbok Kim } 908074cfcb4SYongbok Kim #endif 909074cfcb4SYongbok Kim 910931d019fSRichard Henderson bool mips_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 911931d019fSRichard Henderson MMUAccessType access_type, int mmu_idx, 912931d019fSRichard Henderson bool probe, uintptr_t retaddr) 9131147e189SAurelien Jarno { 9147510454eSAndreas Färber MIPSCPU *cpu = MIPS_CPU(cs); 9157510454eSAndreas Färber CPUMIPSState *env = &cpu->env; 916a8170e5eSAvi Kivity hwaddr physical; 9171147e189SAurelien Jarno int prot; 918995ffde9SRichard Henderson int ret = TLBRET_BADADDR; 9191147e189SAurelien Jarno 9201147e189SAurelien Jarno /* data access */ 921074cfcb4SYongbok Kim /* XXX: put correct access by using cpu_restore_state() correctly */ 922931d019fSRichard Henderson ret = get_physical_address(env, &physical, &prot, address, 923935c1034SPhilippe Mathieu-Daudé access_type, mmu_idx); 924def74c0cSPhilippe Mathieu-Daudé switch (ret) { 925def74c0cSPhilippe Mathieu-Daudé case TLBRET_MATCH: 926339aaf5bSAntony Pavlov qemu_log_mask(CPU_LOG_MMU, 927def74c0cSPhilippe Mathieu-Daudé "%s address=%" VADDR_PRIx " physical " TARGET_FMT_plx 928def74c0cSPhilippe Mathieu-Daudé " prot %d\n", __func__, address, physical, prot); 929def74c0cSPhilippe Mathieu-Daudé break; 930def74c0cSPhilippe Mathieu-Daudé default: 931def74c0cSPhilippe Mathieu-Daudé qemu_log_mask(CPU_LOG_MMU, 932def74c0cSPhilippe Mathieu-Daudé "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, 933def74c0cSPhilippe Mathieu-Daudé ret); 934def74c0cSPhilippe Mathieu-Daudé break; 935def74c0cSPhilippe Mathieu-Daudé } 9361147e189SAurelien Jarno if (ret == TLBRET_MATCH) { 9370c591eb0SAndreas Färber tlb_set_page(cs, address & TARGET_PAGE_MASK, 9387353113fSJakub Jermář physical & TARGET_PAGE_MASK, prot, 939d4c430a8SPaul Brook mmu_idx, TARGET_PAGE_SIZE); 940931d019fSRichard Henderson return true; 941e38f4eb6SRichard Henderson } 942074cfcb4SYongbok Kim #if !defined(TARGET_MIPS64) 943074cfcb4SYongbok Kim if ((ret == TLBRET_NOMATCH) && (env->tlb->nb_tlb > 1)) { 944074cfcb4SYongbok Kim /* 945074cfcb4SYongbok Kim * Memory reads during hardware page table walking are performed 946074cfcb4SYongbok Kim * as if they were kernel-mode load instructions. 947074cfcb4SYongbok Kim */ 948074cfcb4SYongbok Kim int mode = (env->hflags & MIPS_HFLAG_KSU); 949074cfcb4SYongbok Kim bool ret_walker; 950074cfcb4SYongbok Kim env->hflags &= ~MIPS_HFLAG_KSU; 951bca3763bSPhilippe Mathieu-Daudé ret_walker = page_table_walk_refill(env, address, mmu_idx); 952074cfcb4SYongbok Kim env->hflags |= mode; 953074cfcb4SYongbok Kim if (ret_walker) { 954931d019fSRichard Henderson ret = get_physical_address(env, &physical, &prot, address, 955935c1034SPhilippe Mathieu-Daudé access_type, mmu_idx); 956074cfcb4SYongbok Kim if (ret == TLBRET_MATCH) { 957074cfcb4SYongbok Kim tlb_set_page(cs, address & TARGET_PAGE_MASK, 9587353113fSJakub Jermář physical & TARGET_PAGE_MASK, prot, 959074cfcb4SYongbok Kim mmu_idx, TARGET_PAGE_SIZE); 960931d019fSRichard Henderson return true; 961074cfcb4SYongbok Kim } 962074cfcb4SYongbok Kim } 963074cfcb4SYongbok Kim } 964074cfcb4SYongbok Kim #endif 965931d019fSRichard Henderson if (probe) { 966931d019fSRichard Henderson return false; 967931d019fSRichard Henderson } 9686af0bf9cSbellard 969931d019fSRichard Henderson raise_mmu_exception(env, address, access_type, ret); 970931d019fSRichard Henderson do_raise_exception_err(env, cs->exception_index, env->error_code, retaddr); 9716af0bf9cSbellard } 9726af0bf9cSbellard 973d7551eceSAleksandar Markovic hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, 9740debf140SPhilippe Mathieu-Daudé MMUAccessType access_type, uintptr_t retaddr) 97525b91e32SAurelien Jarno { 976a8170e5eSAvi Kivity hwaddr physical; 97725b91e32SAurelien Jarno int prot; 97825b91e32SAurelien Jarno int ret = 0; 9790debf140SPhilippe Mathieu-Daudé CPUState *cs = env_cpu(env); 98025b91e32SAurelien Jarno 98125b91e32SAurelien Jarno /* data access */ 98248b28c6aSPhilippe Mathieu-Daudé ret = get_physical_address(env, &physical, &prot, address, access_type, 9839fbf4a58SJames Hogan cpu_mmu_index(env, false)); 9840debf140SPhilippe Mathieu-Daudé if (ret == TLBRET_MATCH) { 98525b91e32SAurelien Jarno return physical; 98625b91e32SAurelien Jarno } 9870debf140SPhilippe Mathieu-Daudé 9880debf140SPhilippe Mathieu-Daudé raise_mmu_exception(env, address, access_type, ret); 9890debf140SPhilippe Mathieu-Daudé cpu_loop_exit_restore(cs, retaddr); 990c36bbb28SAurelien Jarno } 99125b91e32SAurelien Jarno 9927db13faeSAndreas Färber static void set_hflags_for_handler(CPUMIPSState *env) 993bbfa8f72SNathan Froyd { 994bbfa8f72SNathan Froyd /* Exception handlers are entered in 32-bit mode. */ 995bbfa8f72SNathan Froyd env->hflags &= ~(MIPS_HFLAG_M16); 996bbfa8f72SNathan Froyd /* ...except that microMIPS lets you choose. */ 997bbfa8f72SNathan Froyd if (env->insn_flags & ASE_MICROMIPS) { 998d7551eceSAleksandar Markovic env->hflags |= (!!(env->CP0_Config3 & 999d7551eceSAleksandar Markovic (1 << CP0C3_ISA_ON_EXC)) 1000bbfa8f72SNathan Froyd << MIPS_HFLAG_M16_SHIFT); 1001bbfa8f72SNathan Froyd } 1002bbfa8f72SNathan Froyd } 1003aea14095SLeon Alrae 1004aea14095SLeon Alrae static inline void set_badinstr_registers(CPUMIPSState *env) 1005aea14095SLeon Alrae { 10067a5f784aSStefan Markovic if (env->insn_flags & ISA_NANOMIPS32) { 10077a5f784aSStefan Markovic if (env->CP0_Config3 & (1 << CP0C3_BI)) { 10087a5f784aSStefan Markovic uint32_t instr = (cpu_lduw_code(env, env->active_tc.PC)) << 16; 10097a5f784aSStefan Markovic if ((instr & 0x10000000) == 0) { 10107a5f784aSStefan Markovic instr |= cpu_lduw_code(env, env->active_tc.PC + 2); 10117a5f784aSStefan Markovic } 10127a5f784aSStefan Markovic env->CP0_BadInstr = instr; 10137a5f784aSStefan Markovic 10147a5f784aSStefan Markovic if ((instr & 0xFC000000) == 0x60000000) { 10157a5f784aSStefan Markovic instr = cpu_lduw_code(env, env->active_tc.PC + 4) << 16; 10167a5f784aSStefan Markovic env->CP0_BadInstrX = instr; 10177a5f784aSStefan Markovic } 10187a5f784aSStefan Markovic } 10197a5f784aSStefan Markovic return; 10207a5f784aSStefan Markovic } 10217a5f784aSStefan Markovic 1022aea14095SLeon Alrae if (env->hflags & MIPS_HFLAG_M16) { 1023aea14095SLeon Alrae /* TODO: add BadInstr support for microMIPS */ 1024aea14095SLeon Alrae return; 1025aea14095SLeon Alrae } 1026aea14095SLeon Alrae if (env->CP0_Config3 & (1 << CP0C3_BI)) { 1027aea14095SLeon Alrae env->CP0_BadInstr = cpu_ldl_code(env, env->active_tc.PC); 1028aea14095SLeon Alrae } 1029aea14095SLeon Alrae if ((env->CP0_Config3 & (1 << CP0C3_BP)) && 1030aea14095SLeon Alrae (env->hflags & MIPS_HFLAG_BMASK)) { 1031aea14095SLeon Alrae env->CP0_BadInstrP = cpu_ldl_code(env, env->active_tc.PC - 4); 1032aea14095SLeon Alrae } 1033aea14095SLeon Alrae } 1034f9bd3d79SPhilippe Mathieu-Daudé 103597a8ea5aSAndreas Färber void mips_cpu_do_interrupt(CPUState *cs) 1036ca7c2b1bSths { 103797a8ea5aSAndreas Färber MIPSCPU *cpu = MIPS_CPU(cs); 103897a8ea5aSAndreas Färber CPUMIPSState *env = &cpu->env; 1039aea14095SLeon Alrae bool update_badinstr = 0; 1040aa328addSths target_ulong offset; 10416af0bf9cSbellard int cause = -1; 10426af0bf9cSbellard 1043c8557016SRichard Henderson if (qemu_loglevel_mask(CPU_LOG_INT) 1044c8557016SRichard Henderson && cs->exception_index != EXCP_EXT_INTERRUPT) { 1045c8557016SRichard Henderson qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx 1046c8557016SRichard Henderson " %s exception\n", 104790c429eeSPhilippe Mathieu-Daudé __func__, env->active_tc.PC, env->CP0_EPC, 104890c429eeSPhilippe Mathieu-Daudé mips_exception_name(cs->exception_index)); 10496af0bf9cSbellard } 105027103424SAndreas Färber if (cs->exception_index == EXCP_EXT_INTERRUPT && 105127103424SAndreas Färber (env->hflags & MIPS_HFLAG_DM)) { 105227103424SAndreas Färber cs->exception_index = EXCP_DINT; 105327103424SAndreas Färber } 10546af0bf9cSbellard offset = 0x180; 105527103424SAndreas Färber switch (cs->exception_index) { 1056*8ec7e3c5SRichard Henderson case EXCP_SEMIHOST: 1057*8ec7e3c5SRichard Henderson cs->exception_index = EXCP_NONE; 1058*8ec7e3c5SRichard Henderson mips_semihosting(env); 1059*8ec7e3c5SRichard Henderson return; 10606af0bf9cSbellard case EXCP_DSS: 10616af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DSS; 1062d7551eceSAleksandar Markovic /* 1063d7551eceSAleksandar Markovic * Debug single step cannot be raised inside a delay slot and 1064d7551eceSAleksandar Markovic * resume will always occur on the next instruction 1065d7551eceSAleksandar Markovic * (but we assume the pc has always been updated during 1066d7551eceSAleksandar Markovic * code translation). 1067d7551eceSAleksandar Markovic */ 106832188a03SNathan Froyd env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16); 10696af0bf9cSbellard goto enter_debug_mode; 10706af0bf9cSbellard case EXCP_DINT: 10716af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DINT; 10726af0bf9cSbellard goto set_DEPC; 10736af0bf9cSbellard case EXCP_DIB: 10746af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DIB; 10756af0bf9cSbellard goto set_DEPC; 10766af0bf9cSbellard case EXCP_DBp: 10776af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DBp; 1078c6c2c0fcSPavel Dovgalyuk /* Setup DExcCode - SDBBP instruction */ 1079d7551eceSAleksandar Markovic env->CP0_Debug = (env->CP0_Debug & ~(0x1fULL << CP0DB_DEC)) | 1080d7551eceSAleksandar Markovic (9 << CP0DB_DEC); 10816af0bf9cSbellard goto set_DEPC; 10826af0bf9cSbellard case EXCP_DDBS: 10836af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DDBS; 10846af0bf9cSbellard goto set_DEPC; 10856af0bf9cSbellard case EXCP_DDBL: 10866af0bf9cSbellard env->CP0_Debug |= 1 << CP0DB_DDBL; 10876af0bf9cSbellard set_DEPC: 108832188a03SNathan Froyd env->CP0_DEPC = exception_resume_pc(env); 10894ad40f36Sbellard env->hflags &= ~MIPS_HFLAG_BMASK; 10906af0bf9cSbellard enter_debug_mode: 1091d9224450SMaciej W. Rozycki if (env->insn_flags & ISA_MIPS3) { 1092d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_64; 10932e211e0aSPhilippe Mathieu-Daudé if (!(env->insn_flags & ISA_MIPS_R6) || 10947871abb9SJames Hogan env->CP0_Status & (1 << CP0St_KX)) { 10957871abb9SJames Hogan env->hflags &= ~MIPS_HFLAG_AWRAP; 10967871abb9SJames Hogan } 1097d9224450SMaciej W. Rozycki } 1098d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_CP0; 1099623a930eSths env->hflags &= ~(MIPS_HFLAG_KSU); 11006af0bf9cSbellard /* EJTAG probe trap enable is not implemented... */ 1101d7551eceSAleksandar Markovic if (!(env->CP0_Status & (1 << CP0St_EXL))) { 1102f45cb2f4SPeter Maydell env->CP0_Cause &= ~(1U << CP0Ca_BD); 1103d7551eceSAleksandar Markovic } 110489777fd1SLeon Alrae env->active_tc.PC = env->exception_base + 0x480; 1105bbfa8f72SNathan Froyd set_hflags_for_handler(env); 11066af0bf9cSbellard break; 11076af0bf9cSbellard case EXCP_RESET: 1108fca1be7cSAndreas Färber cpu_reset(CPU(cpu)); 1109aa328addSths break; 11106af0bf9cSbellard case EXCP_SRESET: 111124c7b0e3Sths env->CP0_Status |= (1 << CP0St_SR); 11129d989c73SAurelien Jarno memset(env->CP0_WatchLo, 0, sizeof(env->CP0_WatchLo)); 11136af0bf9cSbellard goto set_error_EPC; 11146af0bf9cSbellard case EXCP_NMI: 111524c7b0e3Sths env->CP0_Status |= (1 << CP0St_NMI); 11166af0bf9cSbellard set_error_EPC: 111732188a03SNathan Froyd env->CP0_ErrorEPC = exception_resume_pc(env); 1118ecd78a0aSpbrook env->hflags &= ~MIPS_HFLAG_BMASK; 1119aa328addSths env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); 1120d9224450SMaciej W. Rozycki if (env->insn_flags & ISA_MIPS3) { 1121d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_64; 11222e211e0aSPhilippe Mathieu-Daudé if (!(env->insn_flags & ISA_MIPS_R6) || 11237871abb9SJames Hogan env->CP0_Status & (1 << CP0St_KX)) { 11247871abb9SJames Hogan env->hflags &= ~MIPS_HFLAG_AWRAP; 11257871abb9SJames Hogan } 1126d9224450SMaciej W. Rozycki } 1127d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_CP0; 1128623a930eSths env->hflags &= ~(MIPS_HFLAG_KSU); 1129d7551eceSAleksandar Markovic if (!(env->CP0_Status & (1 << CP0St_EXL))) { 1130f45cb2f4SPeter Maydell env->CP0_Cause &= ~(1U << CP0Ca_BD); 1131d7551eceSAleksandar Markovic } 113289777fd1SLeon Alrae env->active_tc.PC = env->exception_base; 1133bbfa8f72SNathan Froyd set_hflags_for_handler(env); 11346af0bf9cSbellard break; 11356af0bf9cSbellard case EXCP_EXT_INTERRUPT: 11366af0bf9cSbellard cause = 0; 1137da52a4dfSYongbok Kim if (env->CP0_Cause & (1 << CP0Ca_IV)) { 1138da52a4dfSYongbok Kim uint32_t spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & 0x1f; 1139da52a4dfSYongbok Kim 1140da52a4dfSYongbok Kim if ((env->CP0_Status & (1 << CP0St_BEV)) || spacing == 0) { 11416af0bf9cSbellard offset = 0x200; 1142138afb02SEdgar E. Iglesias } else { 1143da52a4dfSYongbok Kim uint32_t vector = 0; 1144da52a4dfSYongbok Kim uint32_t pending = (env->CP0_Cause & CP0Ca_IP_mask) >> CP0Ca_IP; 1145da52a4dfSYongbok Kim 1146da52a4dfSYongbok Kim if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { 1147d7551eceSAleksandar Markovic /* 1148d7551eceSAleksandar Markovic * For VEIC mode, the external interrupt controller feeds 1149d7551eceSAleksandar Markovic * the vector through the CP0Cause IP lines. 1150d7551eceSAleksandar Markovic */ 1151138afb02SEdgar E. Iglesias vector = pending; 1152da52a4dfSYongbok Kim } else { 1153d7551eceSAleksandar Markovic /* 1154d7551eceSAleksandar Markovic * Vectored Interrupts 1155d7551eceSAleksandar Markovic * Mask with Status.IM7-IM0 to get enabled interrupts. 1156d7551eceSAleksandar Markovic */ 1157da52a4dfSYongbok Kim pending &= (env->CP0_Status >> CP0St_IM) & 0xff; 1158da52a4dfSYongbok Kim /* Find the highest-priority interrupt. */ 1159da52a4dfSYongbok Kim while (pending >>= 1) { 1160da52a4dfSYongbok Kim vector++; 1161138afb02SEdgar E. Iglesias } 1162da52a4dfSYongbok Kim } 1163da52a4dfSYongbok Kim offset = 0x200 + (vector * (spacing << 5)); 1164da52a4dfSYongbok Kim } 1165138afb02SEdgar E. Iglesias } 11666af0bf9cSbellard goto set_EPC; 1167b67bfe8dSths case EXCP_LTLBL: 1168b67bfe8dSths cause = 1; 1169aea14095SLeon Alrae update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); 1170beb811bdSths goto set_EPC; 11716af0bf9cSbellard case EXCP_TLBL: 11726af0bf9cSbellard cause = 2; 1173aea14095SLeon Alrae update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); 1174aea14095SLeon Alrae if ((env->error_code & EXCP_TLB_NOMATCH) && 1175aea14095SLeon Alrae !(env->CP0_Status & (1 << CP0St_EXL))) { 1176d26bc211Sths #if defined(TARGET_MIPS64) 1177100ce988Sths int R = env->CP0_BadVAddr >> 62; 1178100ce988Sths int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; 1179100ce988Sths int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; 1180100ce988Sths 1181480e79aeSJames Hogan if ((R != 0 || UX) && (R != 3 || KX) && 1182480e79aeSJames Hogan (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { 1183100ce988Sths offset = 0x080; 1184480e79aeSJames Hogan } else { 1185100ce988Sths #endif 11866af0bf9cSbellard offset = 0x000; 1187480e79aeSJames Hogan #if defined(TARGET_MIPS64) 1188480e79aeSJames Hogan } 1189480e79aeSJames Hogan #endif 1190100ce988Sths } 11916af0bf9cSbellard goto set_EPC; 1192b67bfe8dSths case EXCP_TLBS: 1193b67bfe8dSths cause = 3; 1194aea14095SLeon Alrae update_badinstr = 1; 1195aea14095SLeon Alrae if ((env->error_code & EXCP_TLB_NOMATCH) && 1196aea14095SLeon Alrae !(env->CP0_Status & (1 << CP0St_EXL))) { 1197b67bfe8dSths #if defined(TARGET_MIPS64) 1198b67bfe8dSths int R = env->CP0_BadVAddr >> 62; 1199b67bfe8dSths int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; 1200b67bfe8dSths int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; 1201b67bfe8dSths 1202480e79aeSJames Hogan if ((R != 0 || UX) && (R != 3 || KX) && 1203480e79aeSJames Hogan (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) { 1204b67bfe8dSths offset = 0x080; 1205480e79aeSJames Hogan } else { 1206b67bfe8dSths #endif 1207b67bfe8dSths offset = 0x000; 1208480e79aeSJames Hogan #if defined(TARGET_MIPS64) 1209480e79aeSJames Hogan } 1210480e79aeSJames Hogan #endif 1211b67bfe8dSths } 1212b67bfe8dSths goto set_EPC; 1213b67bfe8dSths case EXCP_AdEL: 1214b67bfe8dSths cause = 4; 1215aea14095SLeon Alrae update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); 1216b67bfe8dSths goto set_EPC; 1217b67bfe8dSths case EXCP_AdES: 1218b67bfe8dSths cause = 5; 1219aea14095SLeon Alrae update_badinstr = 1; 1220b67bfe8dSths goto set_EPC; 12216af0bf9cSbellard case EXCP_IBE: 12226af0bf9cSbellard cause = 6; 12236af0bf9cSbellard goto set_EPC; 12246af0bf9cSbellard case EXCP_DBE: 12256af0bf9cSbellard cause = 7; 12266af0bf9cSbellard goto set_EPC; 12276af0bf9cSbellard case EXCP_SYSCALL: 12286af0bf9cSbellard cause = 8; 1229aea14095SLeon Alrae update_badinstr = 1; 12306af0bf9cSbellard goto set_EPC; 12316af0bf9cSbellard case EXCP_BREAK: 12326af0bf9cSbellard cause = 9; 1233aea14095SLeon Alrae update_badinstr = 1; 12346af0bf9cSbellard goto set_EPC; 12356af0bf9cSbellard case EXCP_RI: 12366af0bf9cSbellard cause = 10; 1237aea14095SLeon Alrae update_badinstr = 1; 12386af0bf9cSbellard goto set_EPC; 12396af0bf9cSbellard case EXCP_CpU: 12406af0bf9cSbellard cause = 11; 1241aea14095SLeon Alrae update_badinstr = 1; 124239d51eb8Sths env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | 124339d51eb8Sths (env->error_code << CP0Ca_CE); 12446af0bf9cSbellard goto set_EPC; 12456af0bf9cSbellard case EXCP_OVERFLOW: 12466af0bf9cSbellard cause = 12; 1247aea14095SLeon Alrae update_badinstr = 1; 12486af0bf9cSbellard goto set_EPC; 12496af0bf9cSbellard case EXCP_TRAP: 12506af0bf9cSbellard cause = 13; 1251aea14095SLeon Alrae update_badinstr = 1; 12526af0bf9cSbellard goto set_EPC; 1253b10ac204SYongbok Kim case EXCP_MSAFPE: 1254b10ac204SYongbok Kim cause = 14; 1255b10ac204SYongbok Kim update_badinstr = 1; 1256b10ac204SYongbok Kim goto set_EPC; 12575a5012ecSths case EXCP_FPE: 12585a5012ecSths cause = 15; 1259aea14095SLeon Alrae update_badinstr = 1; 12605a5012ecSths goto set_EPC; 1261b67bfe8dSths case EXCP_C2E: 1262b67bfe8dSths cause = 18; 12636af0bf9cSbellard goto set_EPC; 126492ceb440SLeon Alrae case EXCP_TLBRI: 126592ceb440SLeon Alrae cause = 19; 1266aea14095SLeon Alrae update_badinstr = 1; 126792ceb440SLeon Alrae goto set_EPC; 126892ceb440SLeon Alrae case EXCP_TLBXI: 126992ceb440SLeon Alrae cause = 20; 127092ceb440SLeon Alrae goto set_EPC; 1271b10ac204SYongbok Kim case EXCP_MSADIS: 1272b10ac204SYongbok Kim cause = 21; 1273b10ac204SYongbok Kim update_badinstr = 1; 1274b10ac204SYongbok Kim goto set_EPC; 1275b67bfe8dSths case EXCP_MDMX: 1276b67bfe8dSths cause = 22; 1277b67bfe8dSths goto set_EPC; 1278b67bfe8dSths case EXCP_DWATCH: 1279b67bfe8dSths cause = 23; 128067cc32ebSVeres Lajos /* XXX: TODO: manage deferred watch exceptions */ 1281b67bfe8dSths goto set_EPC; 1282b67bfe8dSths case EXCP_MCHECK: 1283b67bfe8dSths cause = 24; 12846276c767Sths goto set_EPC; 12856276c767Sths case EXCP_THREAD: 12866276c767Sths cause = 25; 1287b67bfe8dSths goto set_EPC; 1288853c3240SJia Liu case EXCP_DSPDIS: 1289853c3240SJia Liu cause = 26; 1290853c3240SJia Liu goto set_EPC; 1291b67bfe8dSths case EXCP_CACHE: 1292b67bfe8dSths cause = 30; 1293b67bfe8dSths offset = 0x100; 12946af0bf9cSbellard set_EPC: 129524c7b0e3Sths if (!(env->CP0_Status & (1 << CP0St_EXL))) { 129632188a03SNathan Froyd env->CP0_EPC = exception_resume_pc(env); 1297aea14095SLeon Alrae if (update_badinstr) { 1298aea14095SLeon Alrae set_badinstr_registers(env); 1299aea14095SLeon Alrae } 13004ad40f36Sbellard if (env->hflags & MIPS_HFLAG_BMASK) { 1301f45cb2f4SPeter Maydell env->CP0_Cause |= (1U << CP0Ca_BD); 13026af0bf9cSbellard } else { 1303f45cb2f4SPeter Maydell env->CP0_Cause &= ~(1U << CP0Ca_BD); 13046af0bf9cSbellard } 130524c7b0e3Sths env->CP0_Status |= (1 << CP0St_EXL); 1306d9224450SMaciej W. Rozycki if (env->insn_flags & ISA_MIPS3) { 1307d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_64; 13082e211e0aSPhilippe Mathieu-Daudé if (!(env->insn_flags & ISA_MIPS_R6) || 13097871abb9SJames Hogan env->CP0_Status & (1 << CP0St_KX)) { 13107871abb9SJames Hogan env->hflags &= ~MIPS_HFLAG_AWRAP; 13117871abb9SJames Hogan } 1312d9224450SMaciej W. Rozycki } 1313d9224450SMaciej W. Rozycki env->hflags |= MIPS_HFLAG_CP0; 1314623a930eSths env->hflags &= ~(MIPS_HFLAG_KSU); 131524c7b0e3Sths } 1316c53f4a62Sths env->hflags &= ~MIPS_HFLAG_BMASK; 1317aa328addSths if (env->CP0_Status & (1 << CP0St_BEV)) { 131889777fd1SLeon Alrae env->active_tc.PC = env->exception_base + 0x200; 131974dbf824SJames Hogan } else if (cause == 30 && !(env->CP0_Config3 & (1 << CP0C3_SC) && 132074dbf824SJames Hogan env->CP0_Config5 & (1 << CP0C5_CV))) { 132174dbf824SJames Hogan /* Force KSeg1 for cache errors */ 132267433345SJames Hogan env->active_tc.PC = KSEG1_BASE | (env->CP0_EBase & 0x1FFFF000); 1323aa328addSths } else { 132474dbf824SJames Hogan env->active_tc.PC = env->CP0_EBase & ~0xfff; 1325aa328addSths } 132674dbf824SJames Hogan 1327b5dc7732Sths env->active_tc.PC += offset; 1328bbfa8f72SNathan Froyd set_hflags_for_handler(env); 1329d7551eceSAleksandar Markovic env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | 1330d7551eceSAleksandar Markovic (cause << CP0Ca_EC); 13316af0bf9cSbellard break; 13326af0bf9cSbellard default: 1333c8557016SRichard Henderson abort(); 13346af0bf9cSbellard } 1335c8557016SRichard Henderson if (qemu_loglevel_mask(CPU_LOG_INT) 1336c8557016SRichard Henderson && cs->exception_index != EXCP_EXT_INTERRUPT) { 133793fcfe39Saliguori qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" 13383594c774Sths " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", 1339b5dc7732Sths __func__, env->active_tc.PC, env->CP0_EPC, cause, 13406af0bf9cSbellard env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, 13416af0bf9cSbellard env->CP0_DEPC); 13426af0bf9cSbellard } 134327103424SAndreas Färber cs->exception_index = EXCP_NONE; 13446af0bf9cSbellard } 13452ee4aed8Sbellard 13466eb66e08SPhilippe Mathieu-Daudé bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 13476eb66e08SPhilippe Mathieu-Daudé { 13486eb66e08SPhilippe Mathieu-Daudé if (interrupt_request & CPU_INTERRUPT_HARD) { 13496eb66e08SPhilippe Mathieu-Daudé MIPSCPU *cpu = MIPS_CPU(cs); 13506eb66e08SPhilippe Mathieu-Daudé CPUMIPSState *env = &cpu->env; 13516eb66e08SPhilippe Mathieu-Daudé 13526eb66e08SPhilippe Mathieu-Daudé if (cpu_mips_hw_interrupts_enabled(env) && 13536eb66e08SPhilippe Mathieu-Daudé cpu_mips_hw_interrupts_pending(env)) { 13546eb66e08SPhilippe Mathieu-Daudé /* Raise it */ 13556eb66e08SPhilippe Mathieu-Daudé cs->exception_index = EXCP_EXT_INTERRUPT; 13566eb66e08SPhilippe Mathieu-Daudé env->error_code = 0; 13576eb66e08SPhilippe Mathieu-Daudé mips_cpu_do_interrupt(cs); 13586eb66e08SPhilippe Mathieu-Daudé return true; 13596eb66e08SPhilippe Mathieu-Daudé } 13606eb66e08SPhilippe Mathieu-Daudé } 13616eb66e08SPhilippe Mathieu-Daudé return false; 13626eb66e08SPhilippe Mathieu-Daudé } 13636eb66e08SPhilippe Mathieu-Daudé 13647db13faeSAndreas Färber void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra) 13652ee4aed8Sbellard { 13665a7330b3SRichard Henderson CPUState *cs = env_cpu(env); 1367c227f099SAnthony Liguori r4k_tlb_t *tlb; 13683b1c8be4Sths target_ulong addr; 13693b1c8be4Sths target_ulong end; 13702d72e7b0SPaul Burton uint16_t ASID = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; 137199029be1SYongbok Kim uint32_t MMID = env->CP0_MemoryMapID; 137299029be1SYongbok Kim bool mi = !!((env->CP0_Config5 >> CP0C5_MI) & 1); 137399029be1SYongbok Kim uint32_t tlb_mmid; 13743b1c8be4Sths target_ulong mask; 13752ee4aed8Sbellard 137699029be1SYongbok Kim MMID = mi ? MMID : (uint32_t) ASID; 137799029be1SYongbok Kim 1378ead9360eSths tlb = &env->tlb->mmu.r4k.tlb[idx]; 1379d7551eceSAleksandar Markovic /* 138099029be1SYongbok Kim * The qemu TLB is flushed when the ASID/MMID changes, so no need to 1381d7551eceSAleksandar Markovic * flush these entries again. 1382d7551eceSAleksandar Markovic */ 138399029be1SYongbok Kim tlb_mmid = mi ? tlb->MMID : (uint32_t) tlb->ASID; 138499029be1SYongbok Kim if (tlb->G == 0 && tlb_mmid != MMID) { 13852ee4aed8Sbellard return; 13862ee4aed8Sbellard } 13872ee4aed8Sbellard 1388ead9360eSths if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) { 1389d7551eceSAleksandar Markovic /* 1390d7551eceSAleksandar Markovic * For tlbwr, we can shadow the discarded entry into 1391d7551eceSAleksandar Markovic * a new (fake) TLB entry, as long as the guest can not 1392d7551eceSAleksandar Markovic * tell that it's there. 1393d7551eceSAleksandar Markovic */ 1394ead9360eSths env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb; 1395ead9360eSths env->tlb->tlb_in_use++; 13962ee4aed8Sbellard return; 13972ee4aed8Sbellard } 13982ee4aed8Sbellard 13993b1c8be4Sths /* 1k pages are not supported. */ 1400f2e9ebefSths mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); 14013b1c8be4Sths if (tlb->V0) { 1402f2e9ebefSths addr = tlb->VPN & ~mask; 1403d26bc211Sths #if defined(TARGET_MIPS64) 1404e034e2c3Sths if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { 1405100ce988Sths addr |= 0x3FFFFF0000000000ULL; 1406100ce988Sths } 1407100ce988Sths #endif 14083b1c8be4Sths end = addr | (mask >> 1); 14093b1c8be4Sths while (addr < end) { 141031b030d4SAndreas Färber tlb_flush_page(cs, addr); 14113b1c8be4Sths addr += TARGET_PAGE_SIZE; 14123b1c8be4Sths } 14133b1c8be4Sths } 14143b1c8be4Sths if (tlb->V1) { 1415f2e9ebefSths addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1); 1416d26bc211Sths #if defined(TARGET_MIPS64) 1417e034e2c3Sths if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { 1418100ce988Sths addr |= 0x3FFFFF0000000000ULL; 1419100ce988Sths } 1420100ce988Sths #endif 14213b1c8be4Sths end = addr | mask; 142253715e48Sths while (addr - 1 < end) { 142331b030d4SAndreas Färber tlb_flush_page(cs, addr); 14243b1c8be4Sths addr += TARGET_PAGE_SIZE; 14253b1c8be4Sths } 14263b1c8be4Sths } 14272ee4aed8Sbellard } 1428