arm: support 16kb vm granules
authorCurtis Dunham <Curtis.Dunham@arm.com>
Tue, 27 May 2014 16:00:56 +0000 (11:00 -0500)
committerCurtis Dunham <Curtis.Dunham@arm.com>
Tue, 27 May 2014 16:00:56 +0000 (11:00 -0500)
src/arch/arm/miscregs.hh
src/arch/arm/table_walker.cc
src/arch/arm/table_walker.hh

index c447dcd276de5400c7e7bd91167fe7f268ec58fd..938df568810814e7310f2d18251b5d2028a13d16 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013 ARM Limited
+ * Copyright (c) 2010-2014 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -1715,6 +1715,30 @@ namespace ArmISA
         Bitfield<20> tbi;
     EndBitUnion(TTBCR)
 
+    // Fields of TCR_EL{1,2,3} (mostly overlapping)
+    // TCR_EL1 is natively 64 bits, the others are 32 bits
+    BitUnion64(TCR)
+        Bitfield<5, 0> t0sz;
+        Bitfield<7> epd0; // EL1
+        Bitfield<9, 8> irgn0;
+        Bitfield<11, 10> orgn0;
+        Bitfield<13, 12> sh0;
+        Bitfield<15, 14> tg0;
+        Bitfield<18, 16> ps;
+        Bitfield<20> tbi; // EL2/EL3
+        Bitfield<21, 16> t1sz; // EL1
+        Bitfield<22> a1; // EL1
+        Bitfield<23> epd1; // EL1
+        Bitfield<25, 24> irgn1; // EL1
+        Bitfield<27, 26> orgn1; // EL1
+        Bitfield<29, 28> sh1; // EL1
+        Bitfield<31, 30> tg1; // EL1
+        Bitfield<34, 32> ips; // EL1
+        Bitfield<36> as; // EL1
+        Bitfield<37> tbi0; // EL1
+        Bitfield<38> tbi1; // EL1
+    EndBitUnion(TCR)
+
     BitUnion32(HTCR)
         Bitfield<2, 0> t0sz;
         Bitfield<9, 8> irgn0;
index ea2865aebc6c9aef468e22e945d031a400fe8472..757dac6953ed0c46fb5207fb66e44c4af9d28338 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2013 ARM Limited
+ * Copyright (c) 2010, 2012-2014 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -220,18 +220,18 @@ TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint16_t _asid,
           case EL0:
           case EL1:
             currState->sctlr = currState->tc->readMiscReg(MISCREG_SCTLR_EL1);
-            currState->ttbcr = currState->tc->readMiscReg(MISCREG_TCR_EL1);
+            currState->tcr = currState->tc->readMiscReg(MISCREG_TCR_EL1);
             break;
           // @todo: uncomment this to enable Virtualization
           // case EL2:
           //   assert(haveVirtualization);
           //   currState->sctlr = currState->tc->readMiscReg(MISCREG_SCTLR_EL2);
-          //   currState->ttbcr = currState->tc->readMiscReg(MISCREG_TCR_EL2);
+          //   currState->tcr = currState->tc->readMiscReg(MISCREG_TCR_EL2);
           //   break;
           case EL3:
             assert(haveSecurity);
             currState->sctlr = currState->tc->readMiscReg(MISCREG_SCTLR_EL3);
-            currState->ttbcr = currState->tc->readMiscReg(MISCREG_TCR_EL3);
+            currState->tcr = currState->tc->readMiscReg(MISCREG_TCR_EL3);
             break;
           default:
             panic("Invalid exception level");
@@ -625,8 +625,7 @@ TableWalker::processWalkLPAE()
 
     currState->longDesc.lookupLevel = start_lookup_level;
     currState->longDesc.aarch64 = false;
-    currState->longDesc.largeGrain = false;
-    currState->longDesc.grainSize = 12;
+    currState->longDesc.grainSize = Grain4KB;
 
     Event *event = start_lookup_level == L1 ? (Event *) &doL1LongDescEvent
                                             : (Event *) &doL2LongDescEvent;
@@ -663,13 +662,18 @@ TableWalker::processWalkAArch64()
 {
     assert(currState->aarch64);
 
-    DPRINTF(TLB, "Beginning table walk for address %#llx, TTBCR: %#llx\n",
-            currState->vaddr_tainted, currState->ttbcr);
+    DPRINTF(TLB, "Beginning table walk for address %#llx, TCR: %#llx\n",
+            currState->vaddr_tainted, currState->tcr);
+
+    static const GrainSize GrainMapDefault[] =
+      { Grain4KB, Grain64KB, Grain16KB, ReservedGrain };
+    static const GrainSize GrainMap_EL1_tg1[] =
+      { ReservedGrain, Grain16KB, Grain4KB, Grain64KB };
 
     // Determine TTBR, table size, granule size and phys. address range
     Addr ttbr = 0;
     int tsz = 0, ps = 0;
-    bool large_grain = false;
+    GrainSize tg = Grain4KB; // grain size computed from tg* field
     bool fault = false;
     switch (currState->el) {
       case EL0:
@@ -678,44 +682,44 @@ TableWalker::processWalkAArch64()
           case 0:
             DPRINTF(TLB, " - Selecting TTBR0 (AArch64)\n");
             ttbr = currState->tc->readMiscReg(MISCREG_TTBR0_EL1);
-            tsz = adjustTableSizeAArch64(64 - currState->ttbcr.t0sz);
-            large_grain = currState->ttbcr.tg0;
+            tsz = adjustTableSizeAArch64(64 - currState->tcr.t0sz);
+            tg = GrainMapDefault[currState->tcr.tg0];
             if (bits(currState->vaddr, 63, tsz) != 0x0 ||
-                currState->ttbcr.epd0)
+                currState->tcr.epd0)
               fault = true;
             break;
           case 0xffff:
             DPRINTF(TLB, " - Selecting TTBR1 (AArch64)\n");
             ttbr = currState->tc->readMiscReg(MISCREG_TTBR1_EL1);
-            tsz = adjustTableSizeAArch64(64 - currState->ttbcr.t1sz);
-            large_grain = currState->ttbcr.tg1;
+            tsz = adjustTableSizeAArch64(64 - currState->tcr.t1sz);
+            tg = GrainMap_EL1_tg1[currState->tcr.tg1];
             if (bits(currState->vaddr, 63, tsz) != mask(64-tsz) ||
-                currState->ttbcr.epd1)
+                currState->tcr.epd1)
               fault = true;
             break;
           default:
             // top two bytes must be all 0s or all 1s, else invalid addr
             fault = true;
         }
-        ps = currState->ttbcr.ips;
+        ps = currState->tcr.ips;
         break;
       case EL2:
       case EL3:
         switch(bits(currState->vaddr, 63,48)) {
             case 0:
-        DPRINTF(TLB, " - Selecting TTBR0 (AArch64)\n");
-        if (currState->el == EL2)
-            ttbr = currState->tc->readMiscReg(MISCREG_TTBR0_EL2);
-        else
-            ttbr = currState->tc->readMiscReg(MISCREG_TTBR0_EL3);
-        tsz = adjustTableSizeAArch64(64 - currState->ttbcr.t0sz);
-        large_grain = currState->ttbcr.tg0;
+                DPRINTF(TLB, " - Selecting TTBR0 (AArch64)\n");
+                if (currState->el == EL2)
+                    ttbr = currState->tc->readMiscReg(MISCREG_TTBR0_EL2);
+                else
+                    ttbr = currState->tc->readMiscReg(MISCREG_TTBR0_EL3);
+                tsz = adjustTableSizeAArch64(64 - currState->tcr.t0sz);
+                tg = GrainMapDefault[currState->tcr.tg0];
                 break;
             default:
                 // invalid addr if top two bytes are not all 0s
-            fault = true;
+                fault = true;
         }
-        ps = currState->ttbcr.ps;
+        ps = currState->tcr.ips;
         break;
     }
 
@@ -744,32 +748,54 @@ TableWalker::processWalkAArch64()
 
     }
 
+    if (tg == ReservedGrain) {
+        warn_once("Reserved granule size requested; gem5's IMPLEMENTATION "
+                  "DEFINED behavior takes this to mean 4KB granules\n");
+        tg = Grain4KB;
+    }
+
+    int stride = tg - 3;
+    LookupLevel start_lookup_level = MAX_LOOKUP_LEVELS;
+
     // Determine starting lookup level
-    LookupLevel start_lookup_level;
-    int grain_size, stride;
-    if (large_grain) {  // 64 KB granule
-        grain_size = 16;
-        stride = grain_size - 3;
-        if (tsz > grain_size + 2 * stride)
-            start_lookup_level = L1;
-        else if (tsz > grain_size + stride)
-            start_lookup_level = L2;
-        else
-            start_lookup_level = L3;
-    } else {  // 4 KB granule
-        grain_size = 12;
-        stride = grain_size - 3;
-        if (tsz > grain_size + 3 * stride)
-            start_lookup_level = L0;
-        else if (tsz > grain_size + 2 * stride)
-            start_lookup_level = L1;
-        else
-            start_lookup_level = L2;
+    // 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.
+    {
+        struct GrainMap {
+            GrainSize grain_size;
+            unsigned lookup_level_cutoff[MAX_LOOKUP_LEVELS];
+        };
+        static const GrainMap GM[] = {
+            { Grain4KB,  { 39, 30,  0, 0 } },
+            { Grain16KB, { 47, 36, 25, 0 } },
+            { Grain64KB, { 64, 42, 29, 0 } }
+        };
+
+        const unsigned *lookup = NULL; // points to a lookup_level_cutoff
+
+        for (unsigned i = 0; i < 3; ++i) { // choose entry of GM[]
+            if (tg == GM[i].grain_size) {
+                lookup = GM[i].lookup_level_cutoff;
+                break;
+            }
+        }
+        assert(lookup);
+
+        for (int L = L0; L != MAX_LOOKUP_LEVELS; ++L) {
+            if (tsz > lookup[L]) {
+                start_lookup_level = (LookupLevel) L;
+                break;
+            }
+        }
+        panic_if(start_lookup_level == MAX_LOOKUP_LEVELS,
+                 "Table walker couldn't find lookup level\n");
     }
 
     // Determine table base address
-    int base_addr_lo = 3 + tsz - stride * (3 - start_lookup_level) -
-        grain_size;
+    int base_addr_lo = 3 + tsz - stride * (3 - start_lookup_level) - tg;
     Addr base_addr = mbits(ttbr, 47, base_addr_lo);
 
     // Determine physical address size and raise an Address Size Fault if
@@ -812,7 +838,7 @@ TableWalker::processWalkAArch64()
     // Determine descriptor address
     Addr desc_addr = base_addr |
         (bits(currState->vaddr, tsz - 1,
-              stride * (3 - start_lookup_level) + grain_size) << 3);
+              stride * (3 - start_lookup_level) + tg) << 3);
 
     // Trickbox address check
     Fault f = tlb->walkTrickBoxCheck(desc_addr, currState->isSecure,
@@ -839,8 +865,7 @@ TableWalker::processWalkAArch64()
 
     currState->longDesc.lookupLevel = start_lookup_level;
     currState->longDesc.aarch64 = true;
-    currState->longDesc.largeGrain = large_grain;
-    currState->longDesc.grainSize = grain_size;
+    currState->longDesc.grainSize = tg;
 
     if (currState->timing) {
         Event *event;
index 4753fe6a045b34b4872f2f9300584b319d94a555..2e1d0f9f558df9a0a180bdfde243a9b98a278d03 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2013 ARM Limited
+ * Copyright (c) 2010-2014 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -362,6 +362,14 @@ class TableWalker : public MemObject
 
     };
 
+    // Granule sizes for AArch64 long descriptors
+    enum GrainSize {
+        Grain4KB  = 12,
+        Grain16KB = 14,
+        Grain64KB = 16,
+        ReservedGrain = 0
+    };
+
     /** Long-descriptor format (LPAE) */
     class LongDescriptor : public DescriptorBase {
       public:
@@ -409,11 +417,8 @@ class TableWalker : public MemObject
         /** True if the current lookup is performed in AArch64 state */
         bool aarch64;
 
-        /** True if the granule size is 64 KB (AArch64 only) */
-        bool largeGrain;
-
         /** Width of the granule size in bits */
-        int grainSize;
+        GrainSize grainSize;
 
         /** Return the descriptor type */
         EntryType type() const
@@ -421,8 +426,8 @@ class TableWalker : public MemObject
             switch (bits(data, 1, 0)) {
               case 0x1:
                 // In AArch64 blocks are not allowed at L0 for the 4 KB granule
-                // and at L1 for the 64 KB granule
-                if (largeGrain)
+                // and at L1 for 16/64 KB granules
+                if (grainSize > Grain4KB)
                     return lookupLevel == L2 ? Block : Invalid;
                 return lookupLevel == L0 || lookupLevel == L3 ? Invalid : Block;
               case 0x3:
@@ -435,15 +440,29 @@ class TableWalker : public MemObject
         /** Return the bit width of the page/block offset */
         uint8_t offsetBits() const
         {
-            assert(type() == Block || type() == Page);
-            if (largeGrain) {
-                if (type() == Block)
-                    return 29 /* 512 MB */;
-                return 16 /* 64 KB */;  // type() == Page
+            if (type() == Block) {
+                switch (grainSize) {
+                    case Grain4KB:
+                        return lookupLevel == L1 ? 30 /* 1 GB */
+                                                 : 21 /* 2 MB */;
+                    case Grain16KB:
+                        return 25  /* 32 MB */;
+                    case Grain64KB:
+                        return 29 /* 512 MB */;
+                    default:
+                        panic("Invalid AArch64 VM granule size\n");
+                }
+            } else if (type() == Page) {
+                switch (grainSize) {
+                    case Grain4KB:
+                    case Grain16KB:
+                    case Grain64KB:
+                        return grainSize; /* enum -> uint okay */
+                    default:
+                        panic("Invalid AArch64 VM granule size\n");
+                }
             } else {
-                if (type() == Block)
-                    return lookupLevel == L1 ? 30 /* 1 GB */ : 21 /* 2 MB */;
-                return 12 /* 4 KB */;  // type() == Page
+                panic("AArch64 page table entry must be block or page\n");
             }
         }
 
@@ -707,8 +726,11 @@ class TableWalker : public MemObject
         /** Cached copy of the cpsr as it existed when translation began */
         CPSR cpsr;
 
-        /** Cached copy of the ttbcr as it existed when translation began. */
-        TTBCR ttbcr;
+        /** Cached copy of ttbcr/tcr as it existed when translation began */
+        union {
+            TTBCR ttbcr; // AArch32 translations
+            TCR tcr;     // AArch64 translations
+        };
 
         /** Cached copy of the htcr as it existed when translation began. */
         HTCR htcr;