arch-arm: Refactor AArch64 MMU permission check

This refactor of the MMU::checkPermissions64 method is moving
the TLB entry access permission bits (AP,XN...) checking into
new separate stage1 and stage2 helpers

Change-Id: If7d42538f5ea1ec21e918cccaf469fcb6a36d82b
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/50387
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Giacomo Travaglini
2021-09-01 15:09:05 +01:00
parent 6b2faf3723
commit fde9e46951
2 changed files with 206 additions and 143 deletions

View File

@@ -453,7 +453,6 @@ MMU::checkPermissions64(TlbEntry *te, const RequestPtr &req, Mode mode,
// Cache clean operations require read permissions to the specified VA
bool is_write = !req->isCacheClean() && mode == Write;
bool is_atomic = req->isAtomic();
[[maybe_unused]] bool is_priv = state.isPriv && !(flags & UserMode);
updateMiscReg(tc, state.curTranType, state.isStage2);
@@ -497,161 +496,27 @@ MMU::checkPermissions64(TlbEntry *te, const RequestPtr &req, Mode mode,
}
}
uint8_t ap = 0x3 & (te->ap); // 2-bit access protection field
bool grant = false;
bool wxn = state.sctlr.wxn;
uint8_t xn = te->xn;
uint8_t pxn = te->pxn;
bool r = (!is_write && !is_fetch);
bool w = is_write;
bool x = is_fetch;
if (ArmSystem::haveEL(tc, EL3) && state.isSecure &&
te->ns && state.scr.sif) {
xn = true;
}
// 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;
DPRINTF(TLBVerbose, "Checking 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 (state.isStage2) {
assert(ArmSystem::haveVirtualization(tc) && 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
uint8_t hap = 0x3 & te->hap;
grant_read = hap & 0x1;
if (is_fetch) {
// sctlr.wxn overrides the xn bit
grant = !wxn && !xn;
} else if (is_atomic) {
grant = hap;
} else if (is_write) {
grant = hap & 0x2;
} else { // is_read
grant = grant_read;
}
std::tie(grant, grant_read) = s2PermBits64(te, req, mode, tc, state,
(!is_write && !is_fetch), is_write, is_fetch);
} else {
switch (state.aarch64EL) {
case EL0:
{
grant_read = ap & 0x1;
uint8_t perm = (ap << 2) | (xn << 1) | pxn;
switch (perm) {
case 0:
case 1:
case 8:
case 9:
grant = x;
break;
case 4:
case 5:
grant = r || w || (x && !wxn);
break;
case 6:
case 7:
grant = r || w;
break;
case 12:
case 13:
grant = r || x;
break;
case 14:
case 15:
grant = r;
break;
default:
grant = false;
}
}
break;
case EL1:
{
if (checkPAN(tc, ap, req, mode, is_priv, state)) {
grant = false;
grant_read = false;
break;
}
uint8_t perm = (ap << 2) | (xn << 1) | pxn;
switch (perm) {
case 0:
case 2:
grant = r || w || (x && !wxn);
break;
case 1:
case 3:
case 4:
case 5:
case 6:
case 7:
// regions that are writeable at EL0 should not be
// executable at EL1
grant = r || w;
break;
case 8:
case 10:
case 12:
case 14:
grant = r || x;
break;
case 9:
case 11:
case 13:
case 15:
grant = r;
break;
default:
grant = false;
}
}
break;
case EL2:
if (state.hcr.e2h && checkPAN(tc, ap, req, mode, is_priv, state)) {
grant = false;
grant_read = false;
break;
}
[[fallthrough]];
case EL3:
{
uint8_t perm = (ap & 0x2) | xn;
switch (perm) {
case 0:
grant = r || w || (x && !wxn);
break;
case 1:
grant = r || w;
break;
case 2:
grant = r || x;
break;
case 3:
grant = r;
break;
default:
grant = false;
}
}
break;
}
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. "
"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);
"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>(
@@ -660,8 +525,8 @@ MMU::checkPermissions64(TlbEntry *te, const RequestPtr &req, Mode mode,
state.isStage2, ArmFault::LpaeTran);
} else {
stats.permsFaults++;
DPRINTF(TLB, "TLB Fault: Data abort on permission check. AP:%d "
"priv:%d write:%d\n", ap, is_priv, is_write);
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,
@@ -673,6 +538,192 @@ MMU::checkPermissions64(TlbEntry *te, const RequestPtr &req, Mode mode,
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::haveVirtualization(tc) && 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
uint8_t hap = te->hap & 0b11;
bool grant = false;
bool grant_read = hap & 0b1;
bool wxn = state.sctlr.wxn;
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, wxn: %d\n", hap, xn,
pxn, r, w, x, wxn);
if (x) {
// sctlr.wxn overrides the xn bit
grant = !wxn && !xn;
} else if (req->isAtomic()) {
grant = hap;
} else if (w) {
grant = hap & 0b10;
} else { // is_read
grant = grant_read;
}
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;
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;
if (ArmSystem::haveEL(tc, EL3) && state.isSecure &&
te->ns && state.scr.sif) {
xn = true;
}
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);
}
switch (state.aarch64EL) {
case EL0:
{
grant_read = ap & 0x1;
uint8_t perm = (ap << 2) | (xn << 1) | pxn;
switch (perm) {
case 0:
case 1:
case 8:
case 9:
grant = x;
break;
case 4:
case 5:
grant = r || w || (x && !wxn);
break;
case 6:
case 7:
grant = r || w;
break;
case 12:
case 13:
grant = r || x;
break;
case 14:
case 15:
grant = r;
break;
default:
grant = false;
}
}
break;
case EL1:
{
uint8_t perm = (ap << 2) | (xn << 1) | pxn;
switch (perm) {
case 0:
case 2:
grant = r || w || (x && !wxn);
break;
case 1:
case 3:
case 4:
case 5:
case 6:
case 7:
// regions that are writeable at EL0 should not be
// executable at EL1
grant = r || w;
break;
case 8:
case 10:
case 12:
case 14:
grant = r || x;
break;
case 9:
case 11:
case 13:
case 15:
grant = r;
break;
default:
grant = false;
}
}
break;
case EL2:
case EL3:
{
uint8_t perm = (ap & 0x2) | xn;
switch (perm) {
case 0:
grant = r || w || (x && !wxn);
break;
case 1:
grant = r || w;
break;
case 2:
grant = r || x;
break;
case 3:
grant = r;
break;
default:
grant = false;
}
}
break;
}
return std::make_pair(grant, grant_read);
}
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)

View File

@@ -378,9 +378,21 @@ class MMU : public BaseMMU
ThreadContext *tc, bool stage2);
Fault checkPermissions64(TlbEntry *te, const RequestPtr &req, Mode mode,
ThreadContext *tc, CachedState &state);
protected:
bool checkPAN(ThreadContext *tc, uint8_t ap, const RequestPtr &req,
Mode mode, const bool is_priv, CachedState &state);
bool faultPAN(ThreadContext *tc, uint8_t ap, const RequestPtr &req,
Mode mode, const bool is_priv, CachedState &state);
std::pair<bool, bool> s1PermBits64(
TlbEntry *te, const RequestPtr &req, Mode mode,
ThreadContext *tc, CachedState &state, bool r, bool w, bool x);
std::pair<bool, bool> s2PermBits64(
TlbEntry *te, const RequestPtr &req, Mode mode,
ThreadContext *tc, CachedState &state, bool r, bool w, bool x);
public: /* Testing */
TlbTestInterface *test;