arch-arm: Update ARMv8.1-PAN to allow unprivileged instructions.

Update the ARMv8.1-PAN implementation to allow specified unprivileged
instructions to execute even when the cpsr.pan bit is set. The
specified instructions generate memory requests with the
TLB::ArmFlags::UserMode flags bit set.

See sections D5.4.2 (About PSTATE.PAN) and G5.6.2 (About the PAN bit)
of the Arm Architecture Reference Manual for details.
https://developer.arm.com/documentation/ddi0487/latest/

Change-Id: I9e904e0154de72c2e4cc70cbc49b3c8407a3cb1d
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/47779
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Jason Lowe-Power <power.jg@gmail.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/arch/arm/tlb.cc b/src/arch/arm/tlb.cc
index e9c487e..8941f9e 100644
--- a/src/arch/arm/tlb.cc
+++ b/src/arch/arm/tlb.cc
@@ -898,7 +898,7 @@
             break;
           case EL1:
             {
-                if (checkPAN(tc, ap, req, mode)) {
+                if (checkPAN(tc, ap, req, mode, is_priv)) {
                     grant = false;
                     grant_read = false;
                     break;
@@ -938,7 +938,7 @@
             }
             break;
           case EL2:
-            if (hcr.e2h && checkPAN(tc, ap, req, mode)) {
+            if (hcr.e2h && checkPAN(tc, ap, req, mode, is_priv)) {
                 grant = false;
                 grant_read = false;
                 break;
@@ -998,7 +998,7 @@
 
 bool
 TLB::checkPAN(ThreadContext *tc, uint8_t ap, const RequestPtr &req,
-              BaseMMU::Mode mode)
+              BaseMMU::Mode mode, const bool is_priv)
 {
     // The PAN bit has no effect on:
     // 1) Instruction accesses.
@@ -1006,15 +1006,22 @@
     // 3) Address translation instructions, other than ATS1E1RP and
     // ATS1E1WP when ARMv8.2-ATS1E1 is implemented. (Unimplemented in
     // gem5)
-    // 4) Unprivileged instructions (Unimplemented in gem5)
-    AA64MMFR1 mmfr1 = tc->readMiscReg(MISCREG_ID_AA64MMFR1_EL1);
-    if (mmfr1.pan && cpsr.pan && (ap & 0x1) && mode != BaseMMU::Execute &&
-        (!req->isCacheMaintenance() ||
-            (req->getFlags() & Request::CACHE_BLOCK_ZERO))) {
+    // 4) Instructions to be treated as unprivileged, unless
+    // HCR_EL2.{E2H, TGE} == {1, 0}
+    const AA64MMFR1 mmfr1 = tc->readMiscReg(MISCREG_ID_AA64MMFR1_EL1);
+    if (mmfr1.pan && 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 && !(hcr.e2h && !hcr.tge)) {
+            // Treated as unprivileged unless HCR_EL2.{E2H, TGE} == {1, 0}
+            return false;
+        }
         return true;
-    } else {
-        return false;
     }
+
+    return false;
 }
 
 Fault
diff --git a/src/arch/arm/tlb.hh b/src/arch/arm/tlb.hh
index 3d399c6..72e8803 100644
--- a/src/arch/arm/tlb.hh
+++ b/src/arch/arm/tlb.hh
@@ -259,7 +259,7 @@
     Fault checkPermissions64(TlbEntry *te, const RequestPtr &req,
                              BaseMMU::Mode mode, ThreadContext *tc);
     bool checkPAN(ThreadContext *tc, uint8_t ap, const RequestPtr &req,
-                  BaseMMU::Mode mode);
+                  BaseMMU::Mode mode, const bool is_priv);
 
     /** Reset the entire TLB. Used for CPU switching to prevent stale
      * translations after multiple switches