summaryrefslogtreecommitdiff
path: root/src/arch
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
parentf246be4cbc27b4173f6917b430a31b9a39cdb380 (diff)
downloadgem5-cb9936cfdefdebf2c0b950f93a62d504d356524d.tar.xz
ARM: Implement the ARM TLB/Tablewalker. Needs performance improvements.
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/arm/ArmTLB.py29
-rw-r--r--src/arch/arm/SConscript2
-rw-r--r--src/arch/arm/faults.hh11
-rw-r--r--src/arch/arm/isa.hh51
-rw-r--r--src/arch/arm/isa/formats/misc.isa34
-rw-r--r--src/arch/arm/isa/insts/ldr.isa6
-rw-r--r--src/arch/arm/isa/insts/str.isa13
-rw-r--r--src/arch/arm/miscregs.cc3
-rw-r--r--src/arch/arm/miscregs.hh59
-rw-r--r--src/arch/arm/pagetable.hh112
-rw-r--r--src/arch/arm/table_walker.cc323
-rw-r--r--src/arch/arm/table_walker.hh272
-rw-r--r--src/arch/arm/tlb.cc362
-rw-r--r--src/arch/arm/tlb.hh50
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 &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 *
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