arm: add stage2 translation support

Change-Id: I8f7c09c7ec3a97149ebebf4b21471b244e6cecc1
This commit is contained in:
Dylan Johnson 2016-08-02 10:38:02 +01:00
parent 49538a7118
commit c53a57f74f
4 changed files with 97 additions and 23 deletions

View 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:

View 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)

View 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;

View 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
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();