From 12210ada547fa8ad10a1db4a440c25f6679809bd Mon Sep 17 00:00:00 2001 From: Curtis Dunham Date: Tue, 27 May 2014 11:00:56 -0500 Subject: [PATCH] arm: support 16kb vm granules --- src/arch/arm/miscregs.hh | 26 +++++++- src/arch/arm/table_walker.cc | 125 +++++++++++++++++++++-------------- src/arch/arm/table_walker.hh | 56 +++++++++++----- 3 files changed, 139 insertions(+), 68 deletions(-) diff --git a/src/arch/arm/miscregs.hh b/src/arch/arm/miscregs.hh index c447dcd27..938df5688 100644 --- a/src/arch/arm/miscregs.hh +++ b/src/arch/arm/miscregs.hh @@ -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; diff --git a/src/arch/arm/table_walker.cc b/src/arch/arm/table_walker.cc index ea2865aeb..757dac695 100644 --- a/src/arch/arm/table_walker.cc +++ b/src/arch/arm/table_walker.cc @@ -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; diff --git a/src/arch/arm/table_walker.hh b/src/arch/arm/table_walker.hh index 4753fe6a0..2e1d0f9f5 100644 --- a/src/arch/arm/table_walker.hh +++ b/src/arch/arm/table_walker.hh @@ -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; -- 2.30.2