blob: 824974ab21ec9d14491a6f82913c72882dddd33b [file] [log] [blame]
/*
* Copyright (c) 2010-2013, 2016-2022 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.
*
* Copyright (c) 2001-2005 The Regents of The University of Michigan
* All rights reserved.
*
* 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.
*/
#include "arch/arm/mmu.hh"
#include "arch/arm/isa.hh"
#include "arch/arm/reg_abi.hh"
#include "arch/arm/stage2_lookup.hh"
#include "arch/arm/table_walker.hh"
#include "arch/arm/tlbi_op.hh"
#include "debug/TLB.hh"
#include "debug/TLBVerbose.hh"
#include "mem/packet_access.hh"
#include "sim/pseudo_inst.hh"
#include "sim/process.hh"
namespace gem5
{
using namespace ArmISA;
MMU::MMU(const ArmMMUParams &p)
: BaseMMU(p),
itbStage2(p.stage2_itb), dtbStage2(p.stage2_dtb),
itbWalker(p.itb_walker), dtbWalker(p.dtb_walker),
itbStage2Walker(p.stage2_itb_walker),
dtbStage2Walker(p.stage2_dtb_walker),
test(nullptr),
miscRegContext(0),
s1State(this, false), s2State(this, true),
_attr(0),
_release(nullptr),
_hasWalkCache(false),
stats(this)
{
// Cache system-level properties
if (FullSystem) {
ArmSystem *arm_sys = dynamic_cast<ArmSystem *>(p.sys);
assert(arm_sys);
haveLargeAsid64 = arm_sys->haveLargeAsid64();
physAddrRange = arm_sys->physAddrRange();
_release = arm_sys->releaseFS();
} else {
haveLargeAsid64 = false;
physAddrRange = 48;
_release = p.release_se;
}
m5opRange = p.sys->m5opRange();
}
void
MMU::init()
{
itbWalker->setMmu(this);
dtbWalker->setMmu(this);
itbStage2Walker->setMmu(this);
dtbStage2Walker->setMmu(this);
itbStage2->setTableWalker(itbStage2Walker);
dtbStage2->setTableWalker(dtbStage2Walker);
getITBPtr()->setTableWalker(itbWalker);
getDTBPtr()->setTableWalker(dtbWalker);
BaseMMU::init();
_hasWalkCache = checkWalkCache();
}
bool
MMU::checkWalkCache() const
{
for (auto tlb : instruction) {
if (static_cast<TLB*>(tlb)->walkCache())
return true;
}
for (auto tlb : data) {
if (static_cast<TLB*>(tlb)->walkCache())
return true;
}
for (auto tlb : unified) {
if (static_cast<TLB*>(tlb)->walkCache())
return true;
}
return false;
}
void
MMU::drainResume()
{
s1State.miscRegValid = false;
s2State.miscRegValid = false;
}
TLB *
MMU::getTlb(BaseMMU::Mode mode, bool stage2) const
{
if (mode == BaseMMU::Execute) {
if (stage2)
return itbStage2;
else
return getITBPtr();
} else {
if (stage2)
return dtbStage2;
else
return getDTBPtr();
}
}
TableWalker *
MMU::getTableWalker(BaseMMU::Mode mode, bool stage2) const
{
if (mode == BaseMMU::Execute) {
if (stage2)
return itbStage2Walker;
else
return itbWalker;
} else {
if (stage2)
return dtbStage2Walker;
else
return dtbWalker;
}
}
bool
MMU::translateFunctional(ThreadContext *tc, Addr va, Addr &pa)
{
CachedState& state = updateMiscReg(tc, NormalTran, false);
auto tlb = getTlb(BaseMMU::Read, state.directToStage2);
TlbEntry::Lookup lookup_data;
lookup_data.va = va;
lookup_data.asn = state.asid;
lookup_data.ignoreAsn = false;
lookup_data.vmid = state.vmid;
lookup_data.hyp = state.isHyp;
lookup_data.secure = state.isSecure;
lookup_data.functional = true;
lookup_data.targetEL = state.aarch64 ? state.aarch64EL : EL1;
lookup_data.inHost = false;
lookup_data.mode = BaseMMU::Read;
TlbEntry *e = tlb->multiLookup(lookup_data);
if (!e)
return false;
pa = e->pAddr(va);
return true;
}
void
MMU::invalidateMiscReg()
{
s1State.miscRegValid = false;
s1State.computeAddrTop.flush();
s2State.computeAddrTop.flush();
}
Fault
MMU::finalizePhysical(const RequestPtr &req,
ThreadContext *tc, Mode mode) const
{
const Addr paddr = req->getPaddr();
if (m5opRange.contains(paddr)) {
uint8_t func;
pseudo_inst::decodeAddrOffset(paddr - m5opRange.start(), func);
req->setLocalAccessor(
[func, mode](ThreadContext *tc, PacketPtr pkt) -> Cycles
{
uint64_t ret;
if (inAArch64(tc))
pseudo_inst::pseudoInst<RegABI64>(tc, func, ret);
else
pseudo_inst::pseudoInst<RegABI32>(tc, func, ret);
if (mode == Read)
pkt->setLE(ret);
return Cycles(1);
}
);
}
return NoFault;
}
Fault
MMU::translateSe(const RequestPtr &req, ThreadContext *tc, Mode mode,
Translation *translation, bool &delay, bool timing,
CachedState &state)
{
updateMiscReg(tc, NormalTran, state.isStage2);
Addr vaddr_tainted = req->getVaddr();
Addr vaddr = 0;
if (state.aarch64) {
vaddr = purifyTaggedAddr(vaddr_tainted, tc, state.aarch64EL,
static_cast<TCR>(state.ttbcr), mode==Execute, state);
} else {
vaddr = vaddr_tainted;
}
Request::Flags flags = req->getFlags();
bool is_fetch = (mode == Execute);
bool is_write = (mode == Write);
if (!is_fetch) {
if (state.sctlr.a || !(flags & AllowUnaligned)) {
if (vaddr & mask(flags & AlignmentMask)) {
// LPAE is always disabled in SE mode
return std::make_shared<DataAbort>(
vaddr_tainted,
TlbEntry::DomainType::NoAccess, is_write,
ArmFault::AlignmentFault, state.isStage2,
ArmFault::VmsaTran);
}
}
}
Addr paddr;
Process *p = tc->getProcessPtr();
if (!p->pTable->translate(vaddr, paddr))
return std::make_shared<GenericPageTableFault>(vaddr_tainted);
req->setPaddr(paddr);
return finalizePhysical(req, tc, mode);
}
Fault
MMU::checkPermissions(TlbEntry *te, const RequestPtr &req, Mode mode,
bool stage2)
{
return checkPermissions(te, req, mode, stage2 ? s2State : s1State);
}
Fault
MMU::checkPermissions(TlbEntry *te, const RequestPtr &req, Mode mode,
CachedState &state)
{
// a data cache maintenance instruction that operates by MVA does
// not generate a Data Abort exeception due to a Permission fault
if (req->isCacheMaintenance()) {
return NoFault;
}
Addr vaddr = req->getVaddr(); // 32-bit don't have to purify
Request::Flags flags = req->getFlags();
bool is_fetch = (mode == Execute);
bool is_write = (mode == Write);
bool is_priv = state.isPriv && !(flags & UserMode);
// Get the translation type from the actuall table entry
ArmFault::TranMethod tranMethod = te->longDescFormat ? ArmFault::LpaeTran
: ArmFault::VmsaTran;
// If this is the second stage of translation and the request is for a
// stage 1 page table walk then we need to check the HCR.PTW bit. This
// allows us to generate a fault if the request targets an area marked
// as a device or strongly ordered.
if (state.isStage2 && req->isPTWalk() && state.hcr.ptw &&
(te->mtype != TlbEntry::MemoryType::Normal)) {
return std::make_shared<DataAbort>(
vaddr, te->domain, is_write,
ArmFault::PermissionLL + te->lookupLevel,
state.isStage2, tranMethod);
}
// Generate an alignment fault for unaligned data accesses to device or
// strongly ordered memory
if (!is_fetch) {
if (te->mtype != TlbEntry::MemoryType::Normal) {
if (vaddr & mask(flags & AlignmentMask)) {
stats.alignFaults++;
return std::make_shared<DataAbort>(
vaddr, TlbEntry::DomainType::NoAccess, is_write,
ArmFault::AlignmentFault, state.isStage2,
tranMethod);
}
}
}
if (te->nonCacheable) {
// Prevent prefetching from I/O devices.
if (req->isPrefetch()) {
// Here we can safely use the fault status for the short
// desc. format in all cases
return std::make_shared<PrefetchAbort>(
vaddr, ArmFault::PrefetchUncacheable,
state.isStage2, tranMethod);
}
}
if (!te->longDescFormat) {
switch ((state.dacr >> (static_cast<uint8_t>(te->domain) * 2)) & 0x3) {
case 0:
stats.domainFaults++;
DPRINTF(TLB, "TLB Fault: Data abort on domain. DACR: %#x"
" domain: %#x write:%d\n", state.dacr,
static_cast<uint8_t>(te->domain), is_write);
if (is_fetch) {
// Use PC value instead of vaddr because vaddr might
// be aligned to cache line and should not be the
// address reported in FAR
return std::make_shared<PrefetchAbort>(
req->getPC(),
ArmFault::DomainLL + te->lookupLevel,
state.isStage2, tranMethod);
} else
return std::make_shared<DataAbort>(
vaddr, te->domain, is_write,
ArmFault::DomainLL + te->lookupLevel,
state.isStage2, tranMethod);
case 1:
// Continue with permissions check
break;
case 2:
panic("UNPRED domain\n");
case 3:
return NoFault;
}
}
// The 'ap' variable is AP[2:0] or {AP[2,1],1b'0}, i.e. always three bits
uint8_t ap = te->longDescFormat ? te->ap << 1 : te->ap;
uint8_t hap = te->hap;
if (state.sctlr.afe == 1 || te->longDescFormat)
ap |= 1;
bool abt;
bool isWritable = true;
// If this is a stage 2 access (eg for reading stage 1 page table entries)
// then don't perform the AP permissions check, we stil do the HAP check
// below.
if (state.isStage2) {
abt = false;
} else {
switch (ap) {
case 0:
DPRINTF(TLB, "Access permissions 0, checking rs:%#x\n",
(int)state.sctlr.rs);
if (!state.sctlr.xp) {
switch ((int)state.sctlr.rs) {
case 2:
abt = is_write;
break;
case 1:
abt = is_write || !is_priv;
break;
case 0:
case 3:
default:
abt = true;
break;
}
} else {
abt = true;
}
break;
case 1:
abt = !is_priv;
break;
case 2:
abt = !is_priv && is_write;
isWritable = is_priv;
break;
case 3:
abt = false;
break;
case 4:
panic("UNPRED premissions\n");
case 5:
abt = !is_priv || is_write;
isWritable = false;
break;
case 6:
case 7:
abt = is_write;
isWritable = false;
break;
default:
panic("Unknown permissions %#x\n", ap);
}
}
bool hapAbt = is_write ? !(hap & 2) : !(hap & 1);
bool xn = te->xn || (isWritable && state.sctlr.wxn) ||
(ap == 3 && state.sctlr.uwxn && is_priv);
if (is_fetch && (abt || xn ||
(te->longDescFormat && te->pxn && is_priv) ||
(state.isSecure && te->ns && state.scr.sif))) {
stats.permsFaults++;
DPRINTF(TLB, "TLB Fault: Prefetch abort on permission check. AP:%d "
"priv:%d write:%d ns:%d sif:%d sctlr.afe: %d \n",
ap, is_priv, is_write, te->ns,
state.scr.sif, state.sctlr.afe);
// Use PC value instead of vaddr because vaddr might be aligned to
// cache line and should not be the address reported in FAR
return std::make_shared<PrefetchAbort>(
req->getPC(),
ArmFault::PermissionLL + te->lookupLevel,
state.isStage2, tranMethod);
} else if (abt | hapAbt) {
stats.permsFaults++;
DPRINTF(TLB, "TLB Fault: Data abort on permission check. AP:%d priv:%d"
" write:%d\n", ap, is_priv, is_write);
return std::make_shared<DataAbort>(
vaddr, te->domain, is_write,
ArmFault::PermissionLL + te->lookupLevel,
state.isStage2 | !abt, tranMethod);
}
return NoFault;
}
Fault
MMU::checkPermissions64(TlbEntry *te, const RequestPtr &req, Mode mode,
ThreadContext *tc, bool stage2)
{
return checkPermissions64(te, req, mode, tc, stage2 ? s2State : s1State);
}
Fault
MMU::checkPermissions64(TlbEntry *te, const RequestPtr &req, Mode mode,
ThreadContext *tc, CachedState &state)
{
assert(state.aarch64);
// A data cache maintenance instruction that operates by VA does
// not generate a Permission fault unless:
// * It is a data cache invalidate (dc ivac) which requires write
// permissions to the VA, or
// * It is executed from EL0
if (req->isCacheClean() && state.aarch64EL != EL0 && !state.isStage2) {
return NoFault;
}
Addr vaddr_tainted = req->getVaddr();
Addr vaddr = purifyTaggedAddr(vaddr_tainted, tc, state.aarch64EL,
static_cast<TCR>(state.ttbcr), mode==Execute, state);
Request::Flags flags = req->getFlags();
bool is_fetch = (mode == Execute);
// Cache clean operations require read permissions to the specified VA
bool is_write = !req->isCacheClean() && mode == Write;
bool is_atomic = req->isAtomic();
updateMiscReg(tc, state.curTranType, state.isStage2);
// If this is the second stage of translation and the request is for a
// stage 1 page table walk then we need to check the HCR.PTW bit. This
// allows us to generate a fault if the request targets an area marked
// as a device or strongly ordered.
if (state.isStage2 && req->isPTWalk() && state.hcr.ptw &&
(te->mtype != TlbEntry::MemoryType::Normal)) {
return std::make_shared<DataAbort>(
vaddr_tainted, te->domain, is_write,
ArmFault::PermissionLL + te->lookupLevel,
state.isStage2, ArmFault::LpaeTran);
}
// Generate an alignment fault for unaligned accesses to device or
// strongly ordered memory
if (!is_fetch) {
if (te->mtype != TlbEntry::MemoryType::Normal) {
if (vaddr & mask(flags & AlignmentMask)) {
stats.alignFaults++;
return std::make_shared<DataAbort>(
vaddr_tainted,
TlbEntry::DomainType::NoAccess,
is_atomic ? false : is_write,
ArmFault::AlignmentFault, state.isStage2,
ArmFault::LpaeTran);
}
}
}
if (te->nonCacheable) {
// Prevent prefetching from I/O devices.
if (req->isPrefetch()) {
// Here we can safely use the fault status for the short
// desc. format in all cases
return std::make_shared<PrefetchAbort>(
vaddr_tainted,
ArmFault::PrefetchUncacheable,
state.isStage2, ArmFault::LpaeTran);
}
}
bool grant = false;
// grant_read is used for faults from an atomic instruction that
// both reads and writes from a memory location. From a ISS point
// of view they count as read if a read to that address would have
// generated the fault; they count as writes otherwise
bool grant_read = true;
if (state.isStage2) {
std::tie(grant, grant_read) = s2PermBits64(te, req, mode, tc, state,
(!is_write && !is_fetch), is_write, is_fetch);
} else {
std::tie(grant, grant_read) = s1PermBits64(te, req, mode, tc, state,
(!is_write && !is_fetch), is_write, is_fetch);
}
if (!grant) {
if (is_fetch) {
stats.permsFaults++;
DPRINTF(TLB, "TLB Fault: Prefetch abort on permission check. "
"ns:%d scr.sif:%d sctlr.afe: %d\n",
te->ns, state.scr.sif, state.sctlr.afe);
// Use PC value instead of vaddr because vaddr might be aligned to
// cache line and should not be the address reported in FAR
return std::make_shared<PrefetchAbort>(
req->getPC(),
ArmFault::PermissionLL + te->lookupLevel,
state.isStage2, ArmFault::LpaeTran);
} else {
stats.permsFaults++;
DPRINTF(TLB, "TLB Fault: Data abort on permission check."
"ns:%d", te->ns);
return std::make_shared<DataAbort>(
vaddr_tainted, te->domain,
(is_atomic && !grant_read) ? false : is_write,
ArmFault::PermissionLL + te->lookupLevel,
state.isStage2, ArmFault::LpaeTran);
}
}
return NoFault;
}
std::pair<bool, bool>
MMU::s2PermBits64(TlbEntry *te, const RequestPtr &req, Mode mode,
ThreadContext *tc, CachedState &state, bool r, bool w, bool x)
{
assert(ArmSystem::haveEL(tc, EL2) && state.aarch64EL != EL2);
// In stage 2 we use the hypervisor access permission bits.
// The following permissions are described in ARM DDI 0487A.f
// D4-1802
bool grant = false;
bool grant_read = te->hap & 0b01;
bool grant_write = te->hap & 0b10;
uint8_t xn = te->xn;
uint8_t pxn = te->pxn;
if (ArmSystem::haveEL(tc, EL3) && state.isSecure &&
te->ns && state.scr.sif) {
xn = true;
}
DPRINTF(TLBVerbose,
"Checking S2 permissions: hap:%d, xn:%d, pxn:%d, r:%d, "
"w:%d, x:%d\n", te->hap, xn, pxn, r, w, x);
if (x) {
grant = grant_read && !xn;
} else if (req->isAtomic()) {
grant = grant_read || grant_write;
} else if (w) {
grant = grant_write;
} else if (r) {
grant = grant_read;
} else {
panic("Invalid Operation\n");
}
return std::make_pair(grant, grant_read);
}
std::pair<bool, bool>
MMU::s1PermBits64(TlbEntry *te, const RequestPtr &req, Mode mode,
ThreadContext *tc, CachedState &state, bool r, bool w, bool x)
{
bool grant = false, grant_read = true, grant_write = true, grant_exec = true;
const uint8_t ap = te->ap & 0b11; // 2-bit access protection field
const bool is_priv = state.isPriv && !(req->getFlags() & UserMode);
bool wxn = state.sctlr.wxn;
uint8_t xn = te->xn;
uint8_t pxn = te->pxn;
DPRINTF(TLBVerbose, "Checking S1 permissions: ap:%d, xn:%d, pxn:%d, r:%d, "
"w:%d, x:%d, is_priv: %d, wxn: %d\n", ap, xn,
pxn, r, w, x, is_priv, wxn);
if (faultPAN(tc, ap, req, mode, is_priv, state)) {
return std::make_pair(false, false);
}
ExceptionLevel regime = !is_priv ? EL0 : state.aarch64EL;
if (hasUnprivRegime(regime, state)) {
bool pr = false;
bool pw = false;
bool ur = false;
bool uw = false;
// Apply leaf permissions
switch (ap) {
case 0b00: // Privileged access
pr = 1; pw = 1; ur = 0; uw = 0;
break;
case 0b01: // No effect
pr = 1; pw = 1; ur = 1; uw = 1;
break;
case 0b10: // Read-only, privileged access
pr = 1; pw = 0; ur = 0; uw = 0;
break;
case 0b11: // Read-only
pr = 1; pw = 0; ur = 1; uw = 0;
break;
}
// Locations writable by unprivileged cannot be executed by privileged
const bool px = !(pxn || uw);
const bool ux = !xn;
grant_read = is_priv ? pr : ur;
grant_write = is_priv ? pw : uw;
grant_exec = is_priv ? px : ux;
} else {
switch (bits(ap, 1)) {
case 0b0: // No effect
grant_read = 1; grant_write = 1;
break;
case 0b1: // Read-Only
grant_read = 1; grant_write = 0;
break;
}
grant_exec = !xn;
}
// Do not allow execution from writable location
// if wxn is set
grant_exec = grant_exec && !(wxn && grant_write);
if (ArmSystem::haveEL(tc, EL3) && state.isSecure && te->ns) {
grant_exec = grant_exec && !state.scr.sif;
}
if (x) {
grant = grant_exec;
} else if (req->isAtomic()) {
grant = grant_read && grant_write;
} else if (w) {
grant = grant_write;
} else {
grant = grant_read;
}
return std::make_pair(grant, grant_read);
}
bool
MMU::hasUnprivRegime(ExceptionLevel el, bool e2h)
{
switch (el) {
case EL0:
case EL1:
// EL1&0
return true;
case EL2:
// EL2&0 or EL2
return e2h;
case EL3:
default:
return false;
}
}
bool
MMU::hasUnprivRegime(ExceptionLevel el, CachedState &state)
{
return hasUnprivRegime(el, state.hcr.e2h);
}
bool
MMU::faultPAN(ThreadContext *tc, uint8_t ap, const RequestPtr &req, Mode mode,
const bool is_priv, CachedState &state)
{
bool exception = false;
switch (state.aarch64EL) {
case EL0:
break;
case EL1:
if (checkPAN(tc, ap, req, mode, is_priv, state)) {
exception = true;;
}
break;
case EL2:
if (state.hcr.e2h && checkPAN(tc, ap, req, mode, is_priv, state)) {
exception = true;;
}
break;
case EL3:
break;
}
return exception;
}
bool
MMU::checkPAN(ThreadContext *tc, uint8_t ap, const RequestPtr &req, Mode mode,
const bool is_priv, CachedState &state)
{
// The PAN bit has no effect on:
// 1) Instruction accesses.
// 2) Data Cache instructions other than DC ZVA
// 3) Address translation instructions, other than ATS1E1RP and
// ATS1E1WP when ARMv8.2-ATS1E1 is implemented. (Unimplemented in
// gem5)
// 4) Instructions to be treated as unprivileged, unless
// HCR_EL2.{E2H, TGE} == {1, 0}
if (HaveExt(tc, ArmExtension::FEAT_PAN) && state.cpsr.pan && (ap & 0x1) &&
mode != BaseMMU::Execute) {
if (req->isCacheMaintenance() &&
!(req->getFlags() & Request::CACHE_BLOCK_ZERO)) {
// Cache maintenance other than DC ZVA
return false;
} else if (!is_priv && !(state.hcr.e2h && !state.hcr.tge)) {
// Treated as unprivileged unless HCR_EL2.{E2H, TGE} == {1, 0}
return false;
}
return true;
}
return false;
}
Addr
MMU::purifyTaggedAddr(Addr vaddr_tainted, ThreadContext *tc, ExceptionLevel el,
TCR tcr, bool is_inst, CachedState& state)
{
const bool selbit = bits(vaddr_tainted, 55);
// Call the memoized version of computeAddrTop
const auto topbit = state.computeAddrTop(tc, selbit, is_inst, tcr, el);
return maskTaggedAddr(vaddr_tainted, tc, el, topbit);
}
Fault
MMU::translateMmuOff(ThreadContext *tc, const RequestPtr &req, Mode mode,
ArmTranslationType tran_type, Addr vaddr, bool long_desc_format,
CachedState &state)
{
bool is_fetch = (mode == Execute);
bool is_atomic = req->isAtomic();
req->setPaddr(vaddr);
// When the MMU is off the security attribute corresponds to the
// security state of the processor
if (state.isSecure)
req->setFlags(Request::SECURE);
if (state.aarch64) {
bool selbit = bits(vaddr, 55);
TCR tcr1 = tc->readMiscReg(MISCREG_TCR_EL1);
int topbit = computeAddrTop(tc, selbit, is_fetch, tcr1, currEL(tc));
int addr_sz = bits(vaddr, topbit, physAddrRange);
if (addr_sz != 0){
Fault f;
if (is_fetch)
f = std::make_shared<PrefetchAbort>(vaddr,
ArmFault::AddressSizeLL, state.isStage2,
ArmFault::LpaeTran);
else
f = std::make_shared<DataAbort>( vaddr,
TlbEntry::DomainType::NoAccess,
is_atomic ? false : mode==Write,
ArmFault::AddressSizeLL, state.isStage2,
ArmFault::LpaeTran);
return f;
}
}
// @todo: double check this (ARM ARM issue C B3.2.1)
if (long_desc_format || state.sctlr.tre == 0 || state.nmrr.ir0 == 0 ||
state.nmrr.or0 == 0 || state.prrr.tr0 != 0x2) {
if (!req->isCacheMaintenance()) {
req->setFlags(Request::UNCACHEABLE);
}
req->setFlags(Request::STRICT_ORDER);
}
// Set memory attributes
TlbEntry temp_te;
temp_te.ns = !state.isSecure;
bool dc = (HaveExt(tc, ArmExtension::FEAT_VHE) &&
state.hcr.e2h == 1 && state.hcr.tge == 1) ? 0: state.hcr.dc;
bool i_cacheability = state.sctlr.i && !state.sctlr.m;
if (state.isStage2 || !dc || state.isSecure ||
(state.isHyp && !(tran_type & S1CTran))) {
temp_te.mtype = is_fetch ? TlbEntry::MemoryType::Normal
: TlbEntry::MemoryType::StronglyOrdered;
temp_te.innerAttrs = i_cacheability? 0x2: 0x0;
temp_te.outerAttrs = i_cacheability? 0x2: 0x0;
temp_te.shareable = true;
temp_te.outerShareable = true;
} else {
temp_te.mtype = TlbEntry::MemoryType::Normal;
temp_te.innerAttrs = 0x3;
temp_te.outerAttrs = 0x3;
temp_te.shareable = false;
temp_te.outerShareable = false;
}
temp_te.setAttributes(long_desc_format);
DPRINTF(TLBVerbose, "(No MMU) setting memory attributes: shareable: "
"%d, innerAttrs: %d, outerAttrs: %d, stage2: %d\n",
temp_te.shareable, temp_te.innerAttrs, temp_te.outerAttrs,
state.isStage2);
setAttr(temp_te.attributes);
return testTranslation(req, mode, TlbEntry::DomainType::NoAccess, state);
}
Fault
MMU::translateMmuOn(ThreadContext* tc, const RequestPtr &req, Mode mode,
Translation *translation, bool &delay, bool timing,
bool functional, Addr vaddr,
ArmFault::TranMethod tranMethod, CachedState &state)
{
TlbEntry *te = NULL;
bool is_fetch = (mode == Execute);
TlbEntry mergeTe;
Request::Flags flags = req->getFlags();
Addr vaddr_tainted = req->getVaddr();
Fault fault = getResultTe(&te, req, tc, mode, translation, timing,
functional, &mergeTe, state);
// only proceed if we have a valid table entry
if (!isCompleteTranslation(te) && (fault == NoFault)) delay = true;
// If we have the table entry transfer some of the attributes to the
// request that triggered the translation
if (isCompleteTranslation(te)) {
// Set memory attributes
DPRINTF(TLBVerbose,
"Setting memory attributes: shareable: %d, innerAttrs: %d, "
"outerAttrs: %d, mtype: %d, stage2: %d\n",
te->shareable, te->innerAttrs, te->outerAttrs,
static_cast<uint8_t>(te->mtype), state.isStage2);
setAttr(te->attributes);
if (te->nonCacheable && !req->isCacheMaintenance())
req->setFlags(Request::UNCACHEABLE);
// Require requests to be ordered if the request goes to
// strongly ordered or device memory (i.e., anything other
// than normal memory requires strict order).
if (te->mtype != TlbEntry::MemoryType::Normal)
req->setFlags(Request::STRICT_ORDER);
Addr pa = te->pAddr(vaddr);
req->setPaddr(pa);
if (state.isSecure && !te->ns) {
req->setFlags(Request::SECURE);
}
if (!is_fetch && fault == NoFault &&
(vaddr & mask(flags & AlignmentMask)) &&
(te->mtype != TlbEntry::MemoryType::Normal)) {
// Unaligned accesses to Device memory should always cause an
// abort regardless of sctlr.a
stats.alignFaults++;
bool is_write = (mode == Write);
return std::make_shared<DataAbort>(
vaddr_tainted,
TlbEntry::DomainType::NoAccess, is_write,
ArmFault::AlignmentFault, state.isStage2,
tranMethod);
}
// Check for a trickbox generated address fault
if (fault == NoFault)
fault = testTranslation(req, mode, te->domain, 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;
}
}
Fault
MMU::translateFs(const RequestPtr &req, ThreadContext *tc, Mode mode,
Translation *translation, bool &delay, bool timing,
ArmTranslationType tran_type, bool functional,
CachedState &state)
{
// No such thing as a functional timing access
assert(!(timing && functional));
Addr vaddr_tainted = req->getVaddr();
Addr vaddr = 0;
if (state.aarch64) {
vaddr = purifyTaggedAddr(vaddr_tainted, tc, state.aarch64EL,
static_cast<TCR>(state.ttbcr), mode==Execute, state);
} else {
vaddr = vaddr_tainted;
}
Request::Flags flags = req->getFlags();
bool is_fetch = (mode == Execute);
bool is_write = (mode == Write);
bool long_desc_format = state.aarch64 || longDescFormatInUse(tc);
ArmFault::TranMethod tranMethod = long_desc_format ? ArmFault::LpaeTran
: ArmFault::VmsaTran;
DPRINTF(TLBVerbose,
"CPSR is priv:%d UserMode:%d secure:%d S1S2NsTran:%d\n",
state.isPriv, flags & UserMode, state.isSecure,
tran_type & S1S2NsTran);
DPRINTF(TLB, "translateFs addr %#x, mode %d, st2 %d, scr %#x sctlr %#x "
"flags %#lx tranType 0x%x\n", vaddr_tainted, mode,
state.isStage2, state.scr, state.sctlr, flags, tran_type);
if (!state.isStage2) {
if ((req->isInstFetch() && (!state.sctlr.i)) ||
((!req->isInstFetch()) && (!state.sctlr.c))){
if (!req->isCacheMaintenance()) {
req->setFlags(Request::UNCACHEABLE);
}
req->setFlags(Request::STRICT_ORDER);
}
}
if (!is_fetch) {
if (state.sctlr.a || !(flags & AllowUnaligned)) {
if (vaddr & mask(flags & AlignmentMask)) {
stats.alignFaults++;
return std::make_shared<DataAbort>(
vaddr_tainted,
TlbEntry::DomainType::NoAccess, is_write,
ArmFault::AlignmentFault, state.isStage2,
tranMethod);
}
}
}
bool vm = state.hcr.vm;
if (HaveExt(tc, ArmExtension::FEAT_VHE) &&
state.hcr.e2h == 1 && state.hcr.tge == 1)
vm = 0;
else if (state.hcr.dc == 1)
vm = 1;
Fault fault = NoFault;
// If guest MMU is off or hcr.vm=0 go straight to stage2
if ((state.isStage2 && !vm) || (!state.isStage2 && !state.sctlr.m)) {
fault = translateMmuOff(tc, req, mode, tran_type, vaddr,
long_desc_format, state);
} else {
DPRINTF(TLBVerbose, "Translating %s=%#x context=%d\n",
state.isStage2 ? "IPA" : "VA", vaddr_tainted, state.asid);
// Translation enabled
fault = translateMmuOn(tc, req, mode, translation, delay, timing,
functional, vaddr, tranMethod, state);
}
// Check for Debug Exceptions
SelfDebug *sd = ArmISA::ISA::getSelfDebug(tc);
if (sd->enabled() && fault == NoFault) {
fault = sd->testDebug(tc, req, mode);
}
return fault;
}
Fault
MMU::translateAtomic(const RequestPtr &req, ThreadContext *tc, Mode mode,
ArmTranslationType tran_type)
{
return translateAtomic(req, tc, mode, tran_type, false);
}
Fault
MMU::translateAtomic(const RequestPtr &req, ThreadContext *tc, Mode mode,
ArmTranslationType tran_type, bool stage2)
{
auto& state = updateMiscReg(tc, tran_type, stage2);
bool delay = false;
Fault fault;
if (FullSystem)
fault = translateFs(req, tc, mode, NULL, delay, false,
tran_type, false, state);
else
fault = translateSe(req, tc, mode, NULL, delay, false, state);
assert(!delay);
return fault;
}
Fault
MMU::translateFunctional(const RequestPtr &req, ThreadContext *tc, Mode mode)
{
return translateFunctional(req, tc, mode, NormalTran, false);
}
Fault
MMU::translateFunctional(const RequestPtr &req, ThreadContext *tc, Mode mode,
ArmTranslationType tran_type)
{
return translateFunctional(req, tc, mode, tran_type, false);
}
Fault
MMU::translateFunctional(const RequestPtr &req, ThreadContext *tc, Mode mode,
ArmTranslationType tran_type, bool stage2)
{
auto& state = updateMiscReg(tc, tran_type, stage2);
bool delay = false;
Fault fault;
if (FullSystem)
fault = translateFs(req, tc, mode, NULL, delay, false,
tran_type, true, state);
else
fault = translateSe(req, tc, mode, NULL, delay, false, state);
assert(!delay);
return fault;
}
void
MMU::translateTiming(const RequestPtr &req, ThreadContext *tc,
Translation *translation, Mode mode, ArmTranslationType tran_type,
bool stage2)
{
auto& state = updateMiscReg(tc, tran_type, stage2);
assert(translation);
translateComplete(req, tc, translation, mode, tran_type,
stage2, state);
}
Fault
MMU::translateComplete(const RequestPtr &req, ThreadContext *tc,
Translation *translation, Mode mode, ArmTranslationType tran_type,
bool call_from_s2)
{
return translateComplete(req, tc, translation, mode, tran_type,
call_from_s2, s1State);
}
Fault
MMU::translateComplete(const RequestPtr &req, ThreadContext *tc,
Translation *translation, Mode mode, ArmTranslationType tran_type,
bool call_from_s2, CachedState &state)
{
bool delay = false;
Fault fault;
if (FullSystem)
fault = translateFs(req, tc, mode, translation, delay, true, tran_type,
false, state);
else
fault = translateSe(req, tc, mode, translation, delay, true, state);
DPRINTF(TLBVerbose, "Translation returning delay=%d fault=%d\n", delay,
fault != NoFault);
// If we have a translation, and we're not in the middle of doing a stage
// 2 translation tell the translation that we've either finished or its
// going to take a while. By not doing this when we're in the middle of a
// stage 2 translation we prevent marking the translation as delayed twice,
// one when the translation starts and again when the stage 1 translation
// completes.
if (translation && (call_from_s2 || !state.stage2Req || req->hasPaddr() ||
fault != NoFault)) {
if (!delay)
translation->finish(fault, req, tc, mode);
else
translation->markDelayed();
}
return fault;
}
vmid_t
MMU::CachedState::getVMID(ThreadContext *tc) const
{
AA64MMFR1 mmfr1 = tc->readMiscReg(MISCREG_ID_AA64MMFR1_EL1);
VTCR_t vtcr = tc->readMiscReg(MISCREG_VTCR_EL2);
vmid_t vmid = 0;
switch (mmfr1.vmidbits) {
case 0b0000:
// 8 bits
vmid = bits(tc->readMiscReg(MISCREG_VTTBR_EL2), 55, 48);
break;
case 0b0010:
if (vtcr.vs && ELIs64(tc, EL2)) {
// 16 bits
vmid = bits(tc->readMiscReg(MISCREG_VTTBR_EL2), 63, 48);
} else {
// 8 bits
vmid = bits(tc->readMiscReg(MISCREG_VTTBR_EL2), 55, 48);
}
break;
default:
panic("Reserved ID_AA64MMFR1_EL1.VMIDBits value: %#x",
mmfr1.vmidbits);
}
return vmid;
}
MMU::CachedState&
MMU::updateMiscReg(ThreadContext *tc,
ArmTranslationType tran_type, bool stage2)
{
// check if the regs have changed, or the translation mode is different.
// NOTE: the tran type doesn't affect stage 2 TLB's as they only handle
// one type of translation anyway
auto& state = stage2 ? s2State : s1State;
if (state.miscRegValid && miscRegContext == tc->contextId() &&
((tran_type == state.curTranType) || stage2)) {
} else {
DPRINTF(TLBVerbose, "TLB variables changed!\n");
state.updateMiscReg(tc, tran_type);
itbStage2->setVMID(state.vmid);
dtbStage2->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();
}
if (state.directToStage2) {
s2State.updateMiscReg(tc, tran_type);
return s2State;
} else {
return state;
}
}
void
MMU::CachedState::updateMiscReg(ThreadContext *tc,
ArmTranslationType tran_type)
{
cpsr = tc->readMiscReg(MISCREG_CPSR);
// Dependencies: SCR/SCR_EL3, CPSR
isSecure = ArmISA::isSecure(tc) &&
!(tran_type & HypMode) && !(tran_type & S1S2NsTran);
aarch64EL = tranTypeEL(cpsr, tran_type);
aarch64 = isStage2 ?
ELIs64(tc, EL2) :
ELIs64(tc, aarch64EL == EL0 ? EL1 : aarch64EL);
hcr = tc->readMiscReg(MISCREG_HCR_EL2);
if (aarch64) { // AArch64
// determine EL we need to translate in
switch (aarch64EL) {
case EL0:
if (HaveExt(tc, ArmExtension::FEAT_VHE) &&
hcr.tge == 1 && hcr.e2h == 1) {
// VHE code for EL2&0 regime
sctlr = tc->readMiscReg(MISCREG_SCTLR_EL2);
ttbcr = tc->readMiscReg(MISCREG_TCR_EL2);
uint64_t ttbr_asid = ttbcr.a1 ?
tc->readMiscReg(MISCREG_TTBR1_EL2) :
tc->readMiscReg(MISCREG_TTBR0_EL2);
asid = bits(ttbr_asid,
(mmu->haveLargeAsid64 && ttbcr.as) ? 63 : 55, 48);
} else {
sctlr = tc->readMiscReg(MISCREG_SCTLR_EL1);
ttbcr = tc->readMiscReg(MISCREG_TCR_EL1);
uint64_t ttbr_asid = ttbcr.a1 ?
tc->readMiscReg(MISCREG_TTBR1_EL1) :
tc->readMiscReg(MISCREG_TTBR0_EL1);
asid = bits(ttbr_asid,
(mmu->haveLargeAsid64 && ttbcr.as) ? 63 : 55, 48);
}
break;
case EL1:
{
sctlr = tc->readMiscReg(MISCREG_SCTLR_EL1);
ttbcr = tc->readMiscReg(MISCREG_TCR_EL1);
uint64_t ttbr_asid = ttbcr.a1 ?
tc->readMiscReg(MISCREG_TTBR1_EL1) :
tc->readMiscReg(MISCREG_TTBR0_EL1);
asid = bits(ttbr_asid,
(mmu->haveLargeAsid64 && ttbcr.as) ? 63 : 55, 48);
}
break;
case EL2:
sctlr = tc->readMiscReg(MISCREG_SCTLR_EL2);
ttbcr = tc->readMiscReg(MISCREG_TCR_EL2);
if (hcr.e2h == 1) {
// VHE code for EL2&0 regime
uint64_t ttbr_asid = ttbcr.a1 ?
tc->readMiscReg(MISCREG_TTBR1_EL2) :
tc->readMiscReg(MISCREG_TTBR0_EL2);
asid = bits(ttbr_asid,
(mmu->haveLargeAsid64 && ttbcr.as) ? 63 : 55, 48);
} else {
asid = -1;
}
break;
case EL3:
sctlr = tc->readMiscReg(MISCREG_SCTLR_EL3);
ttbcr = tc->readMiscReg(MISCREG_TCR_EL3);
asid = -1;
break;
}
scr = tc->readMiscReg(MISCREG_SCR_EL3);
isPriv = aarch64EL != EL0;
if (mmu->release()->has(ArmExtension::VIRTUALIZATION)) {
vmid = getVMID(tc);
isHyp = aarch64EL == EL2;
isHyp |= tran_type & HypMode;
isHyp &= (tran_type & S1S2NsTran) == 0;
isHyp &= (tran_type & S1CTran) == 0;
bool vm = hcr.vm;
if (HaveExt(tc, ArmExtension::FEAT_VHE) &&
hcr.e2h == 1 && hcr.tge ==1) {
vm = 0;
}
if (hcr.e2h == 1 && (aarch64EL == EL2
|| (hcr.tge ==1 && aarch64EL == EL0))) {
isHyp = true;
directToStage2 = false;
stage2Req = false;
stage2DescReq = false;
} else {
// Work out if we should skip the first stage of translation and go
// directly to stage 2. This value is cached so we don't have to
// compute it for every translation.
bool sec = !isSecure || (isSecure && IsSecureEL2Enabled(tc));
stage2Req = isStage2 ||
(vm && !isHyp && sec &&
!(tran_type & S1CTran) && (aarch64EL < EL2) &&
!(tran_type & S1E1Tran)); // <--- FIX THIS HACK
stage2DescReq = isStage2 || (vm && !isHyp && sec &&
(aarch64EL < EL2));
directToStage2 = !isStage2 && stage2Req && !sctlr.m;
}
} else {
vmid = 0;
isHyp = false;
directToStage2 = false;
stage2Req = false;
stage2DescReq = false;
}
} else { // AArch32
sctlr = tc->readMiscReg(snsBankedIndex(MISCREG_SCTLR, tc,
!isSecure));
ttbcr = tc->readMiscReg(snsBankedIndex(MISCREG_TTBCR, tc,
!isSecure));
scr = tc->readMiscReg(MISCREG_SCR_EL3);
isPriv = cpsr.mode != MODE_USER;
if (longDescFormatInUse(tc)) {
uint64_t ttbr_asid = tc->readMiscReg(
snsBankedIndex(ttbcr.a1 ? MISCREG_TTBR1 :
MISCREG_TTBR0,
tc, !isSecure));
asid = bits(ttbr_asid, 55, 48);
} else { // Short-descriptor translation table format in use
CONTEXTIDR context_id = tc->readMiscReg(snsBankedIndex(
MISCREG_CONTEXTIDR, tc,!isSecure));
asid = context_id.asid;
}
prrr = tc->readMiscReg(snsBankedIndex(MISCREG_PRRR, tc,
!isSecure));
nmrr = tc->readMiscReg(snsBankedIndex(MISCREG_NMRR, tc,
!isSecure));
dacr = tc->readMiscReg(snsBankedIndex(MISCREG_DACR, tc,
!isSecure));
hcr = tc->readMiscReg(MISCREG_HCR_EL2);
if (mmu->release()->has(ArmExtension::VIRTUALIZATION)) {
vmid = bits(tc->readMiscReg(MISCREG_VTTBR), 55, 48);
isHyp = cpsr.mode == MODE_HYP;
isHyp |= tran_type & HypMode;
isHyp &= (tran_type & S1S2NsTran) == 0;
isHyp &= (tran_type & S1CTran) == 0;
if (isHyp) {
sctlr = tc->readMiscReg(MISCREG_HSCTLR);
}
// Work out if we should skip the first stage of translation and go
// directly to stage 2. This value is cached so we don't have to
// compute it for every translation.
bool sec = !isSecure || (isSecure && IsSecureEL2Enabled(tc));
stage2Req = hcr.vm && !isStage2 && !isHyp && sec &&
!(tran_type & S1CTran);
stage2DescReq = hcr.vm && !isStage2 && !isHyp && sec;
directToStage2 = stage2Req && !sctlr.m;
} else {
vmid = 0;
stage2Req = false;
isHyp = false;
directToStage2 = false;
stage2DescReq = false;
}
}
miscRegValid = true;
curTranType = tran_type;
}
ExceptionLevel
MMU::tranTypeEL(CPSR cpsr, ArmTranslationType type)
{
switch (type) {
case S1E0Tran:
case S12E0Tran:
return EL0;
case S1E1Tran:
case S12E1Tran:
return EL1;
case S1E2Tran:
return EL2;
case S1E3Tran:
return EL3;
case NormalTran:
case S1CTran:
case S1S2NsTran:
case HypMode:
return currEL(cpsr);
default:
panic("Unknown translation mode!\n");
}
}
Fault
MMU::getTE(TlbEntry **te, const RequestPtr &req, ThreadContext *tc, Mode mode,
Translation *translation, bool timing, bool functional,
bool is_secure, ArmTranslationType tran_type,
bool stage2)
{
return getTE(te, req, tc, mode, translation, timing, functional,
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);
TlbEntry::Lookup lookup_data;
lookup_data.va = va;
lookup_data.asn = asid;
lookup_data.ignoreAsn = ignore_asn;
lookup_data.vmid = vmid;
lookup_data.hyp = hyp;
lookup_data.secure = secure;
lookup_data.functional = functional;
lookup_data.targetEL = target_el;
lookup_data.inHost = in_host;
lookup_data.mode = mode;
return tlb->multiLookup(lookup_data);
}
Fault
MMU::getTE(TlbEntry **te, const RequestPtr &req, ThreadContext *tc, Mode mode,
Translation *translation, bool timing, bool functional,
bool is_secure, ArmTranslationType tran_type,
CachedState& state)
{
// In a 2-stage system, the IPA->PA translation can be started via this
// call so make sure the miscRegs are correct.
if (state.isStage2) {
updateMiscReg(tc, tran_type, true);
}
Addr vaddr_tainted = req->getVaddr();
Addr vaddr = 0;
ExceptionLevel target_el = state.aarch64 ? state.aarch64EL : EL1;
if (state.aarch64) {
vaddr = purifyTaggedAddr(vaddr_tainted, tc, target_el,
static_cast<TCR>(state.ttbcr), mode==Execute, state);
} else {
vaddr = vaddr_tainted;
}
*te = lookup(vaddr, state.asid, state.vmid, state.isHyp, is_secure, false,
false, target_el, false, state.isStage2, mode);
if (!isCompleteTranslation(*te)) {
if (req->isPrefetch()) {
// if the request is a prefetch don't attempt to fill the TLB or go
// any further with the memory access (here we can safely use the
// fault status for the short desc. format in all cases)
stats.prefetchFaults++;
return std::make_shared<PrefetchAbort>(
vaddr_tainted, ArmFault::PrefetchTLBMiss, state.isStage2);
}
// 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:%d)\n",
vaddr_tainted, state.asid, state.vmid);
Fault fault;
fault = getTableWalker(mode, state.isStage2)->walk(
req, tc, state.asid, state.vmid, state.isHyp, mode,
translation, timing, functional, is_secure,
tran_type, state.stage2DescReq, *te);
// for timing mode, return and wait for table walk,
if (timing || fault != NoFault) {
return fault;
}
*te = lookup(vaddr, state.asid, state.vmid, state.isHyp, is_secure,
true, false, target_el, false, state.isStage2, mode);
assert(*te);
}
return NoFault;
}
Fault
MMU::getResultTe(TlbEntry **te, const RequestPtr &req,
ThreadContext *tc, Mode mode,
Translation *translation, bool timing, bool functional,
TlbEntry *mergeTe, CachedState &state)
{
Fault fault;
if (state.isStage2) {
// We are already in the stage 2 TLB. Grab the table entry for stage
// 2 only. We are here because stage 1 translation is disabled.
TlbEntry *s2_te = nullptr;
// Get the stage 2 table entry
fault = getTE(&s2_te, req, tc, mode, translation, timing, functional,
state.isSecure, state.curTranType, state);
// Check permissions of stage 2
if (isCompleteTranslation(s2_te) && (fault == NoFault)) {
if (state.aarch64)
fault = checkPermissions64(s2_te, req, mode, tc, state);
else
fault = checkPermissions(s2_te, req, mode, state);
}
*te = s2_te;
return fault;
}
TlbEntry *s1_te = nullptr;
Addr vaddr_tainted = req->getVaddr();
// Get the stage 1 table entry
fault = getTE(&s1_te, req, tc, mode, translation, timing, functional,
state.isSecure, state.curTranType, state);
// only proceed if we have a valid table entry
if (isCompleteTranslation(s1_te) && (fault == NoFault)) {
// Check stage 1 permissions before checking stage 2
if (state.aarch64)
fault = checkPermissions64(s1_te, req, mode, tc, state);
else
fault = checkPermissions(s1_te, req, mode, state);
if (state.stage2Req & (fault == NoFault)) {
Stage2LookUp *s2_lookup = new Stage2LookUp(this, *s1_te,
req, translation, mode, timing, functional, state.isSecure,
state.curTranType);
fault = s2_lookup->getTe(tc, mergeTe);
if (s2_lookup->isComplete()) {
*te = mergeTe;
// We've finished with the lookup so delete it
delete s2_lookup;
} else {
// The lookup hasn't completed, so we can't delete it now. We
// get round this by asking the object to self delete when the
// translation is complete.
s2_lookup->setSelfDelete();
}
} else {
// This case deals with an S1 hit (or bypass), followed by
// an S2 hit-but-perms issue
if (state.isStage2) {
DPRINTF(TLBVerbose, "s2TLB: reqVa %#x, reqPa %#x, fault %p\n",
vaddr_tainted, req->hasPaddr() ? req->getPaddr() : ~0,
fault);
if (fault != NoFault) {
auto arm_fault = reinterpret_cast<ArmFault*>(fault.get());
arm_fault->annotate(ArmFault::S1PTW, false);
arm_fault->annotate(ArmFault::OVA, vaddr_tainted);
}
}
*te = s1_te;
}
}
return fault;
}
bool
MMU::isCompleteTranslation(TlbEntry *entry) const
{
return entry && !entry->partial;
}
void
MMU::takeOverFrom(BaseMMU *old_mmu)
{
BaseMMU::takeOverFrom(old_mmu);
auto *ommu = dynamic_cast<MMU*>(old_mmu);
assert(ommu);
_attr = ommu->_attr;
s1State = ommu->s1State;
s2State = ommu->s2State;
}
void
MMU::setTestInterface(SimObject *_ti)
{
if (!_ti) {
test = nullptr;
} else {
TlbTestInterface *ti(dynamic_cast<TlbTestInterface *>(_ti));
fatal_if(!ti, "%s is not a valid ARM TLB tester\n", _ti->name());
test = ti;
}
}
Fault
MMU::testTranslation(const RequestPtr &req, Mode mode,
TlbEntry::DomainType domain, CachedState &state)
{
if (!test || !req->hasSize() || req->getSize() == 0 ||
req->isCacheMaintenance()) {
return NoFault;
} else {
return test->translationCheck(req, state.isPriv, mode, domain);
}
}
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(),
"Number of MMU faults due to alignment restrictions"),
ADD_STAT(prefetchFaults, statistics::units::Count::get(),
"Number of MMU faults due to prefetch"),
ADD_STAT(domainFaults, statistics::units::Count::get(),
"Number of MMU faults due to domain restrictions"),
ADD_STAT(permsFaults, statistics::units::Count::get(),
"Number of MMU faults due to permissions restrictions")
{
}
} // namespace gem5