arch-arm: Proper support for NonSecure IPA space in Secure state

Change-Id: Ie2e2278ecdc5213db74999e3561b2918937c2c2e
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
This commit is contained in:
Giacomo Travaglini
2024-01-18 17:02:32 +00:00
parent eb400e773b
commit f3e3c60805
12 changed files with 110 additions and 39 deletions

View File

@@ -339,14 +339,7 @@ TlbiOp64::tlbiIpaS2(ThreadContext *tc, RegVal value,
bool last_level, TlbiAttr attrs)
{
if (EL2Enabled(tc)) {
if (ss == SecurityState::Secure && bits(value, 63)) {
ss = SecurityState::NonSecure;
}
const int top_bit = ArmSystem::physAddrRange(tc) == 52 ?
39 : 35;
TLBIIPA tlbi_op(TranslationRegime::EL10, ss,
static_cast<Addr>(bits(value, top_bit, 0)) << 12,
TLBIIPA tlbi_op(tc, TranslationRegime::EL10, ss, value,
last_level, attrs);
if (shareable) {
@@ -398,11 +391,7 @@ TlbiOp64::tlbiRipaS2(ThreadContext *tc, RegVal value,
bool last_level, TlbiAttr attrs)
{
if (EL2Enabled(tc)) {
if (ss == SecurityState::Secure && bits(value, 63)) {
ss = SecurityState::NonSecure;
}
TLBIRIPA tlbi_op(TranslationRegime::EL10, ss, value,
TLBIRIPA tlbi_op(tc, TranslationRegime::EL10, ss, value,
last_level, attrs);
if (shareable) {

View File

@@ -1385,11 +1385,11 @@ MMU::tranTypeEL(CPSR cpsr, SCR scr, ArmTranslationType type)
Fault
MMU::getTE(TlbEntry **te, const RequestPtr &req, ThreadContext *tc, Mode mode,
Translation *translation, bool timing, bool functional,
SecurityState ss, ArmTranslationType tran_type,
SecurityState ss, PASpace ipaspace, ArmTranslationType tran_type,
bool stage2)
{
return getTE(te, req, tc, mode, translation, timing, functional,
ss, tran_type, stage2 ? s2State : s1State);
ss, ipaspace, tran_type, stage2 ? s2State : s1State);
}
TlbEntry*
@@ -1416,7 +1416,7 @@ MMU::lookup(Addr va, uint16_t asid, vmid_t vmid, SecurityState ss,
Fault
MMU::getTE(TlbEntry **te, const RequestPtr &req, ThreadContext *tc, Mode mode,
Translation *translation, bool timing, bool functional,
SecurityState ss, ArmTranslationType tran_type,
SecurityState ss, PASpace ipaspace, ArmTranslationType tran_type,
CachedState& state)
{
// In a 2-stage system, the IPA->PA translation can be started via this
@@ -1459,7 +1459,7 @@ MMU::getTE(TlbEntry **te, const RequestPtr &req, ThreadContext *tc, Mode mode,
fault = getTableWalker(mode, state.isStage2)->walk(
req, tc, state.asid, state.vmid, mode,
translation, timing, functional, ss,
tran_type, state.stage2DescReq, *te);
ipaspace, tran_type, state.stage2DescReq, *te);
// for timing mode, return and wait for table walk,
if (timing || fault != NoFault) {
@@ -1482,12 +1482,16 @@ MMU::getResultTe(TlbEntry **te, const RequestPtr &req,
Fault fault;
if (state.isStage2) {
PASpace ipaspace = state.securityState == SecurityState::Secure ?
PASpace::Secure : PASpace::NonSecure;
// 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 *s2_te = nullptr;
// Get the stage 2 table entry
fault = getTE(&s2_te, req, tc, mode, translation, timing, functional,
state.securityState, state.curTranType, state);
state.securityState, ipaspace,
state.curTranType, state);
// Check permissions of stage 2
if (isCompleteTranslation(s2_te) && (fault == NoFault)) {
if (state.aarch64)
@@ -1505,7 +1509,8 @@ MMU::getResultTe(TlbEntry **te, const RequestPtr &req,
// Get the stage 1 table entry
fault = getTE(&s1_te, req, tc, mode, translation, timing, functional,
state.securityState, state.curTranType, state);
state.securityState, PASpace::NonSecure,
state.curTranType, state);
// only proceed if we have a valid table entry
if (isCompleteTranslation(s1_te) && (fault == NoFault)) {
// Check stage 1 permissions before checking stage 2

View File

@@ -412,12 +412,14 @@ class MMU : public BaseMMU
Fault getTE(TlbEntry **te, const RequestPtr &req,
ThreadContext *tc, Mode mode,
Translation *translation, bool timing, bool functional,
SecurityState ss, ArmTranslationType tran_type,
SecurityState ss, PASpace ipaspace,
ArmTranslationType tran_type,
bool stage2);
Fault getTE(TlbEntry **te, const RequestPtr &req,
ThreadContext *tc, Mode mode,
Translation *translation, bool timing, bool functional,
SecurityState ss, ArmTranslationType tran_type,
SecurityState ss, PASpace ipaspace,
ArmTranslationType tran_type,
CachedState &state);
Fault getResultTe(TlbEntry **te, const RequestPtr &req,

View File

@@ -241,6 +241,8 @@ struct TlbEntry : public Serializable
bool ns;
// Security state of the translation regime
SecurityState ss;
// IPA Space (stage2 entries only)
PASpace ipaSpace;
// Translation regime on insert, AARCH64 EL0&1, AARCH32 -> el=1
TranslationRegime regime;
// This is used to distinguish between instruction and data entries
@@ -272,6 +274,7 @@ struct TlbEntry : public Serializable
domain(DomainType::Client), mtype(MemoryType::StronglyOrdered),
longDescFormat(false), global(false), valid(true),
ns(true), ss(SecurityState::NonSecure),
ipaSpace(PASpace::NonSecure),
regime(TranslationRegime::EL10),
type(TypeTLB::unified), partial(false),
nonCacheable(uncacheable),
@@ -292,6 +295,7 @@ struct TlbEntry : public Serializable
domain(DomainType::Client), mtype(MemoryType::StronglyOrdered),
longDescFormat(false), global(false), valid(false),
ns(true), ss(SecurityState::NonSecure),
ipaSpace(PASpace::NonSecure),
regime(TranslationRegime::EL10),
type(TypeTLB::unified), partial(false), nonCacheable(false),
shareable(false), outerShareable(false), xn(0), pxn(0),

View File

@@ -655,6 +655,10 @@ namespace ArmISA
Bitfield<19> vs; // Only defined for VTCR_EL2
Bitfield<21> ha; // Only defined for VTCR_EL2
Bitfield<22> hd; // Only defined for VTCR_EL2
Bitfield<29> nsw; // Only defined for VTCR_EL2
Bitfield<29> sw; // Only defined for VSTCR_EL2
Bitfield<30> nsa; // Only defined for VTCR_EL2
Bitfield<30> sa; // Only defined for VSTCR_EL2
EndBitUnion(VTCR_t)
BitUnion32(PRRR)

View File

@@ -57,7 +57,7 @@ Fault
Stage2LookUp::getTe(ThreadContext *tc, TlbEntry *destTe)
{
fault = mmu->getTE(&stage2Te, req, tc, mode, this, timing,
functional, ss, tranType, true);
functional, ss, ipaSpace, tranType, true);
// Call finish if we're done already
if ((fault != NoFault) || (stage2Te != NULL)) {
@@ -193,7 +193,7 @@ Stage2LookUp::finish(const Fault &_fault, const RequestPtr &req,
if ((fault == NoFault) && (stage2Te == NULL)) {
// OLD_LOOK: stage2Tlb
fault = mmu->getTE(&stage2Te, req, tc, mode, this,
timing, functional, ss, tranType, true);
timing, functional, ss, ipaSpace, tranType, true);
}
// Now we have the stage 2 table entry we need to merge it with the stage

View File

@@ -73,6 +73,7 @@ class Stage2LookUp : public BaseMMU::Translation
bool complete;
bool selfDelete;
SecurityState ss;
PASpace ipaSpace;
public:
Stage2LookUp(MMU *_mmu, TlbEntry s1_te, const RequestPtr &_req,
@@ -82,7 +83,8 @@ class Stage2LookUp : public BaseMMU::Translation
: mmu(_mmu), stage1Te(s1_te), s1Req(_req),
transState(_transState), mode(_mode), timing(_timing),
functional(_functional), tranType(_tranType), stage2Te(nullptr),
fault(NoFault), complete(false), selfDelete(false), ss(_ss)
fault(NoFault), complete(false), selfDelete(false), ss(_ss),
ipaSpace(s1_te.ns ? PASpace::NonSecure : PASpace::Secure)
{
req = std::make_shared<Request>();
req->setVirt(s1_te.pAddr(s1Req->getVaddr()), s1Req->getSize(),

View File

@@ -295,7 +295,8 @@ Fault
TableWalker::walk(const RequestPtr &_req, ThreadContext *_tc, uint16_t _asid,
vmid_t _vmid, MMU::Mode _mode,
MMU::Translation *_trans, bool _timing, bool _functional,
SecurityState ss, MMU::ArmTranslationType tranType,
SecurityState ss, PASpace ipaspace,
MMU::ArmTranslationType tranType,
bool _stage2Req, const TlbEntry *walk_entry)
{
assert(!(_functional && _timing));
@@ -344,6 +345,7 @@ TableWalker::walk(const RequestPtr &_req, ThreadContext *_tc, uint16_t _asid,
if (isStage2) {
currState->regime = TranslationRegime::EL10;
currState->aarch64 = ELIs64(_tc, EL2);
currState->ipaSpace = ipaspace;
} else {
currState->regime =
translationRegime(_tc, currState->el);
@@ -382,7 +384,8 @@ TableWalker::walk(const RequestPtr &_req, ThreadContext *_tc, uint16_t _asid,
currState->hcr = currState->tc->readMiscReg(MISCREG_HCR_EL2);
if (isStage2) {
currState->sctlr = currState->tc->readMiscReg(MISCREG_SCTLR_EL1);
if (currState->secureLookup) {
if (currState->ss == SecurityState::Secure &&
currState->ipaSpace == PASpace::Secure) {
currState->vtcr =
currState->tc->readMiscReg(MISCREG_VSTCR_EL2);
} else {
@@ -905,12 +908,19 @@ TableWalker::processWalkAArch64()
switch (currState->regime) {
case TranslationRegime::EL10:
if (isStage2) {
if (currState->secureLookup) {
if (currState->ss == SecurityState::Secure &&
currState->ipaSpace == PASpace::Secure) {
// Secure EL1&0 Secure IPA
DPRINTF(TLB, " - Selecting VSTTBR_EL2 (AArch64 stage 2)\n");
ttbr = currState->tc->readMiscReg(MISCREG_VSTTBR_EL2);
currState->secureLookup = !currState->vtcr.sw;
} else {
// Secure EL1&0 NonSecure IPA or NonSecure EL1&0
DPRINTF(TLB, " - Selecting VTTBR_EL2 (AArch64 stage 2)\n");
ttbr = currState->tc->readMiscReg(MISCREG_VTTBR_EL2);
currState->secureLookup = currState->ss == SecurityState::Secure ?
!currState->vtcr.nsw : // Secure EL1&0 NonSecure IPA
false; // NonSecure EL1&0
}
tsz = 64 - currState->vtcr.t0sz64;
tg = GrainMap_tg0[currState->vtcr.tg0];
@@ -1809,8 +1819,10 @@ TableWalker::doLongDescriptor()
case LongDescriptor::Table:
{
// Set hierarchical permission flags
currState->secureLookup = currState->secureLookup &&
currState->longDesc.secureTable();
if (!isStage2) {
currState->secureLookup = currState->secureLookup &&
currState->longDesc.secureTable();
}
currState->longDescData->rwTable =
currState->longDescData->rwTable &&
(currState->longDesc.rwTable() || currState->hpd);
@@ -2246,6 +2258,7 @@ TableWalker::insertPartialTableEntry(LongDescriptor &descriptor)
te.lookupLevel = descriptor.lookupLevel;
te.ns = !descriptor.secure(have_security, currState);
te.ss = currState->ss;
te.ipaSpace = currState->ipaSpace; // Used by stage2 entries only
te.type = TypeTLB::unified;
te.regime = currState->regime;
@@ -2292,6 +2305,7 @@ TableWalker::insertTableEntry(DescriptorBase &descriptor, bool long_descriptor)
te.lookupLevel = descriptor.lookupLevel;
te.ns = !descriptor.secure(have_security, currState);
te.ss = currState->ss;
te.ipaSpace = currState->ipaSpace; // Used by stage2 entries only
te.xn = descriptor.xn();
te.type = currState->mode == BaseMMU::Execute ?
TypeTLB::instruction : TypeTLB::data;

View File

@@ -493,8 +493,13 @@ class TableWalker : public ClockedObject
secure(bool have_security, WalkerState *currState) const override
{
if (type() == Block || type() == Page) {
return have_security &&
(currState->secureLookup && !bits(data, 5));
if (isStage2) {
return have_security && currState->secureLookup &&
!currState->vtcr.nsa;
} else {
return have_security &&
(currState->secureLookup && !bits(data, 5));
}
} else {
return have_security && currState->secureLookup;
}
@@ -906,6 +911,11 @@ class TableWalker : public ClockedObject
* long descriptor table attributes. */
bool secureLookup = false;
/** IPA space (Secure vs NonSecure); stage2 only.
* This depends on whether the stage1 translation targeted
* a secure or non-secure IPA space */
PASpace ipaSpace;
/** True if table walks are uncacheable (for table descriptors) */
bool isUncacheable;
@@ -1143,6 +1153,7 @@ class TableWalker : public ClockedObject
uint16_t asid, vmid_t _vmid,
BaseMMU::Mode mode, BaseMMU::Translation *_trans,
bool timing, bool functional, SecurityState ss,
PASpace ipaspace,
MMU::ArmTranslationType tran_type, bool stage2,
const TlbEntry *walk_entry);

View File

@@ -328,7 +328,8 @@ TLBIIPA::matchEntry(TlbEntry* te, vmid_t vmid) const
{
TlbEntry::Lookup lookup_data = lookupGen(vmid);
return te->match(lookup_data) && (!lastLevel || !te->partial);
return te->match(lookup_data) && (!lastLevel || !te->partial) &&
ipaSpace == te->ipaSpace;
}
bool
@@ -371,9 +372,10 @@ TLBIRIPA::matchEntry(TlbEntry* te, vmid_t vmid) const
auto addr_match = te->match(lookup_data) && (!lastLevel || !te->partial);
if (addr_match) {
return tgMap[rangeData.tg] == te->tg &&
(resTLBIttl(rangeData.tg, rangeData.ttl) ||
rangeData.ttl == te->lookupLevel);
return ipaSpace == te->ipaSpace &&
tgMap[rangeData.tg] == te->tg &&
(resTLBIttl(rangeData.tg, rangeData.ttl) ||
rangeData.ttl == te->lookupLevel);
} else {
return false;
}

View File

@@ -409,9 +409,37 @@ class TLBIIPA : public TLBIOp
public:
TLBIIPA(TranslationRegime _target_regime, SecurityState _ss, Addr _addr,
bool last_level, Attr _attr=Attr::None)
: TLBIOp(_target_regime, _ss, _attr), addr(_addr), lastLevel(last_level)
: TLBIOp(_target_regime, _ss, _attr),
addr(_addr),
lastLevel(last_level),
ipaSpace(PASpace::NonSecure)
{}
TLBIIPA(ThreadContext *tc, TranslationRegime _target_regime,
SecurityState _ss, RegVal val,
bool last_level, Attr _attr=Attr::None)
: TLBIOp(_target_regime, _ss, _attr),
addr(0),
lastLevel(last_level),
ipaSpace(PASpace::NonSecure)
{
const int top_bit = ArmSystem::physAddrRange(tc) == 52 ?
39 : 35;
addr = static_cast<Addr>(bits(val, top_bit, 0)) << 12;
switch (ss) {
case SecurityState::NonSecure:
ipaSpace = PASpace::NonSecure;
break;
case SecurityState::Secure:
ipaSpace = bits(val, 63) ?
PASpace::NonSecure : PASpace::Secure;
break;
default:
panic("Invalid SecurityState\n");
}
}
void operator()(ThreadContext* tc) override;
bool matchEntry(TlbEntry *entry, vmid_t curr_vmid) const override;
@@ -424,6 +452,7 @@ class TLBIIPA : public TLBIOp
Addr addr;
bool lastLevel;
PASpace ipaSpace;
};
/** TLB Range Invalidate by VA */
@@ -456,11 +485,13 @@ class TLBIRMVAA : public TLBIRange, public TLBIMVAA
class TLBIRIPA : public TLBIRange, public TLBIIPA
{
public:
TLBIRIPA(TranslationRegime _target_regime, SecurityState _ss,
TLBIRIPA(ThreadContext *tc, TranslationRegime _target_regime, SecurityState _ss,
RegVal val, bool last_level, Attr _attr)
: TLBIRange(val),
TLBIIPA(_target_regime, _ss, startAddress(), last_level, _attr)
{}
TLBIIPA(tc, _target_regime, _ss, val, last_level, _attr)
{
addr = startAddress();
}
bool matchEntry(TlbEntry *entry, vmid_t curr_vmid) const override;
};

View File

@@ -275,6 +275,13 @@ namespace ArmISA
Secure
};
/** Physical Address Space */
enum class PASpace
{
NonSecure,
Secure
};
enum ExceptionLevel
{
EL0 = 0,