summaryrefslogtreecommitdiff
path: root/src/arch/arm/tlb.cc
diff options
context:
space:
mode:
authorAli Saidi <Ali.Saidi@ARM.com>2010-06-02 12:58:16 -0500
committerAli Saidi <Ali.Saidi@ARM.com>2010-06-02 12:58:16 -0500
commitcb9936cfdefdebf2c0b950f93a62d504d356524d (patch)
tree3280784b875ccd23475c3f08edc774b50ef1c97d /src/arch/arm/tlb.cc
parentf246be4cbc27b4173f6917b430a31b9a39cdb380 (diff)
downloadgem5-cb9936cfdefdebf2c0b950f93a62d504d356524d.tar.xz
ARM: Implement the ARM TLB/Tablewalker. Needs performance improvements.
Diffstat (limited to 'src/arch/arm/tlb.cc')
-rw-r--r--src/arch/arm/tlb.cc362
1 files changed, 325 insertions, 37 deletions
diff --git a/src/arch/arm/tlb.cc b/src/arch/arm/tlb.cc
index bbf9232c5..362020a91 100644
--- a/src/arch/arm/tlb.cc
+++ b/src/arch/arm/tlb.cc
@@ -47,6 +47,7 @@
#include "arch/arm/faults.hh"
#include "arch/arm/pagetable.hh"
+#include "arch/arm/table_walker.hh"
#include "arch/arm/tlb.hh"
#include "arch/arm/utility.hh"
#include "base/inifile.hh"
@@ -57,16 +58,19 @@
#include "params/ArmTLB.hh"
#include "sim/process.hh"
-
using namespace std;
using namespace ArmISA;
TLB::TLB(const Params *p)
: BaseTLB(p), size(p->size), nlu(0)
+#if FULL_SYSTEM
+ , tableWalker(p->walker)
+#endif
{
- table = new ArmISA::PTE[size];
- memset(table, 0, sizeof(ArmISA::PTE[size]));
+ table = new TlbEntry[size];
+ memset(table, 0, sizeof(TlbEntry[size]));
+ tableWalker->setTlb(this);
}
TLB::~TLB()
@@ -75,50 +79,157 @@ TLB::~TLB()
delete [] table;
}
-ArmISA::PTE *
-TLB::lookup(Addr vpn, uint8_t asn) const
+TlbEntry*
+TLB::lookup(Addr va, uint8_t cid)
{
- panic("lookup() not implemented for ARM\n");
+ // XXX This should either turn into a TlbMap or add caching
+
+ TlbEntry *retval = NULL;
+
+ // Do some kind of caching, fast indexing, anything
+
+ int x = 0;
+ while (retval == NULL && x < size) {
+ if (table[x].match(va, cid)) {
+ retval = &table[x];
+ if (x == nlu)
+ nextnlu();
+
+ break;
+ }
+ x++;
+ }
+
+ 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;
}
// insert a new TLB entry
void
-TLB::insert(Addr addr, ArmISA::PTE &pte)
+TLB::insert(Addr addr, TlbEntry &entry)
+{
+ 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[nlu].valid)
+ DPRINTF(TLB, " - Replacing Valid entry %#x, asn %d ppn %#x size: %#x ap:%d\n",
+ table[nlu].vpn << table[nlu].N, table[nlu].asid, table[nlu].pfn << table[nlu].N,
+ table[nlu].size, table[nlu].ap);
+
+ // XXX Update caching, lookup table etc
+ table[nlu] = entry;
+
+ // XXX Figure out how entries are generally inserted in ARM
+ nextnlu();
+}
+
+void
+TLB::printTlb()
{
- fatal("TLB Insert not yet implemented\n");
+ 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++;
+ }
}
+
void
TLB::flushAll()
{
- DPRINTF(TLB, "flushAll\n");
- memset(table, 0, sizeof(ArmISA::PTE[size]));
- lookupTable.clear();
+ 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]));
nlu = 0;
}
+
+void
+TLB::flushMvaAsid(Addr mva, uint64_t asn)
+{
+ 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);
+ }
+}
+
void
-TLB::serialize(ostream &os)
+TLB::flushAsid(uint64_t asn)
{
- SERIALIZE_SCALAR(size);
- SERIALIZE_SCALAR(nlu);
+ DPRINTF(TLB, "Flushing all entries with asid: %#x\n", asn);
- for (int i = 0; i < size; i++) {
- nameOut(os, csprintf("%s.PTE%d", name(), i));
- table[i].serialize(os);
+ 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::flushMva(Addr mva)
+{
+ 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)
+{
+ panic("Implement Serialize\n");
+}
+
+void
TLB::unserialize(Checkpoint *cp, const string &section)
{
- UNSERIALIZE_SCALAR(size);
- UNSERIALIZE_SCALAR(nlu);
panic("Need to properly unserialize TLB\n");
- for (int i = 0; i < size; i++) {
- table[i].unserialize(cp, csprintf("%s.PTE%d", section, i));
- }
}
void
@@ -182,50 +293,227 @@ TLB::regStats()
}
Fault
-TLB::translateAtomic(RequestPtr req, ThreadContext *tc, Mode mode)
+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,
+ uint8_t domain, bool sNp)
+{
+ return NoFault;
+}
+
+#if !FULL_SYSTEM
+Fault
+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::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 (!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);
+ }
+ 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) {
+ // 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;
+ if (fault)
+ return fault;
+
+ te = lookup(vaddr, context_id);
+ if (!te)
+ printTlb();
+ assert(te);
+ }
+
+ 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;
+
+ switch (ap) {
+ case 0:
+ 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);
+ 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 *