arch-arm: Refactor PTW (#1060)
This PR is refactoring the Arm PageTableWalker in the following way: 1) Simplifying the currState handling logic (mainly the tear down) 2) Amending the TlbTestInterface APIs to use a RequestPtr reference 3) Use finalizePhysical even when MMU is off, which means allowing memory mapped m5ops to work also in that circumstance
This commit is contained in:
@@ -199,6 +199,26 @@ MMU::invalidateMiscReg()
|
||||
s2State.computeAddrTop.flush();
|
||||
}
|
||||
|
||||
Fault
|
||||
MMU::testAndFinalize(const RequestPtr &req,
|
||||
ThreadContext *tc, Mode mode,
|
||||
TlbEntry* te, CachedState &state) const
|
||||
{
|
||||
// If we don't have a valid tlb entry it means virtual memory
|
||||
// is not enabled
|
||||
auto domain = te ? te-> domain : TlbEntry::DomainType::NoAccess;
|
||||
|
||||
// Check for a tester generated address fault
|
||||
Fault fault = testTranslation(req, mode, domain, state);
|
||||
if (fault != NoFault) {
|
||||
return fault;
|
||||
} else {
|
||||
// Now that we checked no fault has been generated in the
|
||||
// translation process, we can finalize the physical address
|
||||
return finalizePhysical(req, tc, mode);
|
||||
}
|
||||
}
|
||||
|
||||
Fault
|
||||
MMU::finalizePhysical(const RequestPtr &req,
|
||||
ThreadContext *tc, Mode mode) const
|
||||
@@ -848,7 +868,7 @@ MMU::translateMmuOff(ThreadContext *tc, const RequestPtr &req, Mode mode,
|
||||
state.isStage2);
|
||||
setAttr(temp_te.attributes);
|
||||
|
||||
return testTranslation(req, mode, TlbEntry::DomainType::NoAccess, state);
|
||||
return testAndFinalize(req, tc, mode, nullptr, state);
|
||||
}
|
||||
|
||||
Fault
|
||||
@@ -909,18 +929,11 @@ MMU::translateMmuOn(ThreadContext* tc, const RequestPtr &req, Mode mode,
|
||||
tranMethod);
|
||||
}
|
||||
|
||||
// Check for a trickbox generated address fault
|
||||
if (fault == NoFault)
|
||||
fault = testTranslation(req, mode, te->domain, state);
|
||||
fault = testAndFinalize(req, tc, mode, te, state);
|
||||
}
|
||||
|
||||
if (fault == NoFault) {
|
||||
// Don't try to finalize a physical address unless the
|
||||
// translation has completed (i.e., there is a table entry).
|
||||
return te ? finalizePhysical(req, tc, mode) : NoFault;
|
||||
} else {
|
||||
return fault;
|
||||
}
|
||||
return fault;
|
||||
}
|
||||
|
||||
Fault
|
||||
@@ -1551,12 +1564,16 @@ MMU::setTestInterface(SimObject *_ti)
|
||||
TlbTestInterface *ti(dynamic_cast<TlbTestInterface *>(_ti));
|
||||
fatal_if(!ti, "%s is not a valid ARM TLB tester\n", _ti->name());
|
||||
test = ti;
|
||||
itbWalker->setTestInterface(test);
|
||||
dtbWalker->setTestInterface(test);
|
||||
itbStage2Walker->setTestInterface(test);
|
||||
dtbStage2Walker->setTestInterface(test);
|
||||
}
|
||||
}
|
||||
|
||||
Fault
|
||||
MMU::testTranslation(const RequestPtr &req, Mode mode,
|
||||
TlbEntry::DomainType domain, CachedState &state)
|
||||
TlbEntry::DomainType domain, CachedState &state) const
|
||||
{
|
||||
if (!test || !req->hasSize() || req->getSize() == 0 ||
|
||||
req->isCacheMaintenance()) {
|
||||
@@ -1566,28 +1583,6 @@ MMU::testTranslation(const RequestPtr &req, Mode mode,
|
||||
}
|
||||
}
|
||||
|
||||
Fault
|
||||
MMU::testWalk(Addr pa, Addr size, Addr va, bool is_secure, Mode mode,
|
||||
TlbEntry::DomainType domain, LookupLevel lookup_level,
|
||||
bool stage2)
|
||||
{
|
||||
return testWalk(pa, size, va, is_secure, mode, domain, lookup_level,
|
||||
stage2 ? s2State : s1State);
|
||||
}
|
||||
|
||||
Fault
|
||||
MMU::testWalk(Addr pa, Addr size, Addr va, bool is_secure, Mode mode,
|
||||
TlbEntry::DomainType domain, LookupLevel lookup_level,
|
||||
CachedState &state)
|
||||
{
|
||||
if (!test) {
|
||||
return NoFault;
|
||||
} else {
|
||||
return test->walkCheck(pa, size, va, is_secure, state.isPriv, mode,
|
||||
domain, lookup_level);
|
||||
}
|
||||
}
|
||||
|
||||
MMU::Stats::Stats(statistics::Group *parent)
|
||||
: statistics::Group(parent),
|
||||
ADD_STAT(alignFaults, statistics::units::Count::get(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013, 2016, 2019-2023 Arm Limited
|
||||
* Copyright (c) 2010-2013, 2016, 2019-2024 Arm Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
@@ -460,13 +460,7 @@ class MMU : public BaseMMU
|
||||
void setTestInterface(SimObject *ti);
|
||||
|
||||
Fault testTranslation(const RequestPtr &req, Mode mode,
|
||||
TlbEntry::DomainType domain, CachedState &state);
|
||||
Fault testWalk(Addr pa, Addr size, Addr va, bool is_secure, Mode mode,
|
||||
TlbEntry::DomainType domain,
|
||||
LookupLevel lookup_level, bool stage2);
|
||||
Fault testWalk(Addr pa, Addr size, Addr va, bool is_secure, Mode mode,
|
||||
TlbEntry::DomainType domain,
|
||||
LookupLevel lookup_level, CachedState &state);
|
||||
TlbEntry::DomainType domain, CachedState &state) const;
|
||||
|
||||
protected:
|
||||
bool checkWalkCache() const;
|
||||
@@ -477,6 +471,10 @@ class MMU : public BaseMMU
|
||||
ThreadContext *tc, ArmTranslationType tran_type,
|
||||
bool stage2);
|
||||
|
||||
Fault testAndFinalize(const RequestPtr &req,
|
||||
ThreadContext *tc, Mode mode,
|
||||
TlbEntry *te, CachedState &state) const;
|
||||
|
||||
protected:
|
||||
ContextID miscRegContext;
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ using namespace ArmISA;
|
||||
TableWalker::TableWalker(const Params &p)
|
||||
: ClockedObject(p),
|
||||
requestorId(p.sys->getRequestorId(this)),
|
||||
port(new Port(*this, requestorId)),
|
||||
port(new Port(*this)),
|
||||
isStage2(p.is_stage2), tlb(NULL),
|
||||
currState(NULL), pending(false),
|
||||
numSquashable(p.num_squash_per_cycle),
|
||||
@@ -78,7 +78,8 @@ TableWalker::TableWalker(const Params &p)
|
||||
doL3LongDescEvent([this]{ doL3LongDescriptorWrapper(); }, name()),
|
||||
LongDescEventByLevel { &doL0LongDescEvent, &doL1LongDescEvent,
|
||||
&doL2LongDescEvent, &doL3LongDescEvent },
|
||||
doProcessEvent([this]{ processWalkWrapper(); }, name())
|
||||
doProcessEvent([this]{ processWalkWrapper(); }, name()),
|
||||
test(nullptr)
|
||||
{
|
||||
sctlr = 0;
|
||||
|
||||
@@ -139,25 +140,20 @@ TableWalker::WalkerState::WalkerState() :
|
||||
{
|
||||
}
|
||||
|
||||
TableWalker::Port::Port(TableWalker& _walker, RequestorID id)
|
||||
TableWalker::Port::Port(TableWalker& _walker)
|
||||
: QueuedRequestPort(_walker.name() + ".port", reqQueue, snoopRespQueue),
|
||||
owner{_walker},
|
||||
reqQueue(_walker, *this),
|
||||
snoopRespQueue(_walker, *this),
|
||||
requestorId(id)
|
||||
snoopRespQueue(_walker, *this)
|
||||
{
|
||||
}
|
||||
|
||||
PacketPtr
|
||||
TableWalker::Port::createPacket(
|
||||
Addr desc_addr, int size,
|
||||
uint8_t *data, Request::Flags flags, Tick delay,
|
||||
const RequestPtr &req,
|
||||
uint8_t *data, Tick delay,
|
||||
Event *event)
|
||||
{
|
||||
RequestPtr req = std::make_shared<Request>(
|
||||
desc_addr, size, flags, requestorId);
|
||||
req->taskId(context_switch_task_id::DMA);
|
||||
|
||||
PacketPtr pkt = new Packet(req, MemCmd::ReadReq);
|
||||
pkt->dataStatic(data);
|
||||
|
||||
@@ -171,10 +167,9 @@ TableWalker::Port::createPacket(
|
||||
|
||||
void
|
||||
TableWalker::Port::sendFunctionalReq(
|
||||
Addr desc_addr, int size,
|
||||
uint8_t *data, Request::Flags flags)
|
||||
const RequestPtr &req, uint8_t *data)
|
||||
{
|
||||
auto pkt = createPacket(desc_addr, size, data, flags, 0, nullptr);
|
||||
auto pkt = createPacket(req, data, 0, nullptr);
|
||||
|
||||
sendFunctional(pkt);
|
||||
|
||||
@@ -183,10 +178,10 @@ TableWalker::Port::sendFunctionalReq(
|
||||
|
||||
void
|
||||
TableWalker::Port::sendAtomicReq(
|
||||
Addr desc_addr, int size,
|
||||
uint8_t *data, Request::Flags flags, Tick delay)
|
||||
const RequestPtr &req,
|
||||
uint8_t *data, Tick delay)
|
||||
{
|
||||
auto pkt = createPacket(desc_addr, size, data, flags, delay, nullptr);
|
||||
auto pkt = createPacket(req, data, delay, nullptr);
|
||||
|
||||
Tick lat = sendAtomic(pkt);
|
||||
|
||||
@@ -195,11 +190,11 @@ TableWalker::Port::sendAtomicReq(
|
||||
|
||||
void
|
||||
TableWalker::Port::sendTimingReq(
|
||||
Addr desc_addr, int size,
|
||||
uint8_t *data, Request::Flags flags, Tick delay,
|
||||
const RequestPtr &req,
|
||||
uint8_t *data, Tick delay,
|
||||
Event *event)
|
||||
{
|
||||
auto pkt = createPacket(desc_addr, size, data, flags, delay, event);
|
||||
auto pkt = createPacket(req, data, delay, event);
|
||||
|
||||
schedTimingReq(pkt, curTick());
|
||||
}
|
||||
@@ -332,18 +327,15 @@ TableWalker::walk(const RequestPtr &_req, ThreadContext *_tc, uint16_t _asid,
|
||||
|
||||
currState->startTime = curTick();
|
||||
currState->tc = _tc;
|
||||
// ARM DDI 0487A.f (ARMv8 ARM) pg J8-5672
|
||||
// aarch32/translation/translation/AArch32.TranslateAddress dictates
|
||||
// even AArch32 EL0 will use AArch64 translation if EL1 is in AArch64.
|
||||
currState->el =
|
||||
MMU::tranTypeEL(_tc->readMiscReg(MISCREG_CPSR),
|
||||
_tc->readMiscReg(MISCREG_SCR_EL3),
|
||||
tranType);
|
||||
|
||||
if (isStage2) {
|
||||
currState->el = EL1;
|
||||
currState->regime = TranslationRegime::EL10;
|
||||
currState->aarch64 = ELIs64(_tc, EL2);
|
||||
} else {
|
||||
currState->el =
|
||||
MMU::tranTypeEL(_tc->readMiscReg(MISCREG_CPSR),
|
||||
_tc->readMiscReg(MISCREG_SCR_EL3),
|
||||
tranType);
|
||||
currState->regime =
|
||||
translationRegime(_tc, currState->el);
|
||||
currState->aarch64 =
|
||||
@@ -442,40 +434,53 @@ TableWalker::walk(const RequestPtr &_req, ThreadContext *_tc, uint16_t _asid,
|
||||
++stats.walksShortDescriptor;
|
||||
}
|
||||
|
||||
if (!currState->timing) {
|
||||
if (currState->timing && (pending || pendingQueue.size())) {
|
||||
pendingQueue.push_back(currState);
|
||||
currState = NULL;
|
||||
pendingChange();
|
||||
return NoFault;
|
||||
} else {
|
||||
if (currState->timing) {
|
||||
pending = true;
|
||||
pendingChange();
|
||||
}
|
||||
|
||||
Fault fault = NoFault;
|
||||
if (currState->aarch64)
|
||||
if (currState->aarch64) {
|
||||
fault = processWalkAArch64();
|
||||
else if (long_desc_format)
|
||||
} else if (long_desc_format) {
|
||||
fault = processWalkLPAE();
|
||||
else
|
||||
} else {
|
||||
fault = processWalk();
|
||||
}
|
||||
|
||||
// If this was a functional non-timing access restore state to
|
||||
// how we found it.
|
||||
if (currState->functional) {
|
||||
delete currState;
|
||||
currState = savedCurrState;
|
||||
} else if (currState->timing) {
|
||||
if (fault) {
|
||||
pending = false;
|
||||
nextWalk(currState->tc);
|
||||
delete currState;
|
||||
currState = NULL;
|
||||
} else {
|
||||
// Either we are using the long descriptor, which means we
|
||||
// need to extract the queue index from longDesc, or we are
|
||||
// using the short. In the latter we always start at L1
|
||||
LookupLevel curr_lookup_level = long_desc_format ?
|
||||
currState->longDesc.lookupLevel : LookupLevel::L1;
|
||||
|
||||
stashCurrState(curr_lookup_level);
|
||||
}
|
||||
} else if (fault) {
|
||||
currState->tc = NULL;
|
||||
currState->req = NULL;
|
||||
}
|
||||
|
||||
return fault;
|
||||
}
|
||||
|
||||
if (pending || pendingQueue.size()) {
|
||||
pendingQueue.push_back(currState);
|
||||
currState = NULL;
|
||||
pendingChange();
|
||||
} else {
|
||||
pending = true;
|
||||
pendingChange();
|
||||
if (currState->aarch64)
|
||||
return processWalkAArch64();
|
||||
else if (long_desc_format)
|
||||
return processWalkLPAE();
|
||||
else
|
||||
return processWalk();
|
||||
}
|
||||
|
||||
return NoFault;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -501,27 +506,36 @@ TableWalker::processWalkWrapper()
|
||||
// We've got a valid request, lets process it
|
||||
pending = true;
|
||||
pendingQueue.pop_front();
|
||||
// Keep currState in case one of the processWalk... calls NULLs it
|
||||
|
||||
bool long_desc_format = currState->aarch64 || currState->el == EL2 ||
|
||||
isStage2 || longDescFormatInUse(currState->tc);
|
||||
|
||||
if (te && te->partial) {
|
||||
currState->walkEntry = *te;
|
||||
}
|
||||
WalkerState *curr_state_copy = currState;
|
||||
Fault f;
|
||||
if (currState->aarch64)
|
||||
f = processWalkAArch64();
|
||||
else if (bool hyp = currState->el == EL2;
|
||||
longDescFormatInUse(currState->tc) ||
|
||||
hyp || isStage2)
|
||||
f = processWalkLPAE();
|
||||
else
|
||||
f = processWalk();
|
||||
Fault fault;
|
||||
if (currState->aarch64) {
|
||||
fault = processWalkAArch64();
|
||||
} else if (long_desc_format) {
|
||||
fault = processWalkLPAE();
|
||||
} else {
|
||||
fault = processWalk();
|
||||
}
|
||||
|
||||
if (f != NoFault) {
|
||||
curr_state_copy->transState->finish(f, curr_state_copy->req,
|
||||
curr_state_copy->tc, curr_state_copy->mode);
|
||||
if (fault != NoFault) {
|
||||
pending = false;
|
||||
nextWalk(currState->tc);
|
||||
|
||||
delete curr_state_copy;
|
||||
currState->transState->finish(fault, currState->req,
|
||||
currState->tc, currState->mode);
|
||||
|
||||
delete currState;
|
||||
currState = NULL;
|
||||
} else {
|
||||
LookupLevel curr_lookup_level = long_desc_format ?
|
||||
currState->longDesc.lookupLevel : LookupLevel::L1;
|
||||
|
||||
stashCurrState(curr_lookup_level);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -648,23 +662,6 @@ TableWalker::processWalk()
|
||||
DPRINTF(TLB, " - Descriptor at address %#x (%s)\n", l1desc_addr,
|
||||
currState->isSecure ? "s" : "ns");
|
||||
|
||||
// Trickbox address check
|
||||
Fault f;
|
||||
f = testWalk(l1desc_addr, sizeof(uint32_t),
|
||||
TlbEntry::DomainType::NoAccess, LookupLevel::L1, isStage2);
|
||||
if (f) {
|
||||
DPRINTF(TLB, "Trickbox check caused fault on %#x\n", currState->vaddr_tainted);
|
||||
if (currState->timing) {
|
||||
pending = false;
|
||||
nextWalk(currState->tc);
|
||||
currState = NULL;
|
||||
} else {
|
||||
currState->tc = NULL;
|
||||
currState->req = NULL;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
Request::Flags flag = Request::PT_WALK;
|
||||
if (currState->sctlr.c == 0 || currState->isUncacheable) {
|
||||
flag.set(Request::UNCACHEABLE);
|
||||
@@ -674,16 +671,13 @@ TableWalker::processWalk()
|
||||
flag.set(Request::SECURE);
|
||||
}
|
||||
|
||||
bool delayed;
|
||||
delayed = fetchDescriptor(l1desc_addr, (uint8_t*)&currState->l1Desc.data,
|
||||
sizeof(uint32_t), flag, LookupLevel::L1,
|
||||
&doL1DescEvent,
|
||||
&TableWalker::doL1Descriptor);
|
||||
if (!delayed) {
|
||||
f = currState->fault;
|
||||
}
|
||||
fetchDescriptor(
|
||||
l1desc_addr, currState->l1Desc,
|
||||
sizeof(uint32_t), flag, LookupLevel::L1,
|
||||
&doL1DescEvent,
|
||||
&TableWalker::doL1Descriptor);
|
||||
|
||||
return f;
|
||||
return currState->fault;
|
||||
}
|
||||
|
||||
Fault
|
||||
@@ -824,23 +818,6 @@ TableWalker::processWalkLPAE()
|
||||
desc_addr, currState->isSecure ? "s" : "ns");
|
||||
}
|
||||
|
||||
// Trickbox address check
|
||||
Fault f = testWalk(desc_addr, sizeof(uint64_t),
|
||||
TlbEntry::DomainType::NoAccess, start_lookup_level,
|
||||
isStage2);
|
||||
if (f) {
|
||||
DPRINTF(TLB, "Trickbox check caused fault on %#x\n", currState->vaddr_tainted);
|
||||
if (currState->timing) {
|
||||
pending = false;
|
||||
nextWalk(currState->tc);
|
||||
currState = NULL;
|
||||
} else {
|
||||
currState->tc = NULL;
|
||||
currState->req = NULL;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
if (currState->sctlr.c == 0 || currState->isUncacheable) {
|
||||
flag.set(Request::UNCACHEABLE);
|
||||
}
|
||||
@@ -849,15 +826,13 @@ TableWalker::processWalkLPAE()
|
||||
currState->longDesc.aarch64 = false;
|
||||
currState->longDesc.grainSize = Grain4KB;
|
||||
|
||||
bool delayed = fetchDescriptor(desc_addr, (uint8_t*)&currState->longDesc.data,
|
||||
sizeof(uint64_t), flag, start_lookup_level,
|
||||
LongDescEventByLevel[start_lookup_level],
|
||||
&TableWalker::doLongDescriptor);
|
||||
if (!delayed) {
|
||||
f = currState->fault;
|
||||
}
|
||||
fetchDescriptor(
|
||||
desc_addr, currState->longDesc,
|
||||
sizeof(uint64_t), flag, start_lookup_level,
|
||||
LongDescEventByLevel[start_lookup_level],
|
||||
&TableWalker::doLongDescriptor);
|
||||
|
||||
return f;
|
||||
return currState->fault;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1022,30 +997,19 @@ TableWalker::processWalkAArch64()
|
||||
const bool is_atomic = currState->req->isAtomic();
|
||||
|
||||
if (fault) {
|
||||
Fault f;
|
||||
if (currState->isFetch)
|
||||
f = std::make_shared<PrefetchAbort>(
|
||||
if (currState->isFetch) {
|
||||
return std::make_shared<PrefetchAbort>(
|
||||
currState->vaddr_tainted,
|
||||
ArmFault::TranslationLL + LookupLevel::L0, isStage2,
|
||||
ArmFault::LpaeTran);
|
||||
else
|
||||
f = std::make_shared<DataAbort>(
|
||||
} else {
|
||||
return std::make_shared<DataAbort>(
|
||||
currState->vaddr_tainted,
|
||||
TlbEntry::DomainType::NoAccess,
|
||||
is_atomic ? false : currState->isWrite,
|
||||
ArmFault::TranslationLL + LookupLevel::L0,
|
||||
isStage2, ArmFault::LpaeTran);
|
||||
|
||||
if (currState->timing) {
|
||||
pending = false;
|
||||
nextWalk(currState->tc);
|
||||
currState = NULL;
|
||||
} else {
|
||||
currState->tc = NULL;
|
||||
currState->req = NULL;
|
||||
}
|
||||
return f;
|
||||
|
||||
}
|
||||
|
||||
if (tg == ReservedGrain) {
|
||||
@@ -1069,49 +1033,20 @@ TableWalker::processWalkAArch64()
|
||||
// necessary
|
||||
if (checkAddrSizeFaultAArch64(table_addr, currState->physAddrRange)) {
|
||||
DPRINTF(TLB, "Address size fault before any lookup\n");
|
||||
Fault f;
|
||||
if (currState->isFetch)
|
||||
f = std::make_shared<PrefetchAbort>(
|
||||
return std::make_shared<PrefetchAbort>(
|
||||
currState->vaddr_tainted,
|
||||
ArmFault::AddressSizeLL + start_lookup_level,
|
||||
isStage2,
|
||||
ArmFault::LpaeTran);
|
||||
else
|
||||
f = std::make_shared<DataAbort>(
|
||||
return std::make_shared<DataAbort>(
|
||||
currState->vaddr_tainted,
|
||||
TlbEntry::DomainType::NoAccess,
|
||||
is_atomic ? false : currState->isWrite,
|
||||
ArmFault::AddressSizeLL + start_lookup_level,
|
||||
isStage2,
|
||||
ArmFault::LpaeTran);
|
||||
|
||||
|
||||
if (currState->timing) {
|
||||
pending = false;
|
||||
nextWalk(currState->tc);
|
||||
currState = NULL;
|
||||
} else {
|
||||
currState->tc = NULL;
|
||||
currState->req = NULL;
|
||||
}
|
||||
return f;
|
||||
|
||||
}
|
||||
|
||||
// Trickbox address check
|
||||
Fault f = testWalk(desc_addr, sizeof(uint64_t),
|
||||
TlbEntry::DomainType::NoAccess, start_lookup_level, isStage2);
|
||||
if (f) {
|
||||
DPRINTF(TLB, "Trickbox check caused fault on %#x\n", currState->vaddr_tainted);
|
||||
if (currState->timing) {
|
||||
pending = false;
|
||||
nextWalk(currState->tc);
|
||||
currState = NULL;
|
||||
} else {
|
||||
currState->tc = NULL;
|
||||
currState->req = NULL;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
Request::Flags flag = Request::PT_WALK;
|
||||
@@ -1128,18 +1063,12 @@ TableWalker::processWalkAArch64()
|
||||
currState->longDesc.grainSize = tg;
|
||||
currState->longDesc.physAddrRange = _physAddrRange;
|
||||
|
||||
if (currState->timing) {
|
||||
fetchDescriptor(desc_addr, (uint8_t*) &currState->longDesc.data,
|
||||
sizeof(uint64_t), flag, start_lookup_level,
|
||||
LongDescEventByLevel[start_lookup_level], NULL);
|
||||
} else {
|
||||
fetchDescriptor(desc_addr, (uint8_t*)&currState->longDesc.data,
|
||||
sizeof(uint64_t), flag, -1, NULL,
|
||||
&TableWalker::doLongDescriptor);
|
||||
f = currState->fault;
|
||||
}
|
||||
fetchDescriptor(desc_addr, currState->longDesc,
|
||||
sizeof(uint64_t), flag, start_lookup_level,
|
||||
LongDescEventByLevel[start_lookup_level],
|
||||
&TableWalker::doLongDescriptor);
|
||||
|
||||
return f;
|
||||
return currState->fault;
|
||||
}
|
||||
|
||||
std::tuple<Addr, Addr, TableWalker::LookupLevel>
|
||||
@@ -1690,19 +1619,6 @@ TableWalker::doL1Descriptor()
|
||||
DPRINTF(TLB, "L1 descriptor points to page table at: %#x (%s)\n",
|
||||
l2desc_addr, currState->isSecure ? "s" : "ns");
|
||||
|
||||
// Trickbox address check
|
||||
currState->fault = testWalk(l2desc_addr, sizeof(uint32_t),
|
||||
currState->l1Desc.domain(),
|
||||
LookupLevel::L2, isStage2);
|
||||
|
||||
if (currState->fault) {
|
||||
if (!currState->timing) {
|
||||
currState->tc = NULL;
|
||||
currState->req = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Request::Flags flag = Request::PT_WALK;
|
||||
|
||||
if (currState->sctlr.c == 0 || currState->isUncacheable) {
|
||||
@@ -1712,14 +1628,13 @@ TableWalker::doL1Descriptor()
|
||||
if (currState->isSecure)
|
||||
flag.set(Request::SECURE);
|
||||
|
||||
bool delayed;
|
||||
delayed = fetchDescriptor(l2desc_addr,
|
||||
(uint8_t*)&currState->l2Desc.data,
|
||||
sizeof(uint32_t), flag, -1, &doL2DescEvent,
|
||||
&TableWalker::doL2Descriptor);
|
||||
if (delayed) {
|
||||
currState->delayed = true;
|
||||
}
|
||||
fetchDescriptor(
|
||||
l2desc_addr, currState->l2Desc,
|
||||
sizeof(uint32_t), flag, LookupLevel::L2,
|
||||
&doL2DescEvent,
|
||||
&TableWalker::doL2Descriptor);
|
||||
|
||||
currState->delayed = currState->timing;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1858,24 +1773,10 @@ TableWalker::doLongDescriptor()
|
||||
return;
|
||||
}
|
||||
|
||||
// Trickbox address check
|
||||
currState->fault = testWalk(
|
||||
next_desc_addr, sizeof(uint64_t), TlbEntry::DomainType::Client,
|
||||
toLookupLevel(currState->longDesc.lookupLevel +1), isStage2);
|
||||
|
||||
if (currState->fault) {
|
||||
if (!currState->timing) {
|
||||
currState->tc = NULL;
|
||||
currState->req = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mmu->hasWalkCache()) {
|
||||
insertPartialTableEntry(currState->longDesc);
|
||||
}
|
||||
|
||||
|
||||
Request::Flags flag = Request::PT_WALK;
|
||||
if (currState->secureLookup)
|
||||
flag.set(Request::SECURE);
|
||||
@@ -1899,13 +1800,12 @@ TableWalker::doLongDescriptor()
|
||||
break;
|
||||
}
|
||||
|
||||
bool delayed;
|
||||
delayed = fetchDescriptor(next_desc_addr, (uint8_t*)&currState->longDesc.data,
|
||||
sizeof(uint64_t), flag, -1, event,
|
||||
&TableWalker::doLongDescriptor);
|
||||
if (delayed) {
|
||||
currState->delayed = true;
|
||||
}
|
||||
fetchDescriptor(
|
||||
next_desc_addr, currState->longDesc,
|
||||
sizeof(uint64_t), flag, L, event,
|
||||
&TableWalker::doLongDescriptor);
|
||||
|
||||
currState->delayed = currState->timing;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
@@ -2026,7 +1926,7 @@ TableWalker::doL1DescriptorWrapper()
|
||||
delete currState;
|
||||
} else {
|
||||
// need to do L2 descriptor
|
||||
stateQueues[LookupLevel::L2].push_back(currState);
|
||||
stashCurrState(LookupLevel::L2);
|
||||
}
|
||||
currState = NULL;
|
||||
}
|
||||
@@ -2152,7 +2052,7 @@ TableWalker::doLongDescriptorWrapper(LookupLevel curr_lookup_level)
|
||||
if (curr_lookup_level >= LookupLevel::Num_ArmLookupLevel - 1)
|
||||
panic("Max. number of lookups already reached in table walk\n");
|
||||
// Need to perform additional lookups
|
||||
stateQueues[currState->longDesc.lookupLevel].push_back(currState);
|
||||
stashCurrState(currState->longDesc.lookupLevel);
|
||||
}
|
||||
currState = NULL;
|
||||
}
|
||||
@@ -2167,75 +2067,85 @@ TableWalker::nextWalk(ThreadContext *tc)
|
||||
completeDrain();
|
||||
}
|
||||
|
||||
bool
|
||||
TableWalker::fetchDescriptor(Addr descAddr, uint8_t *data, int numBytes,
|
||||
Request::Flags flags, int queueIndex, Event *event,
|
||||
void
|
||||
TableWalker::fetchDescriptor(Addr desc_addr,
|
||||
DescriptorBase &descriptor, int num_bytes,
|
||||
Request::Flags flags, LookupLevel lookup_level, Event *event,
|
||||
void (TableWalker::*doDescriptor)())
|
||||
{
|
||||
bool isTiming = currState->timing;
|
||||
uint8_t *data = descriptor.getRawPtr();
|
||||
|
||||
DPRINTF(PageTableWalker,
|
||||
"Fetching descriptor at address: 0x%x stage2Req: %d\n",
|
||||
descAddr, currState->stage2Req);
|
||||
desc_addr, currState->stage2Req);
|
||||
|
||||
// If this translation has a stage 2 then we know descAddr is an IPA and
|
||||
// If this translation has a stage 2 then we know desc_addr is an IPA and
|
||||
// needs to be translated before we can access the page table. Do that
|
||||
// check here.
|
||||
if (currState->stage2Req) {
|
||||
Fault fault;
|
||||
|
||||
if (isTiming) {
|
||||
if (currState->timing) {
|
||||
auto *tran = new
|
||||
Stage2Walk(*this, data, event, currState->vaddr,
|
||||
currState->mode, currState->tranType);
|
||||
currState->stage2Tran = tran;
|
||||
readDataTimed(currState->tc, descAddr, tran, numBytes, flags);
|
||||
readDataTimed(currState->tc, desc_addr, tran, num_bytes, flags);
|
||||
fault = tran->fault;
|
||||
|
||||
if (fault != NoFault) {
|
||||
currState->fault = fault;
|
||||
}
|
||||
} else {
|
||||
fault = readDataUntimed(currState->tc,
|
||||
currState->vaddr, descAddr, data, numBytes, flags,
|
||||
currState->vaddr, desc_addr, data, num_bytes, flags,
|
||||
currState->mode,
|
||||
currState->tranType,
|
||||
currState->functional);
|
||||
}
|
||||
|
||||
if (fault != NoFault) {
|
||||
currState->fault = fault;
|
||||
}
|
||||
if (isTiming) {
|
||||
if (queueIndex >= 0) {
|
||||
DPRINTF(PageTableWalker, "Adding to walker fifo: "
|
||||
"queue size before adding: %d\n",
|
||||
stateQueues[queueIndex].size());
|
||||
stateQueues[queueIndex].push_back(currState);
|
||||
currState = NULL;
|
||||
if (fault != NoFault) {
|
||||
currState->fault = fault;
|
||||
}
|
||||
} else {
|
||||
|
||||
(this->*doDescriptor)();
|
||||
}
|
||||
} else {
|
||||
if (isTiming) {
|
||||
port->sendTimingReq(descAddr, numBytes, data, flags,
|
||||
RequestPtr req = std::make_shared<Request>(
|
||||
desc_addr, num_bytes, flags, requestorId);
|
||||
req->taskId(context_switch_task_id::DMA);
|
||||
|
||||
Fault fault = testWalk(req, descriptor.domain(),
|
||||
lookup_level);
|
||||
|
||||
if (fault != NoFault) {
|
||||
currState->fault = fault;
|
||||
return;
|
||||
}
|
||||
|
||||
if (currState->timing) {
|
||||
port->sendTimingReq(req, data,
|
||||
currState->tc->getCpuPtr()->clockPeriod(), event);
|
||||
|
||||
if (queueIndex >= 0) {
|
||||
DPRINTF(PageTableWalker, "Adding to walker fifo: "
|
||||
"queue size before adding: %d\n",
|
||||
stateQueues[queueIndex].size());
|
||||
stateQueues[queueIndex].push_back(currState);
|
||||
currState = NULL;
|
||||
}
|
||||
} else if (!currState->functional) {
|
||||
port->sendAtomicReq(descAddr, numBytes, data, flags,
|
||||
port->sendAtomicReq(req, data,
|
||||
currState->tc->getCpuPtr()->clockPeriod());
|
||||
|
||||
(this->*doDescriptor)();
|
||||
} else {
|
||||
port->sendFunctionalReq(descAddr, numBytes, data, flags);
|
||||
port->sendFunctionalReq(req, data);
|
||||
(this->*doDescriptor)();
|
||||
}
|
||||
}
|
||||
return (isTiming);
|
||||
}
|
||||
|
||||
void
|
||||
TableWalker::stashCurrState(int queue_idx)
|
||||
{
|
||||
DPRINTF(PageTableWalker, "Adding to walker fifo: "
|
||||
"queue size before adding: %d\n",
|
||||
stateQueues[queue_idx].size());
|
||||
stateQueues[queue_idx].push_back(currState);
|
||||
currState = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2396,13 +2306,23 @@ TableWalker::pendingChange()
|
||||
}
|
||||
|
||||
Fault
|
||||
TableWalker::testWalk(Addr pa, Addr size, TlbEntry::DomainType domain,
|
||||
LookupLevel lookup_level, bool stage2)
|
||||
TableWalker::testWalk(const RequestPtr &walk_req, TlbEntry::DomainType domain,
|
||||
LookupLevel lookup_level)
|
||||
{
|
||||
return mmu->testWalk(pa, size, currState->vaddr, currState->isSecure,
|
||||
currState->mode, domain, lookup_level, stage2);
|
||||
if (!test) {
|
||||
return NoFault;
|
||||
} else {
|
||||
return test->walkCheck(walk_req, currState->vaddr, currState->isSecure,
|
||||
currState->el != EL0,
|
||||
currState->mode, domain, lookup_level);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TableWalker::setTestInterface(TlbTestInterface *ti)
|
||||
{
|
||||
test = ti;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
TableWalker::pageSizeNtoStatBin(uint8_t N)
|
||||
@@ -2503,8 +2423,7 @@ TableWalker::Stage2Walk::finish(const Fault &_fault,
|
||||
}
|
||||
|
||||
if (_fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) {
|
||||
parent.getTableWalkerPort().sendTimingReq(
|
||||
req->getPaddr(), numBytes, data, req->getFlags(),
|
||||
parent.getTableWalkerPort().sendTimingReq(req, data,
|
||||
tc->getCpuPtr()->clockPeriod(), event);
|
||||
} else {
|
||||
// We can't do the DMA access as there's been a problem, so tell the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2016, 2019, 2021-2023 Arm Limited
|
||||
* Copyright (c) 2010-2016, 2019, 2021-2024 Arm Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
@@ -86,6 +86,7 @@ class TableWalker : public ClockedObject
|
||||
virtual uint8_t offsetBits() const = 0;
|
||||
virtual bool secure(bool have_security, WalkerState *currState) const = 0;
|
||||
virtual std::string dbgHeader() const = 0;
|
||||
virtual uint8_t* getRawPtr() = 0;
|
||||
virtual uint64_t getRawData() const = 0;
|
||||
virtual uint8_t texcb() const
|
||||
{
|
||||
@@ -122,6 +123,12 @@ class TableWalker : public ClockedObject
|
||||
lookupLevel = LookupLevel::L1;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
getRawPtr() override
|
||||
{
|
||||
return reinterpret_cast<uint8_t*>(&data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
getRawData() const override
|
||||
{
|
||||
@@ -291,6 +298,12 @@ class TableWalker : public ClockedObject
|
||||
lookupLevel = LookupLevel::L2;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
getRawPtr() override
|
||||
{
|
||||
return reinterpret_cast<uint8_t*>(&data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
getRawData() const override
|
||||
{
|
||||
@@ -441,6 +454,12 @@ class TableWalker : public ClockedObject
|
||||
|
||||
uint8_t physAddrRange;
|
||||
|
||||
uint8_t*
|
||||
getRawPtr() override
|
||||
{
|
||||
return reinterpret_cast<uint8_t*>(&data);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
getRawData() const override
|
||||
{
|
||||
@@ -943,14 +962,11 @@ class TableWalker : public ClockedObject
|
||||
class Port : public QueuedRequestPort
|
||||
{
|
||||
public:
|
||||
Port(TableWalker& _walker, RequestorID id);
|
||||
Port(TableWalker& _walker);
|
||||
|
||||
void sendFunctionalReq(Addr desc_addr, int size,
|
||||
uint8_t *data, Request::Flags flag);
|
||||
void sendAtomicReq(Addr desc_addr, int size,
|
||||
uint8_t *data, Request::Flags flag, Tick delay);
|
||||
void sendTimingReq(Addr desc_addr, int size,
|
||||
uint8_t *data, Request::Flags flag, Tick delay,
|
||||
void sendFunctionalReq(const RequestPtr &req, uint8_t *data);
|
||||
void sendAtomicReq(const RequestPtr &req, uint8_t *data, Tick delay);
|
||||
void sendTimingReq(const RequestPtr &req, uint8_t *data, Tick delay,
|
||||
Event *event);
|
||||
|
||||
bool recvTimingResp(PacketPtr pkt) override;
|
||||
@@ -960,8 +976,7 @@ class TableWalker : public ClockedObject
|
||||
void handleResp(TableWalkerState *state, Addr addr,
|
||||
Addr size, Tick delay=0);
|
||||
|
||||
PacketPtr createPacket(Addr desc_addr, int size,
|
||||
uint8_t *data, Request::Flags flag,
|
||||
PacketPtr createPacket(const RequestPtr &req, uint8_t *data,
|
||||
Tick delay, Event *event);
|
||||
|
||||
private:
|
||||
@@ -972,9 +987,6 @@ class TableWalker : public ClockedObject
|
||||
|
||||
/** Packet queue used to store outgoing snoop responses. */
|
||||
SnoopRespPacketQueue snoopRespQueue;
|
||||
|
||||
/** Cached requestorId of the table walker */
|
||||
RequestorID requestorId;
|
||||
};
|
||||
|
||||
/** This translation class is used to trigger the data fetch once a timing
|
||||
@@ -1148,8 +1160,9 @@ class TableWalker : public ClockedObject
|
||||
void doLongDescriptorWrapper(LookupLevel curr_lookup_level);
|
||||
Event* LongDescEventByLevel[4];
|
||||
|
||||
bool fetchDescriptor(Addr descAddr, uint8_t *data, int numBytes,
|
||||
Request::Flags flags, int queueIndex, Event *event,
|
||||
void fetchDescriptor(Addr desc_addr,
|
||||
DescriptorBase &descriptor, int num_bytes,
|
||||
Request::Flags flags, LookupLevel lookup_lvl, Event *event,
|
||||
void (TableWalker::*doDescriptor)());
|
||||
|
||||
Fault generateLongDescFault(ArmFault::FaultSource src);
|
||||
@@ -1183,10 +1196,18 @@ class TableWalker : public ClockedObject
|
||||
|
||||
void pendingChange();
|
||||
|
||||
/** Timing mode: saves the currState into the stateQueues */
|
||||
void stashCurrState(int queue_idx);
|
||||
|
||||
static uint8_t pageSizeNtoStatBin(uint8_t N);
|
||||
|
||||
Fault testWalk(Addr pa, Addr size, TlbEntry::DomainType domain,
|
||||
LookupLevel lookup_level, bool stage2);
|
||||
public: /* Testing */
|
||||
TlbTestInterface *test;
|
||||
|
||||
void setTestInterface(TlbTestInterface *ti);
|
||||
|
||||
Fault testWalk(const RequestPtr &walk_req, TlbEntry::DomainType domain,
|
||||
LookupLevel lookup_level);
|
||||
};
|
||||
|
||||
} // namespace ArmISA
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013, 2016, 2019-2022 Arm Limited
|
||||
* Copyright (c) 2010-2013, 2016, 2019-2022, 2024 Arm Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
@@ -84,8 +84,7 @@ class TlbTestInterface
|
||||
/**
|
||||
* Check if a page table walker access should be forced to fail.
|
||||
*
|
||||
* @param pa Physical address the walker is accessing
|
||||
* @param size Walker access size
|
||||
* @param req walk request bearing a valid phys address
|
||||
* @param va Virtual address that initiated the walk
|
||||
* @param is_secure Access from secure state
|
||||
* @param is_priv Access from a privileged mode (i.e., not EL0)
|
||||
@@ -93,7 +92,8 @@ class TlbTestInterface
|
||||
* @param domain Domain type
|
||||
* @param lookup_level Page table walker level
|
||||
*/
|
||||
virtual Fault walkCheck(Addr pa, Addr size, Addr va, bool is_secure,
|
||||
virtual Fault walkCheck(const RequestPtr &walk_req,
|
||||
Addr va, bool is_secure,
|
||||
Addr is_priv, BaseMMU::Mode mode,
|
||||
TlbEntry::DomainType domain,
|
||||
enums::ArmLookupLevel lookup_level) = 0;
|
||||
|
||||
Reference in New Issue
Block a user