ARM: Allow multiple outstanding TLB walks to queue.
This commit is contained in:
parent
2bad5138e4
commit
4325519fc5
|
@ -43,13 +43,16 @@
|
||||||
#include "dev/io_device.hh"
|
#include "dev/io_device.hh"
|
||||||
#include "cpu/thread_context.hh"
|
#include "cpu/thread_context.hh"
|
||||||
|
|
||||||
|
#define NUM_WALKERS 2 // 2 should be enough to handle crossing page boundaries
|
||||||
|
|
||||||
using namespace ArmISA;
|
using namespace ArmISA;
|
||||||
|
|
||||||
TableWalker::TableWalker(const Params *p)
|
TableWalker::TableWalker(const Params *p)
|
||||||
: MemObject(p), port(NULL), tlb(NULL), tc(NULL), req(NULL),
|
: MemObject(p), stateQueue(NUM_WALKERS), port(NULL), tlb(NULL),
|
||||||
doL1DescEvent(this), doL2DescEvent(this)
|
currState(NULL), doL1DescEvent(this), doL2DescEvent(this)
|
||||||
{}
|
{
|
||||||
|
sctlr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
TableWalker::~TableWalker()
|
TableWalker::~TableWalker()
|
||||||
{
|
{
|
||||||
|
@ -83,80 +86,99 @@ Fault
|
||||||
TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint8_t _cid, TLB::Mode _mode,
|
TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint8_t _cid, TLB::Mode _mode,
|
||||||
TLB::Translation *_trans, bool _timing)
|
TLB::Translation *_trans, bool _timing)
|
||||||
{
|
{
|
||||||
// Right now 1 CPU == 1 TLB == 1 TLB walker
|
if (!currState) {
|
||||||
// In the future we might want to change this as multiple
|
// For atomic mode, a new WalkerState instance should be only created
|
||||||
// threads/contexts could share a walker and/or a TLB
|
// once per TLB. For timing mode, a new instance is generated for every
|
||||||
if (tc || req)
|
// TLB miss.
|
||||||
panic("Overlapping TLB walks attempted\n");
|
DPRINTF(TLBVerbose, "creating new instance of WalkerState\n");
|
||||||
|
|
||||||
tc = _tc;
|
currState = new WalkerState();
|
||||||
transState = _trans;
|
currState->tableWalker = this;
|
||||||
req = _req;
|
}
|
||||||
fault = NoFault;
|
else if (_timing) {
|
||||||
contextId = _cid;
|
panic("currState should always be empty in timing mode!\n");
|
||||||
timing = _timing;
|
}
|
||||||
mode = _mode;
|
|
||||||
|
currState->tc = _tc;
|
||||||
|
currState->transState = _trans;
|
||||||
|
currState->req = _req;
|
||||||
|
currState->fault = NoFault;
|
||||||
|
currState->contextId = _cid;
|
||||||
|
currState->timing = _timing;
|
||||||
|
currState->mode = _mode;
|
||||||
|
|
||||||
/** @todo These should be cached or grabbed from cached copies in
|
/** @todo These should be cached or grabbed from cached copies in
|
||||||
the TLB, all these miscreg reads are expensive */
|
the TLB, all these miscreg reads are expensive */
|
||||||
vaddr = req->getVaddr() & ~PcModeMask;
|
currState->vaddr = currState->req->getVaddr() & ~PcModeMask;
|
||||||
sctlr = tc->readMiscReg(MISCREG_SCTLR);
|
currState->sctlr = currState->tc->readMiscReg(MISCREG_SCTLR);
|
||||||
cpsr = tc->readMiscReg(MISCREG_CPSR);
|
sctlr = currState->sctlr;
|
||||||
N = tc->readMiscReg(MISCREG_TTBCR);
|
currState->cpsr = currState->tc->readMiscReg(MISCREG_CPSR);
|
||||||
|
currState->N = currState->tc->readMiscReg(MISCREG_TTBCR);
|
||||||
|
|
||||||
|
currState->isFetch = (currState->mode == TLB::Execute);
|
||||||
|
currState->isWrite = (currState->mode == TLB::Write);
|
||||||
|
currState->isPriv = (currState->cpsr.mode != MODE_USER);
|
||||||
|
|
||||||
Addr ttbr = 0;
|
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
|
// If translation isn't enabled, we shouldn't be here
|
||||||
assert(sctlr.m);
|
assert(currState->sctlr.m);
|
||||||
|
|
||||||
DPRINTF(TLB, "Begining table walk for address %#x, TTBCR: %#x, bits:%#x\n",
|
DPRINTF(TLB, "Begining table walk for address %#x, TTBCR: %#x, bits:%#x\n",
|
||||||
vaddr, N, mbits(vaddr, 31, 32-N));
|
currState->vaddr, currState->N, mbits(currState->vaddr, 31,
|
||||||
|
32-currState->N));
|
||||||
|
|
||||||
if (N == 0 || !mbits(vaddr, 31, 32-N)) {
|
if (currState->N == 0 || !mbits(currState->vaddr, 31, 32-currState->N)) {
|
||||||
DPRINTF(TLB, " - Selecting TTBR0\n");
|
DPRINTF(TLB, " - Selecting TTBR0\n");
|
||||||
ttbr = tc->readMiscReg(MISCREG_TTBR0);
|
ttbr = currState->tc->readMiscReg(MISCREG_TTBR0);
|
||||||
} else {
|
} else {
|
||||||
DPRINTF(TLB, " - Selecting TTBR1\n");
|
DPRINTF(TLB, " - Selecting TTBR1\n");
|
||||||
ttbr = tc->readMiscReg(MISCREG_TTBR1);
|
ttbr = currState->tc->readMiscReg(MISCREG_TTBR1);
|
||||||
N = 0;
|
currState->N = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Addr l1desc_addr = mbits(ttbr, 31, 14-N) | (bits(vaddr,31-N,20) << 2);
|
Addr l1desc_addr = mbits(ttbr, 31, 14-currState->N) |
|
||||||
|
(bits(currState->vaddr,31-currState->N,20) << 2);
|
||||||
DPRINTF(TLB, " - Descriptor at address %#x\n", l1desc_addr);
|
DPRINTF(TLB, " - Descriptor at address %#x\n", l1desc_addr);
|
||||||
|
|
||||||
|
|
||||||
// Trickbox address check
|
// Trickbox address check
|
||||||
fault = tlb->walkTrickBoxCheck(l1desc_addr, vaddr, sizeof(uint32_t),
|
Fault f;
|
||||||
isFetch, isWrite, 0, true);
|
f = tlb->walkTrickBoxCheck(l1desc_addr, currState->vaddr, sizeof(uint32_t),
|
||||||
if (fault) {
|
currState->isFetch, currState->isWrite, 0, true);
|
||||||
tc = NULL;
|
if (f) {
|
||||||
req = NULL;
|
currState->tc = NULL;
|
||||||
return fault;
|
currState->req = NULL;
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timing) {
|
if (currState->timing) {
|
||||||
port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t),
|
port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t),
|
||||||
&doL1DescEvent, (uint8_t*)&l1Desc.data, (Tick)0);
|
&doL1DescEvent, (uint8_t*)&currState->l1Desc.data, (Tick)0);
|
||||||
|
DPRINTF(TLBVerbose, "Adding to walker fifo: %d free before adding\n",
|
||||||
|
stateQueue.free_slots());
|
||||||
|
stateQueue.add(*currState);
|
||||||
|
currState = NULL;
|
||||||
} else {
|
} else {
|
||||||
port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t),
|
port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t),
|
||||||
NULL, (uint8_t*)&l1Desc.data, (Tick)0);
|
NULL, (uint8_t*)&currState->l1Desc.data, (Tick)0);
|
||||||
doL1Descriptor();
|
doL1Descriptor();
|
||||||
|
f = currState->fault;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fault;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TableWalker::memAttrs(ThreadContext *tc, TlbEntry &te, uint8_t texcb, bool s)
|
TableWalker::memAttrs(ThreadContext *tc, TlbEntry &te, SCTLR sctlr,
|
||||||
|
uint8_t texcb, bool s)
|
||||||
{
|
{
|
||||||
// Note: tc local variable is hiding tc class variable
|
// Note: tc and sctlr local variables are hiding tc and sctrl class
|
||||||
|
// variables
|
||||||
DPRINTF(TLBVerbose, "memAttrs texcb:%d s:%d\n", texcb, s);
|
DPRINTF(TLBVerbose, "memAttrs texcb:%d s:%d\n", texcb, s);
|
||||||
te.shareable = false; // default value
|
te.shareable = false; // default value
|
||||||
bool outer_shareable = false;
|
bool outer_shareable = false;
|
||||||
if (sctlr.tre == 0) {
|
if (sctlr.tre == 0 || ((sctlr.tre == 1) && (sctlr.m == 0))) {
|
||||||
switch(texcb) {
|
switch(texcb) {
|
||||||
case 0: // Stongly-ordered
|
case 0: // Stongly-ordered
|
||||||
te.nonCacheable = true;
|
te.nonCacheable = true;
|
||||||
|
@ -192,8 +214,10 @@ TableWalker::memAttrs(ThreadContext *tc, TlbEntry &te, uint8_t texcb, bool s)
|
||||||
te.outerAttrs = bits(texcb, 1, 0);
|
te.outerAttrs = bits(texcb, 1, 0);
|
||||||
break;
|
break;
|
||||||
case 5: // Reserved
|
case 5: // Reserved
|
||||||
|
panic("Reserved texcb value!\n");
|
||||||
break;
|
break;
|
||||||
case 6: // Implementation Defined
|
case 6: // Implementation Defined
|
||||||
|
panic("Implementation-defined texcb value!\n");
|
||||||
break;
|
break;
|
||||||
case 7: // Outer and Inner Write-Back, Write-Allocate
|
case 7: // Outer and Inner Write-Back, Write-Allocate
|
||||||
te.mtype = TlbEntry::Normal;
|
te.mtype = TlbEntry::Normal;
|
||||||
|
@ -209,6 +233,7 @@ TableWalker::memAttrs(ThreadContext *tc, TlbEntry &te, uint8_t texcb, bool s)
|
||||||
te.outerAttrs = 0;
|
te.outerAttrs = 0;
|
||||||
break;
|
break;
|
||||||
case 9 ... 15: // Reserved
|
case 9 ... 15: // Reserved
|
||||||
|
panic("Reserved texcb value!\n");
|
||||||
break;
|
break;
|
||||||
case 16 ... 31: // Cacheable Memory
|
case 16 ... 31: // Cacheable Memory
|
||||||
te.mtype = TlbEntry::Normal;
|
te.mtype = TlbEntry::Normal;
|
||||||
|
@ -303,7 +328,6 @@ TableWalker::memAttrs(ThreadContext *tc, TlbEntry &te, uint8_t texcb, bool s)
|
||||||
te.shareable = true;
|
te.shareable = true;
|
||||||
if (prrr.ns0 && !s)
|
if (prrr.ns0 && !s)
|
||||||
te.shareable = true;
|
te.shareable = true;
|
||||||
//te.shareable = outer_shareable;
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
panic("Reserved type");
|
panic("Reserved type");
|
||||||
|
@ -343,6 +367,9 @@ TableWalker::memAttrs(ThreadContext *tc, TlbEntry &te, uint8_t texcb, bool s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DPRINTF(TLBVerbose, "memAttrs: shareable: %d, innerAttrs: %d, \
|
||||||
|
outerAttrs: %d\n",
|
||||||
|
te.shareable, te.innerAttrs, te.outerAttrs);
|
||||||
|
|
||||||
/** Formatting for Physical Address Register (PAR)
|
/** Formatting for Physical Address Register (PAR)
|
||||||
* Only including lower bits (TLB info here)
|
* Only including lower bits (TLB info here)
|
||||||
|
@ -375,49 +402,54 @@ TableWalker::memAttrs(ThreadContext *tc, TlbEntry &te, uint8_t texcb, bool s)
|
||||||
void
|
void
|
||||||
TableWalker::doL1Descriptor()
|
TableWalker::doL1Descriptor()
|
||||||
{
|
{
|
||||||
DPRINTF(TLB, "L1 descriptor for %#x is %#x\n", vaddr, l1Desc.data);
|
DPRINTF(TLB, "L1 descriptor for %#x is %#x\n",
|
||||||
|
currState->vaddr, currState->l1Desc.data);
|
||||||
TlbEntry te;
|
TlbEntry te;
|
||||||
|
|
||||||
switch (l1Desc.type()) {
|
switch (currState->l1Desc.type()) {
|
||||||
case L1Descriptor::Ignore:
|
case L1Descriptor::Ignore:
|
||||||
case L1Descriptor::Reserved:
|
case L1Descriptor::Reserved:
|
||||||
if (!delayed) {
|
if (!currState->delayed) {
|
||||||
tc = NULL;
|
currState->tc = NULL;
|
||||||
req = NULL;
|
currState->req = NULL;
|
||||||
}
|
}
|
||||||
DPRINTF(TLB, "L1 Descriptor Reserved/Ignore, causing fault\n");
|
DPRINTF(TLB, "L1 Descriptor Reserved/Ignore, causing fault\n");
|
||||||
if (isFetch)
|
if (currState->isFetch)
|
||||||
fault = new PrefetchAbort(vaddr, ArmFault::Translation0);
|
currState->fault =
|
||||||
|
new PrefetchAbort(currState->vaddr, ArmFault::Translation0);
|
||||||
else
|
else
|
||||||
fault = new DataAbort(vaddr, NULL, isWrite,
|
currState->fault =
|
||||||
|
new DataAbort(currState->vaddr, NULL, currState->isWrite,
|
||||||
ArmFault::Translation0);
|
ArmFault::Translation0);
|
||||||
return;
|
return;
|
||||||
case L1Descriptor::Section:
|
case L1Descriptor::Section:
|
||||||
if (sctlr.afe && bits(l1Desc.ap(), 0) == 0) {
|
if (currState->sctlr.afe && bits(currState->l1Desc.ap(), 0) == 0) {
|
||||||
/** @todo: check sctlr.ha (bit[17]) if Hardware Access Flag is
|
/** @todo: check sctlr.ha (bit[17]) if Hardware Access Flag is
|
||||||
* enabled if set, do l1.Desc.setAp0() instead of generating
|
* enabled if set, do l1.Desc.setAp0() instead of generating
|
||||||
* AccessFlag0
|
* AccessFlag0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fault = new DataAbort(vaddr, NULL, isWrite,
|
currState->fault =
|
||||||
|
new DataAbort(currState->vaddr, NULL, currState->isWrite,
|
||||||
ArmFault::AccessFlag0);
|
ArmFault::AccessFlag0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (l1Desc.supersection()) {
|
if (currState->l1Desc.supersection()) {
|
||||||
panic("Haven't implemented supersections\n");
|
panic("Haven't implemented supersections\n");
|
||||||
}
|
}
|
||||||
te.N = 20;
|
te.N = 20;
|
||||||
te.pfn = l1Desc.pfn();
|
te.pfn = currState->l1Desc.pfn();
|
||||||
te.size = (1<<te.N) - 1;
|
te.size = (1<<te.N) - 1;
|
||||||
te.global = !l1Desc.global();
|
te.global = !currState->l1Desc.global();
|
||||||
te.valid = true;
|
te.valid = true;
|
||||||
te.vpn = vaddr >> te.N;
|
te.vpn = currState->vaddr >> te.N;
|
||||||
te.sNp = true;
|
te.sNp = true;
|
||||||
te.xn = l1Desc.xn();
|
te.xn = currState->l1Desc.xn();
|
||||||
te.ap = l1Desc.ap();
|
te.ap = currState->l1Desc.ap();
|
||||||
te.domain = l1Desc.domain();
|
te.domain = currState->l1Desc.domain();
|
||||||
te.asid = contextId;
|
te.asid = currState->contextId;
|
||||||
memAttrs(tc, te, l1Desc.texcb(), l1Desc.shareable());
|
memAttrs(currState->tc, te, currState->sctlr,
|
||||||
|
currState->l1Desc.texcb(), currState->l1Desc.shareable());
|
||||||
|
|
||||||
DPRINTF(TLB, "Inserting Section Descriptor into TLB\n");
|
DPRINTF(TLB, "Inserting Section Descriptor into TLB\n");
|
||||||
DPRINTF(TLB, " - N%d pfn:%#x size: %#x global:%d valid: %d\n",
|
DPRINTF(TLB, " - N%d pfn:%#x size: %#x global:%d valid: %d\n",
|
||||||
|
@ -425,40 +457,44 @@ TableWalker::doL1Descriptor()
|
||||||
DPRINTF(TLB, " - vpn:%#x sNp: %d xn:%d ap:%d domain: %d asid:%d\n",
|
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);
|
te.vpn, te.sNp, te.xn, te.ap, te.domain, te.asid);
|
||||||
DPRINTF(TLB, " - domain from l1 desc: %d data: %#x bits:%d\n",
|
DPRINTF(TLB, " - domain from l1 desc: %d data: %#x bits:%d\n",
|
||||||
l1Desc.domain(), l1Desc.data, (l1Desc.data >> 5) & 0xF );
|
currState->l1Desc.domain(), currState->l1Desc.data,
|
||||||
|
(currState->l1Desc.data >> 5) & 0xF );
|
||||||
|
|
||||||
if (!timing) {
|
if (!currState->timing) {
|
||||||
tc = NULL;
|
currState->tc = NULL;
|
||||||
req = NULL;
|
currState->req = NULL;
|
||||||
}
|
}
|
||||||
tlb->insert(vaddr, te);
|
tlb->insert(currState->vaddr, te);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
case L1Descriptor::PageTable:
|
case L1Descriptor::PageTable:
|
||||||
Addr l2desc_addr;
|
Addr l2desc_addr;
|
||||||
l2desc_addr = l1Desc.l2Addr() | (bits(vaddr, 19,12) << 2);
|
l2desc_addr = currState->l1Desc.l2Addr() |
|
||||||
|
(bits(currState->vaddr, 19,12) << 2);
|
||||||
DPRINTF(TLB, "L1 descriptor points to page table at: %#x\n",
|
DPRINTF(TLB, "L1 descriptor points to page table at: %#x\n",
|
||||||
l2desc_addr);
|
l2desc_addr);
|
||||||
|
|
||||||
// Trickbox address check
|
// Trickbox address check
|
||||||
fault = tlb->walkTrickBoxCheck(l2desc_addr, vaddr, sizeof(uint32_t),
|
currState->fault = tlb->walkTrickBoxCheck(l2desc_addr, currState->vaddr,
|
||||||
isFetch, isWrite, l1Desc.domain(), false);
|
sizeof(uint32_t), currState->isFetch, currState->isWrite,
|
||||||
if (fault) {
|
currState->l1Desc.domain(), false);
|
||||||
if (!timing) {
|
|
||||||
tc = NULL;
|
if (currState->fault) {
|
||||||
req = NULL;
|
if (!currState->timing) {
|
||||||
|
currState->tc = NULL;
|
||||||
|
currState->req = NULL;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (timing) {
|
if (currState->timing) {
|
||||||
delayed = true;
|
currState->delayed = true;
|
||||||
port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t),
|
port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t),
|
||||||
&doL2DescEvent, (uint8_t*)&l2Desc.data, 0);
|
&doL2DescEvent, (uint8_t*)&currState->l2Desc.data, 0);
|
||||||
} else {
|
} else {
|
||||||
port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t),
|
port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t),
|
||||||
NULL, (uint8_t*)&l2Desc.data, 0);
|
NULL, (uint8_t*)&currState->l2Desc.data, 0);
|
||||||
doL2Descriptor();
|
doL2Descriptor();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -470,103 +506,125 @@ TableWalker::doL1Descriptor()
|
||||||
void
|
void
|
||||||
TableWalker::doL2Descriptor()
|
TableWalker::doL2Descriptor()
|
||||||
{
|
{
|
||||||
DPRINTF(TLB, "L2 descriptor for %#x is %#x\n", vaddr, l2Desc.data);
|
DPRINTF(TLB, "L2 descriptor for %#x is %#x\n",
|
||||||
|
currState->vaddr, currState->l2Desc.data);
|
||||||
TlbEntry te;
|
TlbEntry te;
|
||||||
|
|
||||||
if (l2Desc.invalid()) {
|
if (currState->l2Desc.invalid()) {
|
||||||
DPRINTF(TLB, "L2 descriptor invalid, causing fault\n");
|
DPRINTF(TLB, "L2 descriptor invalid, causing fault\n");
|
||||||
if (!delayed) {
|
if (!currState->delayed) {
|
||||||
tc = NULL;
|
currState->tc = NULL;
|
||||||
req = NULL;
|
currState->req = NULL;
|
||||||
}
|
}
|
||||||
if (isFetch)
|
if (currState->isFetch)
|
||||||
fault = new PrefetchAbort(vaddr, ArmFault::Translation1);
|
currState->fault =
|
||||||
|
new PrefetchAbort(currState->vaddr, ArmFault::Translation1);
|
||||||
else
|
else
|
||||||
fault = new DataAbort(vaddr, l1Desc.domain(), isWrite,
|
currState->fault =
|
||||||
ArmFault::Translation1);
|
new DataAbort(currState->vaddr, currState->l1Desc.domain(),
|
||||||
|
currState->isWrite, ArmFault::Translation1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sctlr.afe && bits(l2Desc.ap(), 0) == 0) {
|
if (currState->sctlr.afe && bits(currState->l2Desc.ap(), 0) == 0) {
|
||||||
/** @todo: check sctlr.ha (bit[17]) if Hardware Access Flag is enabled
|
/** @todo: check sctlr.ha (bit[17]) if Hardware Access Flag is enabled
|
||||||
* if set, do l2.Desc.setAp0() instead of generating AccessFlag0
|
* if set, do l2.Desc.setAp0() instead of generating AccessFlag0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fault = new DataAbort(vaddr, NULL, isWrite, ArmFault::AccessFlag1);
|
currState->fault =
|
||||||
|
new DataAbort(currState->vaddr, NULL, currState->isWrite,
|
||||||
|
ArmFault::AccessFlag1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (l2Desc.large()) {
|
if (currState->l2Desc.large()) {
|
||||||
te.N = 16;
|
te.N = 16;
|
||||||
te.pfn = l2Desc.pfn();
|
te.pfn = currState->l2Desc.pfn();
|
||||||
} else {
|
} else {
|
||||||
te.N = 12;
|
te.N = 12;
|
||||||
te.pfn = l2Desc.pfn();
|
te.pfn = currState->l2Desc.pfn();
|
||||||
}
|
}
|
||||||
|
|
||||||
te.valid = true;
|
te.valid = true;
|
||||||
te.size = (1 << te.N) - 1;
|
te.size = (1 << te.N) - 1;
|
||||||
te.asid = contextId;
|
te.asid = currState->contextId;
|
||||||
te.sNp = false;
|
te.sNp = false;
|
||||||
te.vpn = vaddr >> te.N;
|
te.vpn = currState->vaddr >> te.N;
|
||||||
te.global = l2Desc.global();
|
te.global = currState->l2Desc.global();
|
||||||
te.xn = l2Desc.xn();
|
te.xn = currState->l2Desc.xn();
|
||||||
te.ap = l2Desc.ap();
|
te.ap = currState->l2Desc.ap();
|
||||||
te.domain = l1Desc.domain();
|
te.domain = currState->l1Desc.domain();
|
||||||
memAttrs(tc, te, l2Desc.texcb(), l2Desc.shareable());
|
memAttrs(currState->tc, te, currState->sctlr, currState->l2Desc.texcb(),
|
||||||
|
currState->l2Desc.shareable());
|
||||||
|
|
||||||
if (!delayed) {
|
if (!currState->delayed) {
|
||||||
tc = NULL;
|
currState->tc = NULL;
|
||||||
req = NULL;
|
currState->req = NULL;
|
||||||
}
|
}
|
||||||
tlb->insert(vaddr, te);
|
tlb->insert(currState->vaddr, te);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TableWalker::doL1DescriptorWrapper()
|
TableWalker::doL1DescriptorWrapper()
|
||||||
{
|
{
|
||||||
delayed = false;
|
currState = stateQueue.peek();
|
||||||
|
currState->delayed = false;
|
||||||
|
|
||||||
DPRINTF(TLBVerbose, "calling doL1Descriptor\n");
|
DPRINTF(TLBVerbose, "calling doL1Descriptor for vaddr:%#x\n", currState->vaddr);
|
||||||
doL1Descriptor();
|
doL1Descriptor();
|
||||||
|
|
||||||
// Check if fault was generated
|
// Check if fault was generated
|
||||||
if (fault != NoFault) {
|
if (currState->fault != NoFault) {
|
||||||
transState->finish(fault, req, tc, mode);
|
currState->transState->finish(currState->fault, currState->req,
|
||||||
|
currState->tc, currState->mode);
|
||||||
|
|
||||||
req = NULL;
|
currState->req = NULL;
|
||||||
tc = NULL;
|
currState->tc = NULL;
|
||||||
delayed = false;
|
currState->delayed = false;
|
||||||
|
|
||||||
|
stateQueue.remove();
|
||||||
}
|
}
|
||||||
else if (!delayed) {
|
else if (!currState->delayed) {
|
||||||
DPRINTF(TLBVerbose, "calling translateTiming again\n");
|
DPRINTF(TLBVerbose, "calling translateTiming again\n");
|
||||||
fault = tlb->translateTiming(req, tc, transState, mode);
|
currState->fault = tlb->translateTiming(currState->req, currState->tc,
|
||||||
|
currState->transState, currState->mode);
|
||||||
|
|
||||||
req = NULL;
|
currState->req = NULL;
|
||||||
tc = NULL;
|
currState->tc = NULL;
|
||||||
delayed = false;
|
currState->delayed = false;
|
||||||
|
|
||||||
|
stateQueue.remove();
|
||||||
}
|
}
|
||||||
|
currState = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TableWalker::doL2DescriptorWrapper()
|
TableWalker::doL2DescriptorWrapper()
|
||||||
{
|
{
|
||||||
assert(delayed);
|
currState = stateQueue.peek();
|
||||||
|
assert(currState->delayed);
|
||||||
|
|
||||||
DPRINTF(TLBVerbose, "calling doL2Descriptor\n");
|
DPRINTF(TLBVerbose, "calling doL2Descriptor for vaddr:%#x\n",
|
||||||
|
currState->vaddr);
|
||||||
doL2Descriptor();
|
doL2Descriptor();
|
||||||
|
|
||||||
// Check if fault was generated
|
// Check if fault was generated
|
||||||
if (fault != NoFault) {
|
if (currState->fault != NoFault) {
|
||||||
transState->finish(fault, req, tc, mode);
|
currState->transState->finish(currState->fault, currState->req,
|
||||||
|
currState->tc, currState->mode);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DPRINTF(TLBVerbose, "calling translateTiming again\n");
|
DPRINTF(TLBVerbose, "calling translateTiming again\n");
|
||||||
fault = tlb->translateTiming(req, tc, transState, mode);
|
currState->fault = tlb->translateTiming(currState->req, currState->tc,
|
||||||
|
currState->transState, currState->mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
req = NULL;
|
currState->req = NULL;
|
||||||
tc = NULL;
|
currState->tc = NULL;
|
||||||
delayed = false;
|
currState->delayed = false;
|
||||||
|
|
||||||
|
stateQueue.remove();
|
||||||
|
currState = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArmISA::TableWalker *
|
ArmISA::TableWalker *
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include "params/ArmTableWalker.hh"
|
#include "params/ArmTableWalker.hh"
|
||||||
#include "sim/faults.hh"
|
#include "sim/faults.hh"
|
||||||
#include "sim/eventq.hh"
|
#include "sim/eventq.hh"
|
||||||
|
#include "base/fifo_buffer.hh"
|
||||||
|
|
||||||
class DmaPort;
|
class DmaPort;
|
||||||
class ThreadContext;
|
class ThreadContext;
|
||||||
|
@ -241,12 +242,8 @@ class TableWalker : public MemObject
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Port to issue translation requests from */
|
struct WalkerState //: public SimObject
|
||||||
DmaPort *port;
|
{
|
||||||
|
|
||||||
/** TLB that is initiating these table walks */
|
|
||||||
TLB *tlb;
|
|
||||||
|
|
||||||
/** Thread context that we're doing the walk for */
|
/** Thread context that we're doing the walk for */
|
||||||
ThreadContext *tc;
|
ThreadContext *tc;
|
||||||
|
|
||||||
|
@ -286,15 +283,37 @@ class TableWalker : public MemObject
|
||||||
/** If the mode is timing or atomic */
|
/** If the mode is timing or atomic */
|
||||||
bool timing;
|
bool timing;
|
||||||
|
|
||||||
L1Descriptor l1Desc;
|
|
||||||
L2Descriptor l2Desc;
|
|
||||||
|
|
||||||
/** Save mode for use in delayed response */
|
/** Save mode for use in delayed response */
|
||||||
BaseTLB::Mode mode;
|
BaseTLB::Mode mode;
|
||||||
|
|
||||||
|
L1Descriptor l1Desc;
|
||||||
|
L2Descriptor l2Desc;
|
||||||
|
|
||||||
/** Whether L1/L2 descriptor response is delayed in timing mode */
|
/** Whether L1/L2 descriptor response is delayed in timing mode */
|
||||||
bool delayed;
|
bool delayed;
|
||||||
|
|
||||||
|
TableWalker *tableWalker;
|
||||||
|
|
||||||
|
void doL1Descriptor();
|
||||||
|
void doL2Descriptor();
|
||||||
|
|
||||||
|
std::string name() const {return tableWalker->name();}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FifoBuffer<WalkerState> stateQueue;
|
||||||
|
|
||||||
|
/** Port to issue translation requests from */
|
||||||
|
DmaPort *port;
|
||||||
|
|
||||||
|
/** TLB that is initiating these table walks */
|
||||||
|
TLB *tlb;
|
||||||
|
|
||||||
|
/** Cached copy of the sctlr as it existed when translation began */
|
||||||
|
SCTLR sctlr;
|
||||||
|
|
||||||
|
WalkerState *currState;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef ArmTableWalkerParams Params;
|
typedef ArmTableWalkerParams Params;
|
||||||
TableWalker(const Params *p);
|
TableWalker(const Params *p);
|
||||||
|
@ -313,7 +332,8 @@ class TableWalker : public MemObject
|
||||||
TLB::Translation *_trans, bool timing);
|
TLB::Translation *_trans, bool timing);
|
||||||
|
|
||||||
void setTlb(TLB *_tlb) { tlb = _tlb; }
|
void setTlb(TLB *_tlb) { tlb = _tlb; }
|
||||||
void memAttrs(ThreadContext *tc, TlbEntry &te, uint8_t texcb, bool s);
|
void memAttrs(ThreadContext *tc, TlbEntry &te, SCTLR sctlr,
|
||||||
|
uint8_t texcb, bool s);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -387,7 +387,7 @@ TLB::translateFs(RequestPtr req, ThreadContext *tc, Mode mode,
|
||||||
|
|
||||||
// Set memory attributes
|
// Set memory attributes
|
||||||
TlbEntry temp_te;
|
TlbEntry temp_te;
|
||||||
tableWalker->memAttrs(tc, temp_te, 0, 1);
|
tableWalker->memAttrs(tc, temp_te, sctlr, 0, 1);
|
||||||
temp_te.shareable = true;
|
temp_te.shareable = true;
|
||||||
DPRINTF(TLBVerbose, "(No MMU) setting memory attributes: shareable:\
|
DPRINTF(TLBVerbose, "(No MMU) setting memory attributes: shareable:\
|
||||||
%d, innerAttrs: %d, outerAttrs: %d\n", temp_te.shareable,
|
%d, innerAttrs: %d, outerAttrs: %d\n", temp_te.shareable,
|
||||||
|
|
Loading…
Reference in a new issue