arch-arm: Provide support for a multilevel-TLB in the ArmMMU

This is an initial implementation. It adapts the current MMU code
to account for extra levels of TLBs but it is still missing the
configurability we are looking for (to select the associativity
of the TLB and the replacement policy as an example)

JIRA: https://gem5.atlassian.net/browse/GEM5-790

Change-Id: I938ec38183337cd0e839bf3e3cd03594126128cd
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/48149
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
This commit is contained in:
Giacomo Travaglini
2021-07-08 14:44:36 +01:00
parent 395c6d4fa3
commit fb0929b5f9
5 changed files with 148 additions and 22 deletions

View File

@@ -1089,8 +1089,16 @@ MMU::updateMiscReg(ThreadContext *tc,
itbStage2->setVMID(state.vmid);
dtbStage2->setVMID(state.vmid);
getITBPtr()->setVMID(state.vmid);
getDTBPtr()->setVMID(state.vmid);
for (auto tlb : instruction) {
static_cast<TLB*>(tlb)->setVMID(state.vmid);
}
for (auto tlb : data) {
static_cast<TLB*>(tlb)->setVMID(state.vmid);
}
for (auto tlb : unified) {
static_cast<TLB*>(tlb)->setVMID(state.vmid);
}
miscRegContext = tc->contextId();
}
@@ -1309,6 +1317,16 @@ MMU::getTE(TlbEntry **te, const RequestPtr &req, ThreadContext *tc, Mode mode,
is_secure, tran_type, stage2 ? s2State : s1State);
}
TlbEntry*
MMU::lookup(Addr va, uint16_t asid, vmid_t vmid, bool hyp, bool secure,
bool functional, bool ignore_asn, ExceptionLevel target_el,
bool in_host, bool stage2, BaseMMU::Mode mode)
{
TLB *tlb = getTlb(mode, stage2);
return tlb->multiLookup(va, asid, vmid, hyp, secure, functional,
ignore_asn, target_el, in_host, mode);
}
Fault
MMU::getTE(TlbEntry **te, const RequestPtr &req, ThreadContext *tc, Mode mode,
Translation *translation, bool timing, bool functional,
@@ -1331,9 +1349,9 @@ MMU::getTE(TlbEntry **te, const RequestPtr &req, ThreadContext *tc, Mode mode,
vaddr = vaddr_tainted;
}
auto tlb = getTlb(mode, state.isStage2);
*te = tlb->lookup(vaddr, state.asid, state.vmid, state.isHyp, is_secure,
false, false, target_el, false, mode);
*te = lookup(vaddr, state.asid, state.vmid, state.isHyp, is_secure, false,
false, target_el, false, state.isStage2, mode);
if (*te == NULL) {
if (req->isPrefetch()) {
// if the request is a prefetch don't attempt to fill the TLB or go
@@ -1361,10 +1379,8 @@ MMU::getTE(TlbEntry **te, const RequestPtr &req, ThreadContext *tc, Mode mode,
return fault;
}
*te = tlb->lookup(vaddr, state.asid, state.vmid, state.isHyp, is_secure,
true, false, target_el, false, mode);
if (!*te)
tlb->printTlb();
*te = lookup(vaddr, state.asid, state.vmid, state.isHyp, is_secure,
true, false, target_el, false, state.isStage2, mode);
assert(*te);
}
return NoFault;

View File

@@ -269,8 +269,15 @@ class MMU : public BaseMMU
void
flushStage1(const OP &tlbi_op)
{
iflush(tlbi_op);
dflush(tlbi_op);
for (auto tlb : instruction) {
static_cast<TLB*>(tlb)->flush(tlbi_op);
}
for (auto tlb : data) {
static_cast<TLB*>(tlb)->flush(tlbi_op);
}
for (auto tlb : unified) {
static_cast<TLB*>(tlb)->flush(tlbi_op);
}
}
template <typename OP>
@@ -285,14 +292,24 @@ class MMU : public BaseMMU
void
iflush(const OP &tlbi_op)
{
getITBPtr()->flush(tlbi_op);
for (auto tlb : instruction) {
static_cast<TLB*>(tlb)->flush(tlbi_op);
}
for (auto tlb : unified) {
static_cast<TLB*>(tlb)->flush(tlbi_op);
}
}
template <typename OP>
void
dflush(const OP &tlbi_op)
{
getDTBPtr()->flush(tlbi_op);
for (auto tlb : data) {
static_cast<TLB*>(tlb)->flush(tlbi_op);
}
for (auto tlb : unified) {
static_cast<TLB*>(tlb)->flush(tlbi_op);
}
}
void
@@ -325,6 +342,24 @@ class MMU : public BaseMMU
static ExceptionLevel tranTypeEL(CPSR cpsr, ArmTranslationType type);
public:
/** Lookup an entry in the TLB
* @param vpn virtual address
* @param asn context id/address space id to use
* @param vmid The virtual machine ID used for stage 2 translation
* @param secure if the lookup is secure
* @param hyp if the lookup is done from hyp mode
* @param functional if the lookup should modify state
* @param ignore_asn if on lookup asn should be ignored
* @param target_el selecting the translation regime
* @param in_host if we are in host (EL2&0 regime)
* @param mode to differentiate between read/writes/fetches.
* @return pointer to TLB entry if it exists
*/
TlbEntry *lookup(Addr vpn, uint16_t asn, vmid_t vmid, bool hyp,
bool secure, bool functional,
bool ignore_asn, ExceptionLevel target_el,
bool in_host, bool stage2, BaseMMU::Mode mode);
Fault getTE(TlbEntry **te, const RequestPtr &req,
ThreadContext *tc, Mode mode,
Translation *translation, bool timing, bool functional,

View File

@@ -478,9 +478,9 @@ TableWalker::processWalkWrapper()
// Check if a previous walk filled this request already
// @TODO Should this always be the TLB or should we look in the stage2 TLB?
TlbEntry* te = tlb->lookup(currState->vaddr, currState->asid,
currState->vmid, currState->isHyp, currState->isSecure, true, false,
currState->el, false, BaseMMU::Read);
TlbEntry* te = mmu->lookup(currState->vaddr, currState->asid,
currState->vmid, currState->isHyp, currState->isSecure, true, false,
currState->el, false, isStage2, currState->mode);
// Check if we still need to have a walk for this request. If the requesting
// instruction has been squashed, or a previous walk has filled the TLB with
@@ -544,9 +544,9 @@ TableWalker::processWalkWrapper()
// peak at the next one
if (pendingQueue.size()) {
currState = pendingQueue.front();
te = tlb->lookup(currState->vaddr, currState->asid,
te = mmu->lookup(currState->vaddr, currState->asid,
currState->vmid, currState->isHyp, currState->isSecure, true,
false, currState->el, false, BaseMMU::Read);
false, currState->el, false, isStage2, currState->mode);
} else {
// Terminate the loop, nothing more to do
currState = NULL;
@@ -2354,8 +2354,8 @@ TableWalker::insertTableEntry(DescriptorBase &descriptor, bool long_descriptor)
descriptor.lookupLevel, static_cast<uint8_t>(descriptor.domain()),
descriptor.getRawData());
// Insert the entry into the TLB
tlb->insert(te);
// Insert the entry into the TLBs
tlb->multiInsert(te);
if (!currState->timing) {
currState->tc = NULL;
currState->req = NULL;

View File

@@ -141,6 +141,41 @@ TLB::lookup(Addr va, uint16_t asn, vmid_t vmid, bool hyp, bool secure,
return retval;
}
TlbEntry*
TLB::multiLookup(Addr va, uint16_t asid, vmid_t vmid, bool hyp, bool secure,
bool functional, bool ignore_asn, ExceptionLevel target_el,
bool in_host, BaseMMU::Mode mode)
{
TlbEntry* te = lookup(va, asid, vmid, hyp, secure, functional,
ignore_asn, target_el, in_host, mode);
if (te) {
checkPromotion(te, mode);
} else {
if (auto tlb = static_cast<TLB*>(nextLevel())) {
te = tlb->multiLookup(va, asid, vmid, hyp, secure, functional,
ignore_asn, target_el, in_host, mode);
if (te && !functional)
insert(*te);
}
}
return te;
}
void
TLB::checkPromotion(TlbEntry *entry, BaseMMU::Mode mode)
{
TypeTLB acc_type = (mode == BaseMMU::Execute) ?
TypeTLB::instruction : TypeTLB::data;
// Hitting an instruction TLB entry on a data access or
// a data TLB entry on an instruction access:
// promoting the entry to unified
if (!(entry->type & acc_type))
entry->type = TypeTLB::unified;
}
// insert a new TLB entry
void
TLB::insert(TlbEntry &entry)
@@ -162,8 +197,7 @@ TLB::insert(TlbEntry &entry)
table[size-1].nstid, table[size-1].global, table[size-1].isHyp,
table[size-1].el);
//inserting to MRU position and evicting the LRU one
// inserting to MRU position and evicting the LRU one
for (int i = size - 1; i > 0; --i)
table[i] = table[i-1];
table[0] = entry;
@@ -172,6 +206,16 @@ TLB::insert(TlbEntry &entry)
ppRefills->notify(1);
}
void
TLB::multiInsert(TlbEntry &entry)
{
insert(entry);
if (auto next_level = static_cast<TLB*>(nextLevel())) {
next_level->multiInsert(entry);
}
}
void
TLB::printTlb() const
{

View File

@@ -175,6 +175,26 @@ class TLB : public BaseTLB
bool ignore_asn, ExceptionLevel target_el,
bool in_host, BaseMMU::Mode mode);
/** Lookup an entry in the TLB and in the next levels by
* following the nextLevel pointer
*
* @param vpn virtual address
* @param asn context id/address space id to use
* @param vmid The virtual machine ID used for stage 2 translation
* @param secure if the lookup is secure
* @param hyp if the lookup is done from hyp mode
* @param functional if the lookup should modify state
* @param ignore_asn if on lookup asn should be ignored
* @param target_el selecting the translation regime
* @param in_host if we are in host (EL2&0 regime)
* @param mode to differentiate between read/writes/fetches.
* @return pointer to TLB entry if it exists
*/
TlbEntry *multiLookup(Addr vpn, uint16_t asn, vmid_t vmid, bool hyp,
bool secure, bool functional,
bool ignore_asn, ExceptionLevel target_el,
bool in_host, BaseMMU::Mode mode);
virtual ~TLB();
void takeOverFrom(BaseTLB *otlb) override;
@@ -187,8 +207,12 @@ class TLB : public BaseTLB
void setVMID(vmid_t _vmid) { vmid = _vmid; }
/** Insert a PTE in the current TLB */
void insert(TlbEntry &pte);
/** Insert a PTE in the current TLB and in the higher levels */
void multiInsert(TlbEntry &pte);
/** Reset the entire TLB. Used for CPU switching to prevent stale
* translations after multiple switches
*/
@@ -301,6 +325,13 @@ class TLB : public BaseTLB
void _flushMva(Addr mva, uint64_t asn, bool secure_lookup,
bool ignore_asn, ExceptionLevel target_el,
bool in_host, TypeTLB entry_type);
/** Check if the tlb entry passed as an argument needs to
* be "promoted" as a unified entry:
* this should happen if we are hitting an instruction TLB entry on a
* data access or a data TLB entry on an instruction access:
*/
void checkPromotion(TlbEntry *entry, BaseMMU::Mode mode);
};
} // namespace ArmISA