arm: add stage2 translation support
Change-Id: I8f7c09c7ec3a97149ebebf4b21471b244e6cecc1
This commit is contained in:
parent
49538a7118
commit
c53a57f74f
4 changed files with 97 additions and 23 deletions
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue