* 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()
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 §ion)
{
- 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
.desc("DTB misses")
;
- invalids
- .name(name() + ".invalids")
- .desc("DTB access violations")
- ;
-
accesses
.name(name() + ".accesses")
.desc("DTB accesses")
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 *