diff options
author | Ali Saidi <Ali.Saidi@ARM.com> | 2010-06-02 12:58:16 -0500 |
---|---|---|
committer | Ali Saidi <Ali.Saidi@ARM.com> | 2010-06-02 12:58:16 -0500 |
commit | cb9936cfdefdebf2c0b950f93a62d504d356524d (patch) | |
tree | 3280784b875ccd23475c3f08edc774b50ef1c97d /src/arch | |
parent | f246be4cbc27b4173f6917b430a31b9a39cdb380 (diff) | |
download | gem5-cb9936cfdefdebf2c0b950f93a62d504d356524d.tar.xz |
ARM: Implement the ARM TLB/Tablewalker. Needs performance improvements.
Diffstat (limited to 'src/arch')
-rw-r--r-- | src/arch/arm/ArmTLB.py | 29 | ||||
-rw-r--r-- | src/arch/arm/SConscript | 2 | ||||
-rw-r--r-- | src/arch/arm/faults.hh | 11 | ||||
-rw-r--r-- | src/arch/arm/isa.hh | 51 | ||||
-rw-r--r-- | src/arch/arm/isa/formats/misc.isa | 34 | ||||
-rw-r--r-- | src/arch/arm/isa/insts/ldr.isa | 6 | ||||
-rw-r--r-- | src/arch/arm/isa/insts/str.isa | 13 | ||||
-rw-r--r-- | src/arch/arm/miscregs.cc | 3 | ||||
-rw-r--r-- | src/arch/arm/miscregs.hh | 59 | ||||
-rw-r--r-- | src/arch/arm/pagetable.hh | 112 | ||||
-rw-r--r-- | src/arch/arm/table_walker.cc | 323 | ||||
-rw-r--r-- | src/arch/arm/table_walker.hh | 272 | ||||
-rw-r--r-- | src/arch/arm/tlb.cc | 362 | ||||
-rw-r--r-- | src/arch/arm/tlb.hh | 50 |
14 files changed, 1223 insertions, 104 deletions
diff --git a/src/arch/arm/ArmTLB.py b/src/arch/arm/ArmTLB.py index 3dd2560fe..f0d23445f 100644 --- a/src/arch/arm/ArmTLB.py +++ b/src/arch/arm/ArmTLB.py @@ -1,8 +1,17 @@ # -*- mode:python -*- -# Copyright (c) 2007-2008 The Florida State University +# Copyright (c) 2009 ARM Limited # All rights reserved. # +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright @@ -26,12 +35,28 @@ # (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: Stephen Hines +# Authors: Ali Saidi +from m5.defines import buildEnv from m5.SimObject import SimObject from m5.params import * +from m5.proxy import * + +if buildEnv['FULL_SYSTEM']: + from MemObject import MemObject + + class ArmTableWalker(MemObject): + type = 'ArmTableWalker' + cxx_class = 'ArmISA::TableWalker' + port = Port("Port for TableWalker to do walk the translation with") + sys = Param.System(Parent.any, "system object parameter") + min_backoff = Param.Tick(0, "Minimum backoff delay after failed send") + max_backoff = Param.Tick(100000, "Minimum backoff delay after failed send") + class ArmTLB(SimObject): type = 'ArmTLB' cxx_class = 'ArmISA::TLB' size = Param.Int(64, "TLB size") + if buildEnv['FULL_SYSTEM']: + walker = Param.ArmTableWalker(ArmTableWalker(), "HW Table walker") diff --git a/src/arch/arm/SConscript b/src/arch/arm/SConscript index 73fcc730b..67997f4e0 100644 --- a/src/arch/arm/SConscript +++ b/src/arch/arm/SConscript @@ -65,12 +65,14 @@ if env['TARGET_ISA'] == 'arm': SimObject('ArmTLB.py') TraceFlag('Arm') + TraceFlag('TLBVerbose') TraceFlag('Faults', "Trace Exceptions, interrupts, svc/swi") TraceFlag('Predecoder', "Instructions returned by the predecoder") if env['FULL_SYSTEM']: Source('interrupts.cc') Source('stacktrace.cc') Source('system.cc') + Source('table_walker.cc') SimObject('ArmInterrupts.py') SimObject('ArmSystem.py') diff --git a/src/arch/arm/faults.hh b/src/arch/arm/faults.hh index 7e4013a85..6de9fee28 100644 --- a/src/arch/arm/faults.hh +++ b/src/arch/arm/faults.hh @@ -75,16 +75,17 @@ class ArmFault : public FaultBase Translation1 = 0x7, SynchronousExternalAbort0 = 0x8, Domain0 = 0x9, + SynchronousExternalAbort1 = 0xa, Domain1 = 0xb, - TranslationTableWalk0 = 0xc, + TranslationTableWalkExtAbt0 = 0xc, Permission0 = 0xd, - SynchronousExternalAbort1 = 0xe, + TranslationTableWalkExtAbt1 = 0xe, Permission1 = 0xf, AsynchronousExternalAbort = 0x16, MemoryAccessAsynchronousParityError = 0x18, MemoryAccessSynchronousParityError = 0x19, - TranslationTableWalk1 = 0x1c, - SynchronousParityError = 0x1e + TranslationTableWalkPrtyErr0 = 0x1c, + TranslationTableWalkPrtyErr1 = 0x1e, }; struct FaultVals @@ -208,7 +209,7 @@ class DataAbort : public AbortFault<DataAbort> static const MiscRegIndex FsrIndex = MISCREG_DFSR; static const MiscRegIndex FarIndex = MISCREG_DFAR; - DataAbort(Addr _addr, bool _write, uint8_t _domain, uint8_t _status) : + DataAbort(Addr _addr, uint8_t _domain, bool _write, uint8_t _status) : AbortFault<DataAbort>(_addr, _write, _domain, _status) {} }; diff --git a/src/arch/arm/isa.hh b/src/arch/arm/isa.hh index c9c237946..51503dbf6 100644 --- a/src/arch/arm/isa.hh +++ b/src/arch/arm/isa.hh @@ -41,9 +41,10 @@ */ #ifndef __ARCH_ARM_ISA_HH__ -#define __ARCH_MRM_ISA_HH__ +#define __ARCH_ARM_ISA_HH__ #include "arch/arm/registers.hh" +#include "arch/arm/tlb.hh" #include "arch/arm/types.hh" class ThreadContext; @@ -223,6 +224,8 @@ namespace ArmISA warn("The ccsidr register isn't implemented and " "always reads as 0.\n"); break; + case MISCREG_ID_PFR0: + return 0x1031; // ThumbEE | !Jazelle | Thumb | ARM } return readMiscRegNoEffect(misc_reg); } @@ -347,6 +350,52 @@ namespace ArmISA case MISCREG_MPIDR: case MISCREG_FPSID: return; + case MISCREG_TLBIALLIS: + case MISCREG_TLBIALL: + warn("Need to flush all TLBs in MP\n"); + tc->getITBPtr()->flushAll(); + tc->getDTBPtr()->flushAll(); + return; + case MISCREG_ITLBIALL: + tc->getITBPtr()->flushAll(); + return; + case MISCREG_DTLBIALL: + tc->getDTBPtr()->flushAll(); + return; + case MISCREG_TLBIMVAIS: + case MISCREG_TLBIMVA: + warn("Need to flush all TLBs in MP\n"); + tc->getITBPtr()->flushMvaAsid(mbits(newVal, 31, 12), + bits(newVal, 7,0)); + tc->getDTBPtr()->flushMvaAsid(mbits(newVal, 31, 12), + bits(newVal, 7,0)); + return; + case MISCREG_TLBIASIDIS: + case MISCREG_TLBIASID: + warn("Need to flush all TLBs in MP\n"); + tc->getITBPtr()->flushAsid(bits(newVal, 7,0)); + tc->getDTBPtr()->flushAsid(bits(newVal, 7,0)); + return; + case MISCREG_TLBIMVAAIS: + case MISCREG_TLBIMVAA: + warn("Need to flush all TLBs in MP\n"); + tc->getITBPtr()->flushMva(mbits(newVal, 31,12)); + tc->getDTBPtr()->flushMva(mbits(newVal, 31,12)); + return; + case MISCREG_ITLBIMVA: + tc->getITBPtr()->flushMvaAsid(mbits(newVal, 31, 12), + bits(newVal, 7,0)); + return; + case MISCREG_DTLBIMVA: + tc->getDTBPtr()->flushMvaAsid(mbits(newVal, 31, 12), + bits(newVal, 7,0)); + return; + case MISCREG_ITLBIASID: + tc->getITBPtr()->flushAsid(bits(newVal, 7,0)); + return; + case MISCREG_DTLBIASID: + tc->getDTBPtr()->flushAsid(bits(newVal, 7,0)); + return; } setMiscRegNoEffect(misc_reg, newVal); } diff --git a/src/arch/arm/isa/formats/misc.isa b/src/arch/arm/isa/formats/misc.isa index 1c00a3d6b..be0e63900 100644 --- a/src/arch/arm/isa/formats/misc.isa +++ b/src/arch/arm/isa/formats/misc.isa @@ -138,47 +138,25 @@ let {{ return new WarnUnimplemented( isRead ? "mrc bpiall" : "mcr bpiall", machInst); case MISCREG_TLBIALLIS: - return new WarnUnimplemented( - isRead ? "mrc tlbiallis" : "mcr tlbiallis", machInst); case MISCREG_TLBIMVAIS: - return new WarnUnimplemented( - isRead ? "mrc tlbimvais" : "mcr tlbimvais", machInst); case MISCREG_TLBIASIDIS: - return new WarnUnimplemented( - isRead ? "mrc tlbiasidis" : "mcr tlbiasidis", machInst); case MISCREG_TLBIMVAAIS: - return new WarnUnimplemented( - isRead ? "mrc tlbimvaais" : "mcr tlbimvaais", machInst); case MISCREG_ITLBIALL: - return new WarnUnimplemented( - isRead ? "mrc itlbiall" : "mcr itlbiall", machInst); case MISCREG_ITLBIMVA: - return new WarnUnimplemented( - isRead ? "mrc itlbimva" : "mcr itlbimva", machInst); case MISCREG_ITLBIASID: - return new WarnUnimplemented( - isRead ? "mrc itlbiasid" : "mcr itlbiasid", machInst); case MISCREG_DTLBIALL: - return new WarnUnimplemented( - isRead ? "mrc dtlbiall" : "mcr dtlbiall", machInst); case MISCREG_DTLBIMVA: - return new WarnUnimplemented( - isRead ? "mrc dtlbimva" : "mcr dtlbimva", machInst); case MISCREG_DTLBIASID: - return new WarnUnimplemented( - isRead ? "mrc dtlbiasid" : "mcr dtlbiasid", machInst); case MISCREG_TLBIALL: - return new WarnUnimplemented( - isRead ? "mrc tlbiall" : "mcr tlbiall", machInst); case MISCREG_TLBIMVA: - return new WarnUnimplemented( - isRead ? "mrc tlbimva" : "mcr tlbimva", machInst); case MISCREG_TLBIASID: - return new WarnUnimplemented( - isRead ? "mrc tlbiasid" : "mcr tlbiasid", machInst); case MISCREG_TLBIMVAA: - return new WarnUnimplemented( - isRead ? "mrc tlbimvaa" : "mcr tlbimvaa", machInst); + if (isRead) { + return new Unknown(machInst); + } else { + return new Mcr15(machInst, (IntRegIndex)miscReg, rt); + } + default: if (isRead) { return new Mrc15(machInst, rt, (IntRegIndex)miscReg); diff --git a/src/arch/arm/isa/insts/ldr.isa b/src/arch/arm/isa/insts/ldr.isa index 40d9147df..093ff7a60 100644 --- a/src/arch/arm/isa/insts/ldr.isa +++ b/src/arch/arm/isa/insts/ldr.isa @@ -93,6 +93,9 @@ let {{ eaCode += ";" memFlags = ["ArmISA::TLB::MustBeOne", "%d" % (size - 1)] + if user: + memFlags.append("ArmISA::TLB::UserMode") + if prefetch: Name = "%s_%s" % (mnem.upper(), Name) memFlags.append("Request::PREFETCH") @@ -179,6 +182,9 @@ let {{ eaCode += ";" memFlags = ["%d" % (size - 1), "ArmISA::TLB::MustBeOne"] + if user: + memFlags.append("ArmISA::TLB::UserMode") + if prefetch: Name = "%s_%s" % (mnem.upper(), Name) memFlags.append("Request::PREFETCH") diff --git a/src/arch/arm/isa/insts/str.isa b/src/arch/arm/isa/insts/str.isa index c8d2679fc..d86000947 100644 --- a/src/arch/arm/isa/insts/str.isa +++ b/src/arch/arm/isa/insts/str.isa @@ -107,6 +107,9 @@ let {{ accCode += "Base = Base %s;\n" % offset memFlags = ["ArmISA::TLB::MustBeOne", "%d" % (size - 1)] + if user: + memFlags.append("ArmISA::TLB::UserMode") + if strex: memFlags.append("Request::LLSC") Name = "%s_%s" % (mnem.upper(), Name) @@ -184,10 +187,14 @@ let {{ accCode += "Base = Base %s;\n" % offset base = buildMemBase("MemoryReg", post, writeback) - emitStore(name, Name, False, eaCode, accCode, "",\ - ["ArmISA::TLB::MustBeOne", \ + memFlags = ["ArmISA::TLB::MustBeOne", \ "ArmISA::TLB::AllowUnaligned", \ - "%d" % (size - 1)], [], base) + "%d" % (size - 1)] + if user: + memFlags.append("ArmISA::TLB::UserMode") + + emitStore(name, Name, False, eaCode, accCode, "",\ + memFlags, [], base) def buildDoubleImmStore(mnem, post, add, writeback, \ strex=False, vstr=False): diff --git a/src/arch/arm/miscregs.cc b/src/arch/arm/miscregs.cc index aedc0fce5..e4375fb8a 100644 --- a/src/arch/arm/miscregs.cc +++ b/src/arch/arm/miscregs.cc @@ -38,6 +38,7 @@ */ #include "arch/arm/miscregs.hh" +#include "base/misc.hh" namespace ArmISA { @@ -424,6 +425,8 @@ decodeCP15Reg(unsigned crn, unsigned opc1, unsigned crm, unsigned opc2) // Implementation defined break; } + warn("Unknown miscreg: CRn: %d Opc1: %d CRm: %d opc2: %d\n", + crn, opc1, crm, opc2); // Unrecognized register return NUM_MISCREGS; } diff --git a/src/arch/arm/miscregs.hh b/src/arch/arm/miscregs.hh index cdead8710..42431e777 100644 --- a/src/arch/arm/miscregs.hh +++ b/src/arch/arm/miscregs.hh @@ -130,10 +130,13 @@ namespace ArmISA MISCREG_DFAR, MISCREG_IFAR, MISCREG_MPIDR, + MISCREG_PRRR, + MISCREG_NMRR, + MISCREG_TTBCR, + MISCREG_ID_PFR0, MISCREG_CP15_UNIMP_START, MISCREG_CTR = MISCREG_CP15_UNIMP_START, MISCREG_TCMTR, - MISCREG_ID_PFR0, MISCREG_ID_PFR1, MISCREG_ID_DFR0, MISCREG_ID_AFR0, @@ -159,7 +162,6 @@ namespace ArmISA MISCREG_SCR, MISCREG_SDER, MISCREG_NSACR, - MISCREG_TTBCR, MISCREG_V2PCWPR, MISCREG_V2PCWPW, MISCREG_V2PCWUR, @@ -168,8 +170,6 @@ namespace ArmISA MISCREG_V2POWPW, MISCREG_V2POWUR, MISCREG_V2POWUW, - MISCREG_PRRR, - MISCREG_NMRR, MISCREG_VBAR, MISCREG_MVBAR, MISCREG_ISR, @@ -205,18 +205,20 @@ namespace ArmISA "dtlbiall", "dtlbimva", "dtlbiasid", "tlbiall", "tlbimva", "tlbiasid", "tlbimvaa", "dfsr", "ifsr", "dfar", "ifar", "mpidr", + "prrr", "nmrr", "ttbcr", "id_pfr0", + // Unimplemented below "ctr", "tcmtr", - "id_pfr0", "id_pfr1", "id_dfr0", "id_afr0", + "id_pfr1", "id_dfr0", "id_afr0", "id_mmfr0", "id_mmfr1", "id_mmfr2", "id_mmfr3", "id_isar0", "id_isar1", "id_isar2", "id_isar3", "id_isar4", "id_isar5", "par", "aidr", "actlr", "adfsr", "aifsr", "dcimvac", "dcisw", "mccsw", "dccmvau", - "scr", "sder", "nsacr", "ttbcr", + "scr", "sder", "nsacr", "v2pcwpr", "v2pcwpw", "v2pcwur", "v2pcwuw", "v2powpr", "v2powpw", "v2powur", "v2powuw", - "prrr", "nmrr", "vbar", "mvbar", "isr", "fceidr", + "vbar", "mvbar", "isr", "fceidr", "nop", "raz" }; @@ -343,6 +345,49 @@ namespace ArmISA Bitfield<27, 24> vfpHalfPrecision; Bitfield<31, 28> raz; EndBitUnion(MVFR1) + + BitUnion32(PRRR) + Bitfield<1,0> tr0; + Bitfield<3,2> tr1; + Bitfield<5,4> tr2; + Bitfield<7,6> tr3; + Bitfield<9,8> tr4; + Bitfield<11,10> tr5; + Bitfield<13,12> tr6; + Bitfield<15,14> tr7; + Bitfield<16> ds0; + Bitfield<17> ds1; + Bitfield<18> ns0; + Bitfield<19> ns1; + Bitfield<24> nos0; + Bitfield<25> nos1; + Bitfield<26> nos2; + Bitfield<27> nos3; + Bitfield<28> nos4; + Bitfield<29> nos5; + Bitfield<30> nos6; + Bitfield<31> nos7; + EndBitUnion(PRRR) + + BitUnion32(NMRR) + Bitfield<1,0> ir0; + Bitfield<3,2> ir1; + Bitfield<5,4> ir2; + Bitfield<7,6> ir3; + Bitfield<9,8> ir4; + Bitfield<11,10> ir5; + Bitfield<13,12> ir6; + Bitfield<15,14> ir7; + Bitfield<17,16> or0; + Bitfield<19,18> or1; + Bitfield<21,20> or2; + Bitfield<23,22> or3; + Bitfield<25,24> or4; + Bitfield<27,26> or5; + Bitfield<29,28> or6; + Bitfield<31,30> or7; + EndBitUnion(NMRR) + }; #endif // __ARCH_ARM_MISCREGS_HH__ diff --git a/src/arch/arm/pagetable.hh b/src/arch/arm/pagetable.hh index 3d4943f99..f1e86f0cc 100644 --- a/src/arch/arm/pagetable.hh +++ b/src/arch/arm/pagetable.hh @@ -37,9 +37,7 @@ * (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 - * Steve Reinhardt - * Ali Saidi + * Authors: Ali Saidi */ #ifndef __ARCH_ARM_PAGETABLE_H__ @@ -73,22 +71,91 @@ struct PTE }; -// ITB/DTB table entry -struct TlbEntry +struct TlbRange { - Addr tag; // virtual page number tag - Addr ppn; // physical page number - uint8_t asn; // address space number - bool valid; // valid page table entry + Addr va; + Addr size; + int contextId; + bool global; + inline bool + operator<(const TlbRange &r2) const + { + if (!(global || r2.global)) { + if (contextId < r2.contextId) + return true; + else if (contextId > r2.contextId) + return false; + } + + if (va < r2.va) + return true; + return false; + } - //Construct an entry that maps to physical address addr. + inline bool + operator==(const TlbRange &r2) const + { + return va == r2.va && + size == r2.size && + contextId == r2.contextId && + global == r2.global; + } +}; + + +// ITB/DTB table entry +struct TlbEntry +{ + public: + enum MemoryType { + StronglyOrdered, + Device, + Normal + }; + enum DomainType { + DomainNoAccess = 0, + DomainClient, + DomainReserved, + DomainManager + }; + + // Matching variables + Addr pfn; + Addr size; // Size of this entry, == Type of TLB Rec + Addr vpn; // Virtual Page Number + uint32_t asid; // Address Space Identifier + uint8_t N; // Number of bits in pagesize + bool global; + bool valid; + + // Type of memory + bool nonCacheable; // Can we wrap this in mtype? + bool sNp; // Section descriptor + + // Access permissions + bool xn; // Execute Never + uint8_t ap:3; // Access permissions bits + uint8_t domain:4; // Access Domain + + TlbRange range; // For fast TLB searching + + //Construct an entry that maps to physical address addr for SE mode TlbEntry(Addr _asn, Addr _vaddr, Addr _paddr) { - tag = _vaddr >> PageShift; - ppn = _paddr >> PageShift; - asn = _asn; + pfn = _paddr >> PageShift; + size = PageBytes - 1; + asid = _asn; + global = false; valid = true; + + vpn = _vaddr >> PageShift; + + nonCacheable = sNp = false; + + xn = 0; + ap = 0; // ??? + domain = DomainClient; //??? } TlbEntry() @@ -97,13 +164,28 @@ struct TlbEntry void updateVaddr(Addr new_vaddr) { - tag = new_vaddr >> PageShift; + vpn = new_vaddr >> PageShift; } Addr pageStart() { - return ppn << PageShift; + return pfn << PageShift; + } + + bool + match(Addr va, uint8_t cid) + { + Addr v = vpn << N; + if (valid && va >= v && va <= v + size && (global || cid == asid)) + return true; + return false; + } + + Addr + pAddr(Addr va) + { + return (pfn << N) | (va & size); } void serialize(std::ostream &os) { panic("Need to Implement\n"); } diff --git a/src/arch/arm/table_walker.cc b/src/arch/arm/table_walker.cc new file mode 100644 index 000000000..313b23316 --- /dev/null +++ b/src/arch/arm/table_walker.cc @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2010 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + * + * Authors: Ali Saidi + */ + +#include "arch/arm/faults.hh" +#include "arch/arm/table_walker.hh" +#include "arch/arm/tlb.hh" +#include "dev/io_device.hh" +#include "cpu/thread_context.hh" + + +using namespace ArmISA; + +TableWalker::TableWalker(const Params *p) + : MemObject(p), port(NULL), tlb(NULL), tc(NULL), req(NULL), + doL1DescEvent(this), doL2DescEvent(this) +{} + +TableWalker::~TableWalker() +{ + ; +} + + +unsigned int +drain(Event *de) +{ + panic("Not implemented\n"); +} + +Port* +TableWalker::getPort(const std::string &if_name, int idx) +{ + if (if_name == "port") { + if (port != NULL) + fatal("%s: port already connected to %s", + name(), port->getPeer()->name()); + System *sys = params()->sys; + Tick minb = params()->min_backoff; + Tick maxb = params()->max_backoff; + port = new DmaPort(this, sys, minb, maxb); + return port; + } + return NULL; +} + +Fault +TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint8_t _cid, TLB::Mode mode, + TLB::Translation *_trans, bool _timing) +{ + // Right now 1 CPU == 1 TLB == 1 TLB walker + // In the future we might want to change this as multiple + // threads/contexts could share a walker and/or a TLB + if (tc || req) + panic("Overlapping TLB walks attempted\n"); + + tc = _tc; + transState = _trans; + req = _req; + fault = NoFault; + contextId = _cid; + timing = _timing; + + // XXX These should be cached or grabbed from cached copies in + // the TLB, all these miscreg reads are expensive + vaddr = req->getVaddr() & ~PcModeMask; + sctlr = tc->readMiscReg(MISCREG_SCTLR); + cpsr = tc->readMiscReg(MISCREG_CPSR); + N = tc->readMiscReg(MISCREG_TTBCR); + Addr ttbr = 0; + + isFetch = (mode == TLB::Execute); + isWrite = (mode == TLB::Write); + isPriv = (cpsr.mode != MODE_USER); + + // If translation isn't enabled, we shouldn't be here + assert(sctlr.m); + + if (N == 0 || mbits(vaddr, 31, 32-N)) { + ttbr = tc->readMiscReg(MISCREG_TTBR0); + } else { + ttbr = tc->readMiscReg(MISCREG_TTBR0); + N = 0; + } + + Addr l1desc_addr = mbits(ttbr, 31, 14-N) | (bits(vaddr,31-N,20) << 2); + DPRINTF(TLB, "Begining table walk for address %#x at descriptor %#x\n", + vaddr, l1desc_addr); + + + // Trickbox address check + fault = tlb->walkTrickBoxCheck(l1desc_addr, vaddr, sizeof(uint32_t), + isFetch, 0, true); + if (fault) { + tc = NULL; + req = NULL; + return fault; + } + + if (timing) { + port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t), + &doL1DescEvent, (uint8_t*)&l1Desc.data, (Tick)0); + } else { + port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t), + NULL, (uint8_t*)&l1Desc.data, (Tick)0); + doL1Descriptor(); + } + + return fault; +} + +void +TableWalker::memAttrs(TlbEntry &te, uint8_t texcb) +{ + + if (sctlr.tre == 0) { + switch(texcb) { + case 0: + case 1: + case 4: + case 8: + te.nonCacheable = true; + break; + case 16: + if (bits(texcb, 1,0) == 0 || bits(texcb, 3,2) == 0) + te.nonCacheable = true; + break; + } + } else { + PRRR prrr = tc->readMiscReg(MISCREG_PRRR); + NMRR nmrr = tc->readMiscReg(MISCREG_NMRR); + switch(bits(texcb, 2,0)) { + case 0: + if (nmrr.ir0 == 0 || nmrr.or0 == 0 || prrr.tr0 != 0x2) + te.nonCacheable = true; + break; + case 1: + if (nmrr.ir1 == 0 || nmrr.or1 == 0 || prrr.tr1 != 0x2) + te.nonCacheable = true; + break; + case 2: + if (nmrr.ir2 == 0 || nmrr.or2 == 0 || prrr.tr2 != 0x2) + te.nonCacheable = true; + break; + case 3: + if (nmrr.ir3 == 0 || nmrr.or3 == 0 || prrr.tr3 != 0x2) + te.nonCacheable = true; + break; + case 4: + if (nmrr.ir4 == 0 || nmrr.or4 == 0 || prrr.tr4 != 0x2) + te.nonCacheable = true; + break; + case 5: + if (nmrr.ir5 == 0 || nmrr.or5 == 0 || prrr.tr5 != 0x2) + te.nonCacheable = true; + break; + case 6: + panic("Imp defined type\n"); + case 7: + if (nmrr.ir7 == 0 || nmrr.or7 == 0 || prrr.tr7 != 0x2) + te.nonCacheable = true; + break; + } + } +} + +void +TableWalker::doL1Descriptor() +{ + DPRINTF(TLB, "L1 descriptor for %#x is %#x\n", vaddr, l1Desc.data); + TlbEntry te; + + switch (l1Desc.type()) { + case L1Descriptor::Ignore: + case L1Descriptor::Reserved: + tc = NULL; + req = NULL; + fault = new DataAbort(vaddr, NULL, isWrite, ArmFault::Translation0); + return; + case L1Descriptor::Section: + if (sctlr.afe && bits(l1Desc.ap(), 0) == 0) + panic("Haven't implemented AFE\n"); + + if (l1Desc.supersection()) { + panic("Haven't implemented supersections\n"); + } + te.N = 20; + te.pfn = l1Desc.pfn(); + te.size = (1<<te.N) - 1; + te.global = !l1Desc.global(); + te.valid = true; + te.vpn = vaddr >> te.N; + te.sNp = true; + te.xn = l1Desc.xn(); + te.ap = l1Desc.ap(); + te.domain = l1Desc.domain(); + te.asid = contextId; + memAttrs(te, l1Desc.texcb()); + + DPRINTF(TLB, "Inserting Section Descriptor into TLB\n"); + DPRINTF(TLB, " - N%d pfn:%#x size: %#x global:%d valid: %d\n", + te.N, te.pfn, te.size, te.global, te.valid); + DPRINTF(TLB, " - vpn:%#x sNp: %d xn:%d ap:%d domain: %d asid:%d\n", + te.vpn, te.sNp, te.xn, te.ap, te.domain, te.asid); + DPRINTF(TLB, " - domain from l1 desc: %d data: %#x bits:%d\n", + l1Desc.domain(), l1Desc.data, (l1Desc.data >> 5) & 0xF ); + + tc = NULL; + req = NULL; + tlb->insert(vaddr, te); + + return; + case L1Descriptor::PageTable: + Addr l2desc_addr; + l2desc_addr = l1Desc.l2Addr() | (bits(vaddr, 19,12) << 2); + DPRINTF(TLB, "L1 descriptor points to page table at: %#x\n", l2desc_addr); + + // Trickbox address check + fault = tlb->walkTrickBoxCheck(l2desc_addr, vaddr, sizeof(uint32_t), + isFetch, l1Desc.domain(), false); + if (fault) { + tc = NULL; + req = NULL; + return; + } + + + if (timing) { + port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t), + &doL2DescEvent, (uint8_t*)&l2Desc.data, 0); + } else { + port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t), + NULL, (uint8_t*)&l2Desc.data, 0); + doL2Descriptor(); + } + return; + default: + panic("A new type in a 2 bit field?\n"); + } +} + +void +TableWalker::doL2Descriptor() +{ + DPRINTF(TLB, "L2 descriptor for %#x is %#x\n", vaddr, l2Desc.data); + TlbEntry te; + + if (sctlr.afe && bits(l1Desc.ap(), 0) == 0) + panic("Haven't implemented AFE\n"); + + if (l2Desc.invalid()) { + DPRINTF(TLB, "L2 descriptor invalid, causing fault\n"); + tc = NULL; + req = NULL; + fault = new DataAbort(vaddr, l1Desc.domain(), isWrite, ArmFault::Translation1); + return; + } + + if (l2Desc.large()) { + te.N = 16; + te.pfn = l2Desc.pfn(); + } else { + te.N = 12; + te.pfn = l2Desc.pfn(); + } + + te.valid = true; + te.size = (1 << te.N) - 1; + te.asid = contextId; + te.sNp = false; + te.vpn = vaddr >> te.N; + te.global = l2Desc.global(); + te.xn = l2Desc.xn(); + te.ap = l2Desc.ap(); + te.domain = l1Desc.domain(); + memAttrs(te, l2Desc.texcb()); + + tc = NULL; + req = NULL; + tlb->insert(vaddr, te); +} + +ArmISA::TableWalker * +ArmTableWalkerParams::create() +{ + return new ArmISA::TableWalker(this); +} + diff --git a/src/arch/arm/table_walker.hh b/src/arch/arm/table_walker.hh new file mode 100644 index 000000000..d18b7c489 --- /dev/null +++ b/src/arch/arm/table_walker.hh @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2010 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + * + * Authors: Ali Saidi + */ + +#ifndef __ARCH_ARM_TABLE_WALKER_HH__ +#define __ARCH_ARM_TABLE_WALKER_HH__ + +#include "arch/arm/miscregs.hh" +#include "arch/arm/tlb.hh" +#include "mem/mem_object.hh" +#include "mem/request.hh" +#include "mem/request.hh" +#include "params/ArmTableWalker.hh" +#include "sim/faults.hh" +#include "sim/eventq.hh" + +class DmaPort; +class ThreadContext; + +namespace ArmISA { +class Translation; +class TLB; + +class TableWalker : public MemObject +{ + protected: + struct L1Descriptor { + /** Type of page table entry ARM DDI 0406B: B3-8*/ + enum EntryType { + Ignore, + PageTable, + Section, + Reserved + }; + + uint32_t data; + + EntryType type() const + { + return (EntryType)(data & 0x3); + } + + /** Is the page a Supersection (16MB)?*/ + bool supersection() const + { + return bits(data, 18); + } + + /** Return the physcal address of the entry, bits in position*/ + Addr paddr() const + { + if (supersection()) + panic("Super sections not implemented\n"); + return mbits(data, 31,20); + } + + /** Return the physical frame, bits shifted right */ + Addr pfn() const + { + if (supersection()) + panic("Super sections not implemented\n"); + return bits(data, 31,20); + } + + /** Is the translation global (no asid used)? */ + bool global() const + { + return bits(data, 17); + } + + /** Is the translation not allow execution? */ + bool xn() const + { + return bits(data, 17); + } + + /** Three bit access protection flags */ + uint8_t ap() const + { + return (bits(data, 15) << 2) | bits(data,11,10); + } + + /** Domain Client/Manager: ARM DDI 0406B: B3-31 */ + uint8_t domain() const + { + return bits(data,8,5); + } + + /** Address of L2 descriptor if it exists */ + Addr l2Addr() const + { + return mbits(data, 31,10); + } + + /** Memory region attributes: ARM DDI 0406B: B3-32 */ + uint8_t texcb() const + { + return bits(data, 2) | bits(data,3) << 1 | bits(data, 12, 14) << 2; + } + + }; + + /** Level 2 page table descriptor */ + struct L2Descriptor { + + uint32_t data; + + /** Is the entry invalid */ + bool invalid() const + { + return bits(data, 1,0) == 0;; + } + + /** What is the size of the mapping? */ + bool large() const + { + return bits(data, 1) == 0; + } + + /** Is execution allowed on this mapping? */ + bool xn() const + { + return large() ? bits(data, 15) : bits(data, 0); + } + + /** Is the translation global (no asid used)? */ + bool global() const + { + return !bits(data, 11); + } + + /** Three bit access protection flags */ + uint8_t ap() const + { + return bits(data, 5, 4) | (bits(data, 9) << 2); + } + + /** Memory region attributes: ARM DDI 0406B: B3-32 */ + uint8_t texcb() const + { + return large() ? + (bits(data, 2) | (bits(data,3) << 1) | (bits(data, 12, 14) << 2)) : + (bits(data, 2) | (bits(data,3) << 1) | (bits(data, 6, 8) << 2)); + } + + /** Return the physical frame, bits shifted right */ + Addr pfn() const + { + return large() ? bits(data, 31, 16) : bits(data, 31, 12); + } + + }; + + /** Port to issue translation requests from */ + DmaPort *port; + + /** TLB that is initiating these table walks */ + TLB *tlb; + + /** Thread context that we're doing the walk for */ + ThreadContext *tc; + + /** Request that is currently being serviced */ + RequestPtr req; + + /** Context ID that we're servicing the request under */ + uint8_t contextId; + + /** Translation state for delayed requests */ + TLB::Translation *transState; + + /** The fault that we are going to return */ + Fault fault; + + /** The virtual address that is being translated */ + Addr vaddr; + + /** Cached copy of the sctlr as it existed when translation began */ + SCTLR sctlr; + + /** Cached copy of the cpsr as it existed when the translation began */ + CPSR cpsr; + + /** Width of the base address held in TTRB0 */ + uint32_t N; + + /** If the access is a write */ + bool isWrite; + + /** If the access is not from user mode */ + bool isPriv; + + /** If the access is a fetch (for execution, and no-exec) must be checked?*/ + bool isFetch; + + /** If the mode is timing or atomic */ + bool timing; + + L1Descriptor l1Desc; + L2Descriptor l2Desc; + + public: + typedef ArmTableWalkerParams Params; + TableWalker(const Params *p); + virtual ~TableWalker(); + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + + virtual unsigned int drain(Event *de) { panic("write me\n"); } + virtual Port *getPort(const std::string &if_name, int idx = -1); + + Fault walk(RequestPtr req, ThreadContext *tc, uint8_t cid, TLB::Mode mode, + TLB::Translation *_trans, bool timing); + + void setTlb(TLB *_tlb) { tlb = _tlb; } + + private: + void memAttrs(TlbEntry &te, uint8_t texcb); + + void doL1Descriptor(); + EventWrapper<TableWalker, &TableWalker::doL1Descriptor> doL1DescEvent; + + void doL2Descriptor(); + EventWrapper<TableWalker, &TableWalker::doL2Descriptor> doL2DescEvent; + + +}; + + +} // namespace ArmISA + +#endif //__ARCH_ARM_TABLE_WALKER_HH__ + 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 * diff --git a/src/arch/arm/tlb.hh b/src/arch/arm/tlb.hh index dfd707444..7193ac0e8 100644 --- a/src/arch/arm/tlb.hh +++ b/src/arch/arm/tlb.hh @@ -59,6 +59,8 @@ class ThreadContext; namespace ArmISA { +class TableWalker; + class TLB : public BaseTLB { public: @@ -71,21 +73,24 @@ class TLB : public BaseTLB AlignDoubleWord = 0x7, AllowUnaligned = 0x8, + // Priv code operating as if it wasn't + UserMode = 0x10, // Because zero otherwise looks like a valid setting and may be used // accidentally, this bit must be non-zero to show it was used on // purpose. - MustBeOne = 0x10 + MustBeOne = 0x20 }; protected: typedef std::multimap<Addr, int> PageTable; PageTable lookupTable; // Quick lookup into page table - ArmISA::PTE *table; // the Page Table + TlbEntry *table; // the Page Table int size; // TLB Size int nlu; // not last used entry (for replacement) + TableWalker *tableWalker; void nextnlu() { if (++nlu >= size) nlu = 0; } - ArmISA::PTE *lookup(Addr vpn, uint8_t asn) const; + TlbEntry *lookup(Addr vpn, uint8_t asn); // Access Stats mutable Stats::Scalar read_hits; @@ -101,6 +106,7 @@ class TLB : public BaseTLB Stats::Formula invalids; Stats::Formula accesses; + public: typedef ArmTLBParams Params; TLB(const Params *p); @@ -108,17 +114,49 @@ class TLB : public BaseTLB virtual ~TLB(); int getsize() const { return size; } - void insert(Addr vaddr, ArmISA::PTE &pte); + void insert(Addr vaddr, TlbEntry &pte); + + /** Reset the entire TLB */ void flushAll(); + + /** Remove any entries that match both a va and asn + * @param mva virtual address to flush + * @param asn contextid/asn to flush on match + */ + void flushMvaAsid(Addr mva, uint64_t asn); + + /** Remove any entries that match the asn + * @param asn contextid/asn to flush on match + */ + void flushAsid(uint64_t asn); + + /** Remove all entries that match the va regardless of asn + * @param mva address to flush from cache + */ + void flushMva(Addr mva); + + Fault trickBoxCheck(RequestPtr req, Mode mode, uint8_t domain, bool sNp); + Fault walkTrickBoxCheck(Addr pa, Addr va, Addr sz, bool is_exec, uint8_t + domain, bool sNp); + + void printTlb(); + void demapPage(Addr vaddr, uint64_t asn) { - panic("demapPage unimplemented.\n"); + flushMvaAsid(vaddr, asn); } static bool validVirtualAddress(Addr vaddr); +#if FULL_SYSTEM + Fault translateFs(RequestPtr req, ThreadContext *tc, Mode mode, + Translation *translation, bool &delay, bool timing); +#else + Fault translateSe(RequestPtr req, ThreadContext *tc, Mode mode, + Translation *translation, bool &delay, bool timing); +#endif Fault translateAtomic(RequestPtr req, ThreadContext *tc, Mode mode); - void translateTiming(RequestPtr req, ThreadContext *tc, + Fault translateTiming(RequestPtr req, ThreadContext *tc, Translation *translation, Mode mode); // Checkpointing |