/* * Copyright (C) 2017-2018 MIPS Tech, LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "cache.h" #include #include /* Writes hi, lo0, lo1 and mask into the TLB entry specified by index */ void __attribute__ ((use_hazard_barrier_return)) _MIPS_HAL_NOMIPS16 mips_tlbwi2 (tlbhi_t hi, tlblo_t lo0, tlblo_t lo1, unsigned int mask, int index) { mips32_setentryhi (hi); mips32_setentrylo0 (lo0); mips32_setentrylo1 (lo1); mips32_setpagemask (mask); mips32_setindex (index); mips_ehb (); mips_tlbwi (); return; } /* * Writes hi, lo0, lo1 and mask into the TLB entry specified by the * random register. */ void __attribute__ ((use_hazard_barrier_return)) _MIPS_HAL_NOMIPS16 mips_tlbwr2 (tlbhi_t hi, tlblo_t lo0, tlblo_t lo1, unsigned int mask) { mips32_setentryhi (hi); mips32_setentrylo0 (lo0); mips32_setentrylo1 (lo1); mips32_setpagemask (mask); mips_ehb (); mips_tlbwr (); return; } /* * Probes the TLB for an entry matching hi and if present rewrites that entry, * otherwise updates a random entry. A safe way to update the TLB. */ int __attribute__ ((use_hazard_barrier_return)) _MIPS_HAL_NOMIPS16 mips_tlbrwr2 (tlbhi_t hi, tlblo_t lo0, tlblo_t lo1, unsigned int mask) { int index; tlbhi_t prev_hi; prev_hi = mips32_getentryhi (); mips32_setentryhi (hi); mips_ehb (); /* mtc0 hazard on tlbp */ mips_tlbp (); mips_ehb (); /* tlbp hazard on mfc0 */ index = mips32_getindex (); mips32_setentrylo0 (lo0); mips32_setentrylo1 (lo1); mips32_setpagemask (mask); mips_ehb (); /* mtc0 hazard on tlbwi/tlbwr */ /* Check if entry matches */ if (index >= 0) mips_tlbwi (); else mips_tlbwr (); mips32_setentryhi (prev_hi); return index; } /* * Reads the TLB entry specified by index, and returns the EntryHi, * EntryLo0, EntryLo1 and PageMask parts in *phi, *plo0, *plo1 and *pmask * respectively. */ void _MIPS_HAL_NOMIPS16 mips_tlbri2 (tlbhi_t *phi, tlblo_t *plo0, tlblo_t *plo1, unsigned int *pmask, int index) { mips32_setindex (index); mips_ehb (); /* mtc0 hazard on tlbr */ mips_tlbr (); mips_ehb (); /* tlbr hazard on mfc0 */ *phi = mips32_getentryhi (); *plo0 = mips32_getentrylo0 (); *plo1 = mips32_getentrylo1 (); *pmask = mips32_getpagemask (); return; } /* * Probes the TLB for an entry matching hi and return its index, or -1 if * not found. If found, the EntryLo0, EntryLo1 and PageMask parts of the * entry are also returned in *plo0, *plo1 and *pmask respectively. */ int __attribute__ ((use_hazard_barrier_return)) _MIPS_HAL_NOMIPS16 mips_tlbprobe2 (tlbhi_t hi, tlblo_t *plo0, tlblo_t *plo1, unsigned int *pmask) { int index; tlbhi_t prev_hi; prev_hi = mips32_getentryhi (); mips32_setentryhi (hi); mips_ehb (); /* mtc0 hazard on tlbp */ mips_tlbp (); mips_ehb (); /* tlbp hazard on mfc0 */ index = mips32_getindex (); if (index >= 0) { mips_tlbr (); mips_ehb (); /* tlbr hazard on mfc0 */ *plo0 = mips32_getentrylo0 (); *plo1 = mips32_getentrylo1 (); *pmask = mips32_getpagemask (); } else index = -1; mips32_setentryhi (prev_hi); /* restore EntryHi */ return index; } /* Probes the TLB for an entry matching hi, and if present invalidates it */ void __attribute__ ((use_hazard_barrier_return)) _MIPS_HAL_NOMIPS16 mips_tlbinval (tlbhi_t hi) { int index, tmp_idx; tlbhi_t prev_hi, tmp_hi = 0; register const int zero = 0; unsigned int cfg; prev_hi = mips32_getentryhi (); mips32_setentryhi (hi); mips_ehb (); /* mtc0 hazard on tlbp */ mips_tlbp (); mips_ehb (); /* tlbp hazard on mfc0 */ index = mips32_getindex (); if (index < 0) goto restore; mips32_setentrylo0 (zero); mips32_setentrylo1 (zero); /* Check if Config4 is implemented */ cfg = mips32_getconfig3 (); if ((cfg & CFG3_M) != 0) { cfg = mips32_getconfig4 (); if ((cfg & CFG4_IE_MASK) != 0) { tmp_hi = C0_ENTRYHI_EHINV_MASK; goto do_tlbwi; } } tmp_hi = (tlbhi_t) ((unsigned long) KSEG0_BASE - 0x4000); do { tmp_hi = tmp_hi + 0x4000; mips32_setentryhi (tmp_hi); mips_ehb (); /* mtc0 hazard on tlbp */ mips_tlbp (); mips_ehb (); /* tlbp hazard on mfc0 */ tmp_idx = mips32_getindex (); } while (tmp_idx >= 0); mips32_setindex (index); /* restore Index */ do_tlbwi: mips32_setentryhi (tmp_hi); mips_ehb (); /* mtc0 hazard on tlbp */ mips_tlbwi (); mips_ehb (); /* tlbwi hazard on mfc0 */ restore: mips32_setentryhi (prev_hi); /* restore EntryHi */ return; } /* * Return number of entries and sets in TLB. * Return number of entries in *pentries and * sets in *psets */ static void _MIPS_HAL_NOMIPS16 mips_tlb_entries_sets (int *pentries, int *psets) { int entries = 0, sets = 0, ways = 0; unsigned int cfg, cfg1, tcfg, tmp; cfg = mips32_getconfig (); cfg = (cfg & CFG0_MT_MASK) >> CFG0_MT_SHIFT; if ((cfg == 0) /* No MMU */ || (cfg == (CFG0_MT_FIXED >> CFG0_MT_SHIFT)) /* fixed address translation */ || (cfg == (CFG0_MT_BAT >> CFG0_MT_SHIFT)) /* block address translator */ || ((cfg & ((CFG0_MT_TLB | CFG0_MT_DUAL) >> CFG0_MT_SHIFT)) == 0)) /* presence of TLB */ { *pentries = 0; *psets = 0; return; } cfg1 = mips32_getconfig1 (); /* * As per PRA, field holds number of entries - 1 * Standard TLBs and dual TLBs have extension fields. */ entries = ((cfg1 & CFG1_MMUS_MASK) >> CFG1_MMUSSHIFT) + 1; tcfg = mips32_getconfig3 (); if ((tcfg & CFG3_M) == 0) goto doReturn; tcfg = mips32_getconfig4 (); #if (__mips_isa_rev < 6) tmp = (tcfg & CFG4_MMUED) >> CFG4_MMUED_SHIFT; /* MMU Extension Definition */ if (tmp == (CFG4_MMUED_FTLBVEXT >> CFG4_MMUED_SHIFT)) goto doFTLBVTLB; /* MMUSizeExt */ if (tmp == (CFG4_MMUED_SIZEEXT >> CFG4_MMUED_SHIFT)) goto doSizeExt; if (tmp == 0) goto doReturn; goto doFTLBSize; doSizeExt: entries += ((tcfg & CFG4_MMUSE_MASK) >> CFG4_MMUSE_SHIFT) << CFG1_MMUS_BITS; goto doReturn; #endif doFTLBVTLB: entries += ((tcfg & CFG4_VTLBSEXT_MASK) >> CFG4_VTLBSEXT_SHIFT) << CFG1_MMUS_BITS; doFTLBSize: /* Skip FTLB size calculations if Config:MT != 4 */ if (cfg != (CFG0_MT_DUAL >> CFG0_MT_SHIFT)) goto doReturn; /* Ways */ ways = 2 + ((tcfg & CFG4_FTLBW_MASK) >> CFG4_FTLBW_SHIFT); /* Sets per way */ tmp = ((tcfg & CFG4_FTLBS_MASK) >> CFG4_FTLBS_SHIFT); sets = 1 << tmp; /* Total sets */ entries += ways << tmp; doReturn: *pentries = entries; *psets = sets; return; } /* * Return number of entries in TLB * This function is used for both mips64 and mips32 */ int _MIPS_HAL_NOMIPS16 mips_tlb_size (void) { int entries = 0, sets = 0; mips_tlb_entries_sets (&entries, &sets); (void) sets; return entries; } /* * Invalidate the whole TLB. * This function is used for both mips64 and mips32 */ void __attribute__ ((use_hazard_barrier_return)) _MIPS_HAL_NOMIPS16 mips_tlbinvalall (void) { unsigned int cfg0, cfg; unsigned long tmp_hi, tmp_hi2; int entries = 0, sets = 0, tlb_stride = 0; int end_ptr = 0, index = 0; register const unsigned long zero = 0; extern void *__tlb_stride_length; cfg0 = mips32_getconfig (); cfg0 = (cfg0 & CFG0_MT_MASK) >> CFG0_MT_SHIFT; if ((cfg0 == 0) /* No MMU */ || (cfg0 == (CFG0_MT_FIXED >> CFG0_MT_SHIFT)) /* fixed address translation */ || (cfg0 == (CFG0_MT_BAT >> CFG0_MT_SHIFT))) /* block address translator */ goto doReturn; mips_setentrylo0 (zero); mips_setentrylo1 (zero); mips_setpagemask (zero); /* Fetch size & number of sets */ mips_tlb_entries_sets (&entries, &sets); cfg = mips32_getconfig3 (); if ((cfg & CFG3_M) == 0) goto doBasicInval; cfg = mips32_getconfig4 (); cfg = (cfg & CFG4_IE_MASK) >> CFG4_IE_SHIFT; /* If Config4[IE] = 0, use old method for invalidation */ if (cfg == 0) goto doBasicInval; /* If Config4[IE] = 1, EHINV loop */ if (cfg == (CFG4_IE_EHINV >> CFG4_IE_SHIFT)) goto doEHINV; /* If Config[MT] = 1, one instruction required */ if (cfg0 == (CFG0_MT_TLB >> CFG0_MT_SHIFT) || cfg == (CFG4_IE_INVALL >> CFG4_IE_SHIFT)) { /* TLB walk done by hardware, Config4[IE] = 3 or Config[MT] = 1 */ mips32_setindex (zero); mips_ehb (); mips_eva_tlbinvf (); goto doReturn; } /* * TLB walk done by software, Config4[IE] = 2, Config[MT] = 4 * * One TLBINVF is executed with an index in VTLB range to * invalidate all VTLB entries. * * One TLBINVF is executed per FTLB set. * * We'll clean out the TLB by computing the Size of the VTLB * but not add the 1. This will give us a finger that points * at the last VTLB entry. */ /* Clear VTLB */ mips32_setindex (zero); mips_ehb (); mips_eva_tlbinvf (); tlb_stride = (int) ((unsigned long) &__tlb_stride_length); sets = sets * tlb_stride; end_ptr = entries - sets; do { entries = entries - tlb_stride; mips32_setindex (entries); mips_ehb (); mips_eva_tlbinvf (); } while (entries != end_ptr); goto doReturn; doEHINV: /* * Config4[IE] = 1. EHINV supported, but not tlbinvf. * * Invalidate the TLB for R3 onwards by loading EHINV and writing to all * TLB entries. */ index = 0; tmp_hi = C0_ENTRYHI_EHINV_MASK; mips_setentryhi (tmp_hi); do { mips32_setindex (index); mips_ehb (); /* mtc0 hazard on tlbwi */ mips_tlbwi (); index++; } while (entries != index); goto doReturn; doBasicInval: /* * Perform a basic invalidation of the TLB for R1 onwards by loading * 0x(FFFFFFFF)KSEG0_BASE into EntryHi and writing it into index 0 * incrementing by a pagesize, writing into index 1, etc. * If large physical addressing is enabled, load 0xFFFFFFFF * into the top half of EntryHi. */ tmp_hi = 0; cfg = mips32_getconfig3 (); /* If XPA is present */ if ((cfg & CFG3_LPA) != 0) { cfg = mips32_getpagegrain (); if ((cfg & PAGEGRAIN_ELPA) == 0) tmp_hi = 0xFFFFFFFF; } tmp_hi2 = (unsigned long) KSEG0_BASE - 0x4000; index = 0; do { tmp_hi2 += 0x4000; mips_setentryhi (tmp_hi2); if (tmp_hi != 0) mips32_sethientryhi (tmp_hi); mips_ehb (); /* mtc0 hazard on tlbp */ mips_tlbp (); mips_ehb (); /* tlbp hazard on mfc0 */ if (mips32_getindex () == 0) continue; mips32_setindex (index); mips_ehb (); /* mtc0 hazard on tlbwi */ mips_tlbwi (); index++; } while (entries != index); doReturn: /* * Clear EntryHI. The upper half is cleared * autmatically as mtc0 writes zeroes. */ mips_setentryhi (zero); return; } _MIPS_HAL_NOMIPS16 int m64_tlb_size (void) __attribute__ ((alias ("mips_tlb_size"))); _MIPS_HAL_NOMIPS16 void m64_tlbinvalall (void) __attribute__ ((alias ("mips_tlbinvalall")));