From cb9936cfdefdebf2c0b950f93a62d504d356524d Mon Sep 17 00:00:00 2001 From: Ali Saidi Date: Wed, 2 Jun 2010 12:58:16 -0500 Subject: ARM: Implement the ARM TLB/Tablewalker. Needs performance improvements. --- src/arch/arm/tlb.cc | 362 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 325 insertions(+), 37 deletions(-) (limited to 'src/arch/arm/tlb.cc') 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 §ion) { - 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 * -- cgit v1.2.3