summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDylan Johnson <Dylan.Johnson@ARM.com>2016-08-02 10:38:02 +0100
committerDylan Johnson <Dylan.Johnson@ARM.com>2016-08-02 10:38:02 +0100
commitc53a57f74f31c2593665bae716c5c3679aab5595 (patch)
tree292e4929f35d05d7fac170dacaccd720007269cf /src
parent49538a71186d98f5440c5db646e23507fc2e38d1 (diff)
downloadgem5-c53a57f74f31c2593665bae716c5c3679aab5595.tar.xz
arm: add stage2 translation support
Change-Id: I8f7c09c7ec3a97149ebebf4b21471b244e6cecc1
Diffstat (limited to 'src')
-rw-r--r--src/arch/arm/isa.cc1
-rw-r--r--src/arch/arm/miscregs.hh4
-rw-r--r--src/arch/arm/table_walker.cc57
-rw-r--r--src/arch/arm/tlb.cc58
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();