arch-arm: Revamp of AArch64 S1 access permission logic

This patch is revamping/simplifying the access permission logic in the
ArmMMU (ArmMMU::s1PermBits64) by matching more closely the Arm
architecture reference manual pseudocode.

It also fixes VHE access permission: previous version was not
considering the EL2&0 translation regime.
Now EL2&0 is handled correctly through the new hasUnprivRegime method

Change-Id: I2689738f36a35c35cc4f2ef8af68ee2a3eef65e8
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/60969
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Giacomo Travaglini
2022-06-28 21:11:05 +01:00
parent c1b709e46d
commit ea9620922c
2 changed files with 74 additions and 94 deletions

View File

@@ -619,7 +619,7 @@ 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;
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);
@@ -628,11 +628,6 @@ MMU::s1PermBits64(TlbEntry *te, const RequestPtr &req, Mode mode,
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);
@@ -642,101 +637,84 @@ MMU::s1PermBits64(TlbEntry *te, const RequestPtr &req, Mode mode,
}
ExceptionLevel regime = !is_priv ? EL0 : state.aarch64EL;
switch (regime) {
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;
}
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;
}
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;
}
// 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;
}
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;
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, CachedState &state)
{
switch (el) {
case EL0:
case EL1:
// EL1&0
return true;
case EL2:
// EL2&0 or EL2
return state.hcr.e2h;
case EL3:
default:
return false;
}
}
bool
MMU::faultPAN(ThreadContext *tc, uint8_t ap, const RequestPtr &req, Mode mode,
const bool is_priv, CachedState &state)

View File

@@ -446,6 +446,8 @@ class MMU : public BaseMMU
bool faultPAN(ThreadContext *tc, uint8_t ap, const RequestPtr &req,
Mode mode, const bool is_priv, CachedState &state);
bool hasUnprivRegime(ExceptionLevel el, CachedState &state);
std::pair<bool, bool> s1PermBits64(
TlbEntry *te, const RequestPtr &req, Mode mode,
ThreadContext *tc, CachedState &state, bool r, bool w, bool x);