From c53a57f74f31c2593665bae716c5c3679aab5595 Mon Sep 17 00:00:00 2001 From: Dylan Johnson Date: Tue, 2 Aug 2016 10:38:02 +0100 Subject: [PATCH] arm: add stage2 translation support Change-Id: I8f7c09c7ec3a97149ebebf4b21471b244e6cecc1 --- src/arch/arm/isa.cc | 1 + src/arch/arm/miscregs.hh | 4 ++- src/arch/arm/table_walker.cc | 57 +++++++++++++++++++++++++---------- src/arch/arm/tlb.cc | 58 +++++++++++++++++++++++++++++++----- 4 files changed, 97 insertions(+), 23 deletions(-) diff --git a/src/arch/arm/isa.cc b/src/arch/arm/isa.cc index 2cf67fff7..2300b925e 100644 --- a/src/arch/arm/isa.cc +++ b/src/arch/arm/isa.cc @@ -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: diff --git a/src/arch/arm/miscregs.hh b/src/arch/arm/miscregs.hh index 84f0fe8d1..f92cc7115 100644 --- a/src/arch/arm/miscregs.hh +++ b/src/arch/arm/miscregs.hh @@ -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) diff --git a/src/arch/arm/table_walker.cc b/src/arch/arm/table_walker.cc index 1f0bdeb8e..4eb57c59a 100644 --- a/src/arch/arm/table_walker.cc +++ b/src/arch/arm/table_walker.cc @@ -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; diff --git a/src/arch/arm/tlb.cc b/src/arch/arm/tlb.cc index a499900e0..864f0c28c 100644 --- a/src/arch/arm/tlb.cc +++ b/src/arch/arm/tlb.cc @@ -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();