arm: add stage2 translation support
authorDylan Johnson <Dylan.Johnson@ARM.com>
Tue, 2 Aug 2016 09:38:02 +0000 (10:38 +0100)
committerDylan Johnson <Dylan.Johnson@ARM.com>
Tue, 2 Aug 2016 09:38:02 +0000 (10:38 +0100)
Change-Id: I8f7c09c7ec3a97149ebebf4b21471b244e6cecc1

src/arch/arm/isa.cc
src/arch/arm/miscregs.hh
src/arch/arm/table_walker.cc
src/arch/arm/tlb.cc

index 2cf67fff71d574801309bf3ff875ea0948820393..2300b925ed8f5f2ad71d6b7c3902554bc55c9a17 100644 (file)
@@ -1621,6 +1621,7 @@ ISA::setMiscReg(int misc_reg, const MiscReg &val, ThreadContext *tc)
           case MISCREG_DACR:
           case MISCREG_VTTBR:
           case MISCREG_SCR_EL3:
+          case MISCREG_HCR_EL2:
           case MISCREG_TCR_EL1:
           case MISCREG_TCR_EL2:
           case MISCREG_TCR_EL3:
index 84f0fe8d1c622e44de16b0afb1fde8197cd8422d..f92cc711575f383fc39a29fc2ebb344a7cf214cc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2015 ARM Limited
+ * Copyright (c) 2010-2016 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -1735,10 +1735,12 @@ namespace ArmISA
     BitUnion32(VTCR_t)
         Bitfield<3, 0> t0sz;
         Bitfield<4> s;
+        Bitfield<5, 0> t0sz64;
         Bitfield<7, 6> sl0;
         Bitfield<9, 8> irgn0;
         Bitfield<11, 10> orgn0;
         Bitfield<13, 12> sh0;
+        Bitfield<15, 14> tg0;
     EndBitUnion(VTCR_t)
 
     BitUnion32(PRRR)
index 1f0bdeb8e9cd4b68bd64654d918c3aa8f062af38..4eb57c59afe7f76a8380b967c5da39210d64ef6d 100644 (file)
@@ -249,7 +249,10 @@ TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint16_t _asid,
         currState->vaddr = currState->vaddr_tainted;
 
     if (currState->aarch64) {
-        switch (currState->el) {
+        if (isStage2) {
+            currState->sctlr = currState->tc->readMiscReg(MISCREG_SCTLR_EL1);
+            currState->vtcr = currState->tc->readMiscReg(MISCREG_VTCR_EL2);
+        } else switch (currState->el) {
           case EL0:
           case EL1:
             currState->sctlr = currState->tc->readMiscReg(MISCREG_SCTLR_EL1);
@@ -269,6 +272,7 @@ TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint16_t _asid,
             panic("Invalid exception level");
             break;
         }
+        currState->hcr = currState->tc->readMiscReg(MISCREG_HCR_EL2);
     } else {
         currState->sctlr = currState->tc->readMiscReg(flattenMiscRegNsBanked(
             MISCREG_SCTLR, currState->tc, !currState->isSecure));
@@ -289,9 +293,8 @@ TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint16_t _asid,
     // hyp mode, the second stage MMU is enabled, and this table walker
     // instance is the first stage.
     currState->doingStage2 = false;
-    // @todo: for now disable this in AArch64 (HCR is not set)
-    currState->stage2Req = !currState->aarch64 && currState->hcr.vm &&
-                           !isStage2 && !currState->isSecure && !currState->isHyp;
+    currState->stage2Req = currState->hcr.vm && !isStage2 &&
+                           !currState->isSecure && !currState->isHyp;
 
     bool long_desc_format = currState->aarch64 || _isHyp || isStage2 ||
                             longDescFormatInUse(currState->tc);
@@ -743,10 +746,32 @@ TableWalker::processWalkAArch64()
     int tsz = 0, ps = 0;
     GrainSize tg = Grain4KB; // grain size computed from tg* field
     bool fault = false;
+
+    LookupLevel start_lookup_level = MAX_LOOKUP_LEVELS;
+
     switch (currState->el) {
       case EL0:
       case EL1:
-        switch (bits(currState->vaddr, 63,48)) {
+        if (isStage2) {
+            DPRINTF(TLB, " - Selecting VTTBR0 (AArch64 stage 2)\n");
+            ttbr = currState->tc->readMiscReg(MISCREG_VTTBR_EL2);
+            tsz = 64 - currState->vtcr.t0sz64;
+            tg = GrainMapDefault[currState->vtcr.tg0];
+            // ARM DDI 0487A.f D7-2148
+            // The starting level of stage 2 translation depends on
+            // VTCR_EL2.SL0 and VTCR_EL2.TG0
+            LookupLevel __ = MAX_LOOKUP_LEVELS; // invalid level
+            uint8_t sl_tg = (currState->vtcr.sl0 << 2) | currState->vtcr.tg0;
+            static const LookupLevel SLL[] = {
+                L2, L3, L3, __, // sl0 == 0
+                L1, L2, L2, __, // sl0 == 1, etc.
+                L0, L1, L1, __,
+                __, __, __, __
+            };
+            start_lookup_level = SLL[sl_tg];
+            panic_if(start_lookup_level == MAX_LOOKUP_LEVELS,
+                     "Cannot discern lookup level from vtcr.{sl0,tg0}");
+        } else switch (bits(currState->vaddr, 63,48)) {
           case 0:
             DPRINTF(TLB, " - Selecting TTBR0 (AArch64)\n");
             ttbr = currState->tc->readMiscReg(MISCREG_TTBR0_EL1);
@@ -824,16 +849,13 @@ TableWalker::processWalkAArch64()
         tg = Grain4KB;
     }
 
-    int stride = tg - 3;
-    LookupLevel start_lookup_level = MAX_LOOKUP_LEVELS;
-
     // Determine starting lookup level
     // See aarch64/translation/walk in Appendix G: ARMv8 Pseudocode Library
     // in ARM DDI 0487A.  These table values correspond to the cascading tests
     // to compute the lookup level and are of the form
     // (grain_size + N*stride), for N = {1, 2, 3}.
     // A value of 64 will never succeed and a value of 0 will always succeed.
-    {
+    if (start_lookup_level == MAX_LOOKUP_LEVELS) {
         struct GrainMap {
             GrainSize grain_size;
             unsigned lookup_level_cutoff[MAX_LOOKUP_LEVELS];
@@ -864,6 +886,8 @@ TableWalker::processWalkAArch64()
                  "Table walker couldn't find lookup level\n");
     }
 
+    int stride = tg - 3;
+
     // Determine table base address
     int base_addr_lo = 3 + tsz - stride * (3 - start_lookup_level) - tg;
     Addr base_addr = mbits(ttbr, 47, base_addr_lo);
@@ -969,10 +993,9 @@ TableWalker::processWalkAArch64()
         stateQueues[start_lookup_level].push_back(currState);
         currState = NULL;
     } else if (!currState->functional) {
-        port->dmaAction(MemCmd::ReadReq, desc_addr, sizeof(uint64_t),
-                       NULL, (uint8_t*) &currState->longDesc.data,
-                       currState->tc->getCpuPtr()->clockPeriod(), flag);
-        doLongDescriptor();
+        fetchDescriptor(desc_addr, (uint8_t*)&currState->longDesc.data,
+                        sizeof(uint64_t), flag, -1, NULL,
+                        &TableWalker::doLongDescriptor);
         f = currState->fault;
     } else {
         RequestPtr req = new Request(desc_addr, sizeof(uint64_t), flag,
@@ -1913,8 +1936,12 @@ TableWalker::fetchDescriptor(Addr descAddr, uint8_t *data, int numBytes,
 {
     bool isTiming = currState->timing;
 
-    // do the requests for the page table descriptors have to go through the
-    // second stage MMU
+    DPRINTF(TLBVerbose, "Fetching descriptor at address: 0x%x stage2Req: %d\n",
+            descAddr, currState->stage2Req);
+
+    // If this translation has a stage 2 then we know descAddr is an IPA and
+    // needs to be translated before we can access the page table. Do that
+    // check here.
     if (currState->stage2Req) {
         Fault fault;
         flags = flags | TLB::MustBeOne;
index a499900e0a671c00d97a06b5318bb64bad8bea4d..864f0c28c395e0b9f065211eb5360ac32d0f4433 100644 (file)
@@ -811,7 +811,19 @@ TLB::checkPermissions64(TlbEntry *te, RequestPtr req, Mode mode,
                         "w:%d, x:%d\n", ap, xn, pxn, r, w, x);
 
     if (isStage2) {
-        panic("Virtualization in AArch64 state is not supported yet");
+        assert(ArmSystem::haveVirtualization(tc) && 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;
+        if (is_fetch) {
+            // sctlr.wxn overrides the xn bit
+            grant = !sctlr.wxn && !xn;
+        } else if (is_write) {
+            grant = hap & 0x2;
+        } else { // is_read
+            grant = hap & 0x1;
+        }
     } else {
         switch (aarch64EL) {
           case EL0:
@@ -1233,14 +1245,27 @@ TLB::updateMiscReg(ThreadContext *tc, ArmTranslationType tranType)
             asid = -1;
             break;
         }
+        hcr = tc->readMiscReg(MISCREG_HCR_EL2);
         scr = tc->readMiscReg(MISCREG_SCR_EL3);
         isPriv = aarch64EL != EL0;
-        // @todo: modify this behaviour to support Virtualization in
-        // AArch64
-        vmid           = 0;
-        isHyp          = false;
-        directToStage2 = false;
-        stage2Req      = false;
+        if (haveVirtualization) {
+            vmid           = bits(tc->readMiscReg(MISCREG_VTTBR_EL2), 55, 48);
+            isHyp  =  tranType & HypMode;
+            isHyp &= (tranType & S1S2NsTran) == 0;
+            isHyp &= (tranType & S1CTran)    == 0;
+            // 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.
+            stage2Req = isStage2 ||
+                        (hcr.vm && !isHyp && !isSecure &&
+                         !(tranType & S1CTran) && (aarch64EL < EL2));
+            directToStage2 = !isStage2 && stage2Req && !sctlr.m;
+        } else {
+            vmid           = 0;
+            isHyp          = false;
+            directToStage2 = false;
+            stage2Req      = false;
+        }
     } else {  // AArch32
         sctlr  = tc->readMiscReg(flattenMiscRegNsBanked(MISCREG_SCTLR, tc,
                                  !isSecure));
@@ -1362,6 +1387,25 @@ TLB::getResultTe(TlbEntry **te, RequestPtr req, ThreadContext *tc, Mode mode,
         TlbEntry *mergeTe)
 {
     Fault fault;
+
+    if (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 *s2Te = NULL;
+        // Get the stage 2 table entry
+        fault = getTE(&s2Te, req, tc, mode, translation, timing, functional,
+                      isSecure, curTranType);
+        // Check permissions of stage 2
+        if ((s2Te != NULL) && (fault = NoFault)) {
+            if(aarch64)
+                fault = checkPermissions64(s2Te, req, mode, tc);
+            else
+                fault = checkPermissions(s2Te, req, mode);
+        }
+        *te = s2Te;
+        return fault;
+    }
+
     TlbEntry *s1Te = NULL;
 
     Addr vaddr_tainted = req->getVaddr();