SPARC: Get rid of the copy/pasted StackTrace stolen from Alpha.
[gem5.git] / src / arch / arm / tlb.cc
index f9257a005df3d86c42aadd1f22a6f96e65ed6900..c0ebb52b2143b5f496625bbbab48e278141a81e5 100644 (file)
@@ -12,8 +12,6 @@
  * modified or unmodified, in source code or in binary form.
  *
  * Copyright (c) 2001-2005 The Regents of The University of Michigan
- * Copyright (c) 2007 MIPS Technologies, Inc.
- * Copyright (c) 2007-2008 The Florida State University
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * Authors: Nathan Binkert
+ * Authors: Ali Saidi
+ *          Nathan Binkert
  *          Steve Reinhardt
- *          Jaidev Patwardhan
- *          Stephen Hines
  */
 
 #include <string>
 #include "params/ArmTLB.hh"
 #include "sim/process.hh"
 
+#if FULL_SYSTEM
+#include "arch/arm/table_walker.hh"
+#endif
 
 using namespace std;
 using namespace ArmISA;
 
-///////////////////////////////////////////////////////////////////////
-//
-//  ARM TLB
-//
-
-#define MODE2MASK(X)                   (1 << (X))
-
 TLB::TLB(const Params *p)
-    : BaseTLB(p), size(p->size), nlu(0)
+    : BaseTLB(p), size(p->size)
+#if FULL_SYSTEM
+      , tableWalker(p->walker)
+#endif
+    , rangeMRU(1)
 {
-    table = new ArmISA::PTE[size];
-    memset(table, 0, sizeof(ArmISA::PTE[size]));
-    smallPages=0;
+    table = new TlbEntry[size];
+    memset(table, 0, sizeof(TlbEntry[size]));
+
+#if FULL_SYSTEM
+    tableWalker->setTlb(this);
+#endif
 }
 
 TLB::~TLB()
@@ -85,146 +85,174 @@ TLB::~TLB()
         delete [] table;
 }
 
-// look up an entry in the TLB
-ArmISA::PTE *
-TLB::lookup(Addr vpn, uint8_t asn) const
+bool
+TLB::translateFunctional(ThreadContext *tc, Addr va, Addr &pa)
 {
-    // assume not found...
-    ArmISA::PTE *retval = NULL;
-    PageTable::const_iterator i = lookupTable.find(vpn);
-    if (i != lookupTable.end()) {
-        while (i->first == vpn) {
-            int index = i->second;
-            ArmISA::PTE *pte = &table[index];
-
-            /* 1KB TLB Lookup code - from ARM ARM Volume III - Rev. 2.50 */
-            Addr Mask = pte->Mask;
-            Addr InvMask = ~Mask;
-            Addr VPN  = pte->VPN;
-            //     warn("Valid: %d - %d\n",pte->V0,pte->V1);
-            if(((vpn & InvMask) == (VPN & InvMask)) && (pte->G  || (asn == pte->asid)))
-              { // We have a VPN + ASID Match
-                retval = pte;
-                break;
-              }
-            ++i;
+    uint32_t context_id = tc->readMiscReg(MISCREG_CONTEXTIDR);
+    TlbEntry *e = lookup(va, context_id, true);
+    if (!e)
+        return false;
+    pa = e->pAddr(va);
+    return true;
+}
+
+TlbEntry*
+TLB::lookup(Addr va, uint8_t cid, bool functional)
+{
+
+    TlbEntry *retval = NULL;
+
+    // Maitaining LRU array
+
+    int x = 0;
+    while (retval == NULL && x < size) {
+        if (table[x].match(va, cid)) {
+
+            // We only move the hit entry ahead when the position is higher than rangeMRU
+            if (x > rangeMRU) {
+                TlbEntry tmp_entry = table[x];
+                for(int i = x; i > 0; i--)
+                    table[i] = table[i-1];
+                table[0] = tmp_entry;
+                retval = &table[0];
+            } else {
+                retval = &table[x];
+            }
+            break;
         }
+        x++;
     }
 
-    DPRINTF(TLB, "lookup %#x, asn %#x -> %s ppn %#x\n", vpn, (int)asn,
-            retval ? "hit" : "miss", retval ? retval->PFN1 : 0);
+    DPRINTF(TLBVerbose, "Lookup %#x, cid %#x -> %s ppn %#x size: %#x pa: %#x ap:%d\n",
+            va, cid, retval ? "hit" : "miss", retval ? retval->pfn : 0,
+            retval ? retval->size : 0, retval ? retval->pAddr(va) : 0,
+            retval ? retval->ap : 0);
+    ;
     return retval;
 }
 
-ArmISA::PTE* TLB::getEntry(unsigned Index) const
+// insert a new TLB entry
+void
+TLB::insert(Addr addr, TlbEntry &entry)
 {
-    // Make sure that Index is valid
-    assert(Index<size);
-    return &table[Index];
+    DPRINTF(TLB, "Inserting entry into TLB with pfn:%#x size:%#x vpn: %#x"
+            " asid:%d N:%d global:%d valid:%d nc:%d sNp:%d xn:%d ap:%#x"
+            " domain:%#x\n", entry.pfn, entry.size, entry.vpn, entry.asid,
+            entry.N, entry.global, entry.valid, entry.nonCacheable, entry.sNp,
+            entry.xn, entry.ap, entry.domain);
+
+    if (table[size-1].valid)
+        DPRINTF(TLB, " - Replacing Valid entry %#x, asn %d ppn %#x size: %#x ap:%d\n",
+                table[size-1].vpn << table[size-1].N, table[size-1].asid,
+                table[size-1].pfn << table[size-1].N, table[size-1].size,
+                table[size-1].ap);
+
+    //inserting to MRU position and evicting the LRU one
+
+    for(int i = size-1; i > 0; i--)
+      table[i] = table[i-1];
+    table[0] = entry;
 }
 
-int TLB::probeEntry(Addr vpn,uint8_t asn) const
+void
+TLB::printTlb()
 {
-    // assume not found...
-    ArmISA::PTE *retval = NULL;
-    int Ind=-1;
-    PageTable::const_iterator i = lookupTable.find(vpn);
-    if (i != lookupTable.end()) {
-        while (i->first == vpn) {
-            int index = i->second;
-            ArmISA::PTE *pte = &table[index];
-
-            /* 1KB TLB Lookup code - from ARM ARM Volume III - Rev. 2.50 */
-            Addr Mask = pte->Mask;
-            Addr InvMask = ~Mask;
-            Addr VPN  = pte->VPN;
-            if(((vpn & InvMask) == (VPN & InvMask)) && (pte->G  || (asn == pte->asid)))
-              { // We have a VPN + ASID Match
-                retval = pte;
-                Ind = index;
-                break;
-              }
-
-            ++i;
-        }
+    int x = 0;
+    TlbEntry *te;
+    DPRINTF(TLB, "Current TLB contents:\n");
+    while (x < size) {
+       te = &table[x];
+       if (te->valid)
+           DPRINTF(TLB, " *  %#x, asn %d ppn %#x size: %#x ap:%d\n",
+                te->vpn << te->N, te->asid, te->pfn << te->N, te->size, te->ap);
+       x++;
     }
-    DPRINTF(Arm,"VPN: %x, asid: %d, Result of TLBP: %d\n",vpn,asn,Ind);
-    return Ind;
 }
-Fault inline
-TLB::checkCacheability(RequestPtr &req)
+
+
+void
+TLB::flushAll()
 {
-  Addr VAddrUncacheable = 0xA0000000;
-  // In ARM, cacheability is controlled by certain bits of the virtual address
-  // or by the TLB entry
-  if((req->getVaddr() & VAddrUncacheable) == VAddrUncacheable) {
-    // mark request as uncacheable
-    req->setFlags(Request::UNCACHEABLE);
-  }
-  return NoFault;
+    DPRINTF(TLB, "Flushing all TLB entries\n");
+    int x = 0;
+    TlbEntry *te;
+    while (x < size) {
+       te = &table[x];
+       if (te->valid)
+           DPRINTF(TLB, " -  %#x, asn %d ppn %#x size: %#x ap:%d\n",
+                te->vpn << te->N, te->asid, te->pfn << te->N, te->size, te->ap);
+       x++;
+    }
+
+    memset(table, 0, sizeof(TlbEntry[size]));
 }
-void TLB::insertAt(ArmISA::PTE &pte, unsigned Index, int _smallPages)
+
+
+void
+TLB::flushMvaAsid(Addr mva, uint64_t asn)
 {
-  smallPages=_smallPages;
-  if(Index > size){
-    warn("Attempted to write at index (%d) beyond TLB size (%d)",Index,size);
-  } else {
-    // Update TLB
-    DPRINTF(TLB,"TLB[%d]: %x %x %x %x\n",Index,pte.Mask<<11,((pte.VPN << 11) | pte.asid),((pte.PFN0 <<6) | (pte.C0 << 3) | (pte.D0 << 2) | (pte.V0 <<1) | pte.G),
-            ((pte.PFN1 <<6) | (pte.C1 << 3) | (pte.D1 << 2) | (pte.V1 <<1) | pte.G));
-    if(table[Index].V0 == true || table[Index].V1 == true){ // Previous entry is valid
-      PageTable::iterator i = lookupTable.find(table[Index].VPN);
-      lookupTable.erase(i);
+    DPRINTF(TLB, "Flushing mva %#x asid: %#x\n", mva, asn);
+    TlbEntry *te;
+
+    te = lookup(mva, asn);
+    while (te != NULL) {
+     DPRINTF(TLB, " -  %#x, asn %d ppn %#x size: %#x ap:%d\n",
+            te->vpn << te->N, te->asid, te->pfn << te->N, te->size, te->ap);
+        te->valid = false;
+        te = lookup(mva,asn);
     }
-    table[Index]=pte;
-    // Update fast lookup table
-    lookupTable.insert(make_pair(table[Index].VPN, Index));
-    //    int TestIndex=probeEntry(pte.VPN,pte.asid);
-    //    warn("Inserted at: %d, Found at: %d (%x)\n",Index,TestIndex,pte.Mask);
-  }
-
 }
 
-// insert a new TLB entry
 void
-TLB::insert(Addr addr, ArmISA::PTE &pte)
+TLB::flushAsid(uint64_t asn)
 {
-  fatal("TLB Insert not yet implemented\n");
+    DPRINTF(TLB, "Flushing all entries with asid: %#x\n", asn);
+
+    int x = 0;
+    TlbEntry *te;
+
+    while (x < size) {
+        te = &table[x];
+        if (te->asid == asn) {
+            te->valid = false;
+            DPRINTF(TLB, " -  %#x, asn %d ppn %#x size: %#x ap:%d\n",
+                te->vpn << te->N, te->asid, te->pfn << te->N, te->size, te->ap);
+        }
+        x++;
+    }
 }
 
 void
-TLB::flushAll()
+TLB::flushMva(Addr mva)
 {
-    DPRINTF(TLB, "flushAll\n");
-    memset(table, 0, sizeof(ArmISA::PTE[size]));
-    lookupTable.clear();
-    nlu = 0;
+    DPRINTF(TLB, "Flushing all entries with mva: %#x\n", mva);
+
+    int x = 0;
+    TlbEntry *te;
+
+    while (x < size) {
+        te = &table[x];
+        Addr v = te->vpn << te->N;
+        if (mva >= v && mva < v + te->size) {
+            te->valid = false;
+            DPRINTF(TLB, " -  %#x, asn %d ppn %#x size: %#x ap:%d\n",
+                te->vpn << te->N, te->asid, te->pfn << te->N, te->size, te->ap);
+        }
+        x++;
+    }
 }
 
 void
 TLB::serialize(ostream &os)
 {
-    SERIALIZE_SCALAR(size);
-    SERIALIZE_SCALAR(nlu);
-
-    for (int i = 0; i < size; i++) {
-        nameOut(os, csprintf("%s.PTE%d", name(), i));
-        table[i].serialize(os);
-    }
+    panic("Implement Serialize\n");
 }
 
 void
 TLB::unserialize(Checkpoint *cp, const string &section)
 {
-    UNSERIALIZE_SCALAR(size);
-    UNSERIALIZE_SCALAR(nlu);
 
-    for (int i = 0; i < size; i++) {
-        table[i].unserialize(cp, csprintf("%s.PTE%d", section, i));
-        if (table[i].V0 || table[i].V1) {
-            lookupTable.insert(make_pair(table[i].VPN, i));
-        }
-    }
+    panic("Need to properly unserialize TLB\n");
 }
 
 void
@@ -272,11 +300,6 @@ TLB::regStats()
         .desc("DTB misses")
         ;
 
-    invalids
-        .name(name() + ".invalids")
-        .desc("DTB access violations")
-        ;
-
     accesses
         .name(name() + ".accesses")
         .desc("DTB accesses")
@@ -287,62 +310,287 @@ TLB::regStats()
     accesses = read_accesses + write_accesses;
 }
 
+#if !FULL_SYSTEM
 Fault
-TLB::translateAtomic(RequestPtr req, ThreadContext *tc, Mode mode)
+TLB::translateSe(RequestPtr req, ThreadContext *tc, Mode mode,
+        Translation *translation, bool &delay, bool timing)
 {
+    // XXX Cache misc registers and have miscreg write function inv cache
     Addr vaddr = req->getVaddr() & ~PcModeMask;
     SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR);
     uint32_t flags = req->getFlags();
 
-    if (mode != Execute) {
-        assert(flags & MustBeOne);
+    bool is_fetch = (mode == Execute);
+    bool is_write = (mode == Write);
 
-        if (sctlr.a || (flags & AllowUnaligned) == 0) {
-            if ((vaddr & flags & AlignmentMask) != 0) {
-                return new DataAbort(vaddr, (mode == Write), 0,
-                            ArmFault::AlignmentFault);
+    if (!is_fetch) {
+        assert(flags & MustBeOne);
+        if (sctlr.a || !(flags & AllowUnaligned)) {
+            if (vaddr & flags & AlignmentMask) {
+                return new DataAbort(vaddr, 0, is_write, ArmFault::AlignmentFault);
             }
         }
     }
-#if !FULL_SYSTEM
-    Process * p = tc->getProcessPtr();
 
     Addr paddr;
+    Process *p = tc->getProcessPtr();
+
     if (!p->pTable->translate(vaddr, paddr))
         return Fault(new GenericPageTableFault(vaddr));
     req->setPaddr(paddr);
 
     return NoFault;
-#else
+}
+
+#else // FULL_SYSTEM
+
+Fault
+TLB::trickBoxCheck(RequestPtr req, Mode mode, uint8_t domain, bool sNp)
+{
+    return NoFault;
+}
+
+Fault
+TLB::walkTrickBoxCheck(Addr pa, Addr va, Addr sz, bool is_exec,
+        bool is_write, uint8_t domain, bool sNp)
+{
+    return NoFault;
+}
+
+Fault
+TLB::translateFs(RequestPtr req, ThreadContext *tc, Mode mode,
+        Translation *translation, bool &delay, bool timing)
+{
+    // XXX Cache misc registers and have miscreg write function inv cache
+    Addr vaddr = req->getVaddr() & ~PcModeMask;
+    SCTLR sctlr = tc->readMiscReg(MISCREG_SCTLR);
+    CPSR cpsr = tc->readMiscReg(MISCREG_CPSR);
+    uint32_t flags = req->getFlags();
+
+    bool is_fetch = (mode == Execute);
+    bool is_write = (mode == Write);
+    bool is_priv = (cpsr.mode != MODE_USER) && !(flags & UserMode);
+
+    DPRINTF(TLBVerbose, "CPSR is user:%d UserMode:%d\n", cpsr.mode == MODE_USER, flags
+            & UserMode);
+    // If this is a clrex instruction, provide a PA of 0 with no fault
+    // This will force the monitor to set the tracked address to 0
+    // a bit of a hack but this effectively clrears this processors monitor
+    if (flags & Request::CLEAR_LL){
+       req->setPaddr(0);
+       req->setFlags(Request::UNCACHEABLE);
+       req->setFlags(Request::CLEAR_LL);
+       return NoFault;
+    }
+    if ((req->isInstFetch() && (!sctlr.i)) ||
+        ((!req->isInstFetch()) && (!sctlr.c))){
+       req->setFlags(Request::UNCACHEABLE);
+    }
+    if (!is_fetch) {
+        assert(flags & MustBeOne);
+        if (sctlr.a || !(flags & AllowUnaligned)) {
+            if (vaddr & flags & AlignmentMask) {
+                return new DataAbort(vaddr, 0, is_write, ArmFault::AlignmentFault);
+            }
+        }
+    }
+
+    uint32_t context_id = tc->readMiscReg(MISCREG_CONTEXTIDR);
+    Fault fault;
+
+
     if (!sctlr.m) {
         req->setPaddr(vaddr);
+        if (sctlr.tre == 0) {
+            req->setFlags(Request::UNCACHEABLE);
+        } else {
+            PRRR prrr = tc->readMiscReg(MISCREG_PRRR);
+            NMRR nmrr = tc->readMiscReg(MISCREG_NMRR);
+
+            if (nmrr.ir0 == 0 || nmrr.or0 == 0 || prrr.tr0 != 0x2)
+               req->setFlags(Request::UNCACHEABLE);
+        }
+
+        // Set memory attributes
+        TlbEntry temp_te;
+        tableWalker->memAttrs(tc, temp_te, sctlr, 0, 1);
+        temp_te.shareable = true;
+        DPRINTF(TLBVerbose, "(No MMU) setting memory attributes: shareable:\
+                %d, innerAttrs: %d, outerAttrs: %d\n", temp_te.shareable,
+                temp_te.innerAttrs, temp_te.outerAttrs);
+        setAttr(temp_te.attributes);
+
+        return trickBoxCheck(req, mode, 0, false);
+    }
+
+    DPRINTF(TLBVerbose, "Translating vaddr=%#x context=%d\n", vaddr, context_id);
+    // Translation enabled
+
+    TlbEntry *te = lookup(vaddr, context_id);
+    if (te == NULL) {
+        if (req->isPrefetch()){
+           //if the request is a prefetch don't attempt to fill the TLB
+           //or go any further with the memory access
+           return new PrefetchAbort(vaddr, ArmFault::PrefetchTLBMiss);
+        }
+        // start translation table walk, pass variables rather than
+        // re-retreaving in table walker for speed
+        DPRINTF(TLB, "TLB Miss: Starting hardware table walker for %#x(%d)\n",
+                vaddr, context_id);
+        fault = tableWalker->walk(req, tc, context_id, mode, translation,
+                timing);
+        if (timing) {
+            delay = true;
+            // for timing mode, return and wait for table walk
+            return fault;
+        }
+        if (fault)
+            return fault;
+
+        te = lookup(vaddr, context_id);
+        if (!te)
+            printTlb();
+        assert(te);
+    }
+
+    // Set memory attributes
+    DPRINTF(TLBVerbose,
+            "Setting memory attributes: shareable: %d, innerAttrs: %d, \
+            outerAttrs: %d\n",
+            te->shareable, te->innerAttrs, te->outerAttrs);
+    setAttr(te->attributes);
+    if (te->nonCacheable)
+        req->setFlags(Request::UNCACHEABLE);
+    uint32_t dacr = tc->readMiscReg(MISCREG_DACR);
+    switch ( (dacr >> (te->domain * 2)) & 0x3) {
+      case 0:
+        DPRINTF(TLB, "TLB Fault: Data abort on domain. DACR: %#x domain: %#x"
+               " write:%d sNp:%d\n", dacr, te->domain, is_write, te->sNp);
+        if (is_fetch)
+            return new PrefetchAbort(vaddr,
+                (te->sNp ? ArmFault::Domain0 : ArmFault::Domain1));
+        else
+            return new DataAbort(vaddr, te->domain, is_write,
+                (te->sNp ? ArmFault::Domain0 : ArmFault::Domain1));
+      case 1:
+        // Continue with permissions check
+        break;
+      case 2:
+        panic("UNPRED domain\n");
+      case 3:
+        req->setPaddr(te->pAddr(vaddr));
+        fault = trickBoxCheck(req, mode, te->domain, te->sNp);
+        if (fault)
+            return fault;
         return NoFault;
     }
-    warn_once("MPU translation not implemented\n");
-    req->setPaddr(vaddr);
+
+    uint8_t ap = te->ap;
+
+    if (sctlr.afe == 1)
+        ap |= 1;
+
+    bool abt;
+
+   /* if (!sctlr.xp)
+        ap &= 0x3;
+*/
+    switch (ap) {
+      case 0:
+        DPRINTF(TLB, "Access permissions 0, checking rs:%#x\n", (int)sctlr.rs);
+        if (!sctlr.xp) {
+            switch ((int)sctlr.rs) {
+              case 2:
+                abt = is_write;
+                break;
+              case 1:
+                abt = is_write || !is_priv;
+                break;
+              case 0:
+              case 3:
+              default:
+                abt = true;
+                break;
+            }
+        } else {
+            abt = true;
+        }
+        break;
+      case 1:
+        abt = !is_priv;
+        break;
+      case 2:
+        abt = !is_priv && is_write;
+        break;
+      case 3:
+        abt = false;
+        break;
+      case 4:
+        panic("UNPRED premissions\n");
+      case 5:
+        abt = !is_priv || is_write;
+        break;
+      case 6:
+      case 7:
+        abt = is_write;
+        break;
+      default:
+        panic("Unknown permissions\n");
+    }
+    if ((is_fetch) && (abt || te->xn)) {
+        DPRINTF(TLB, "TLB Fault: Prefetch abort on permission check. AP:%d priv:%d"
+               " write:%d sNp:%d\n", ap, is_priv, is_write, te->sNp);
+        return new PrefetchAbort(vaddr,
+                (te->sNp ? ArmFault::Permission0 :
+                 ArmFault::Permission1));
+    } else if (abt) {
+        DPRINTF(TLB, "TLB Fault: Data abort on permission check. AP:%d priv:%d"
+               " write:%d sNp:%d\n", ap, is_priv, is_write, te->sNp);
+        return new DataAbort(vaddr, te->domain, is_write,
+                (te->sNp ? ArmFault::Permission0 :
+                 ArmFault::Permission1));
+    }
+
+    req->setPaddr(te->pAddr(vaddr));
+    // Check for a trickbox generated address fault
+    fault = trickBoxCheck(req, mode, te->domain, te->sNp);
+    if (fault)
+        return fault;
+
     return NoFault;
-    
+}
 
 #endif
+
+Fault
+TLB::translateAtomic(RequestPtr req, ThreadContext *tc, Mode mode)
+{
+    bool delay = false;
+    Fault fault;
+#if FULL_SYSTEM
+    fault = translateFs(req, tc, mode, NULL, delay, false);
+#else
+    fault = translateSe(req, tc, mode, NULL, delay, false);
+#endif
+    assert(!delay);
+    return fault;
 }
 
-void
+Fault
 TLB::translateTiming(RequestPtr req, ThreadContext *tc,
         Translation *translation, Mode mode)
 {
     assert(translation);
-    translation->finish(translateAtomic(req, tc, mode), req, tc, mode);
-}
-
-ArmISA::PTE &
-TLB::index(bool advance)
-{
-    ArmISA::PTE *pte = &table[nlu];
-
-    if (advance)
-        nextnlu();
-
-    return *pte;
+    bool delay = false;
+    Fault fault;
+#if FULL_SYSTEM
+    fault = translateFs(req, tc, mode, translation, delay, true);
+#else
+    fault = translateSe(req, tc, mode, translation, delay, true);
+#endif
+    if (!delay)
+        translation->finish(fault, req, tc, mode);
+    return fault;
 }
 
 ArmISA::TLB *