arch-arm: Implement AT as standalone instructions (#1697)

Moving the address translation logic outside of the ISA::setMiscReg will
allow it to return and potentially invoke a fault
upon execution of the AT instruction. This change affects AArch64 mode
only
This commit is contained in:
Giacomo Travaglini
2024-10-22 17:25:16 +02:00
committed by GitHub
10 changed files with 193 additions and 95 deletions

View File

@@ -1430,6 +1430,9 @@ DataAbort::annotate(AnnotationIDs id, uint64_t val)
case OFA:
faultAddr = val;
break;
case WnR:
write = val;
break;
// Just ignore unknown ID's
default:
break;

View File

@@ -141,6 +141,8 @@ class ArmFault : public FaultBase
// the abort is triggered by a CMO. The faulting address is
// then the address specified in the register argument of the
// instruction and not the cacheline address (See FAR doc)
WnR, // Write or Read. it should be forced to 1 when
// Cache maintainance and address translation instruction.
// AArch64 only
SF, // DataAbort: width of the accessed register is SixtyFour

View File

@@ -39,6 +39,7 @@
#include "arch/arm/isa.hh"
#include "arch/arm/tlbi_op.hh"
#include "debug/MiscRegs.hh"
namespace gem5
{
@@ -2533,4 +2534,113 @@ TlbiOp64::performTlbi(ExecContext *xc, MiscRegIndex dest_idx, RegVal value) cons
}
}
std::pair<Fault, uint64_t>
AtOp64::performAt(ExecContext *xc, MiscRegIndex dest_idx, RegVal val) const
{
ThreadContext* tc = xc->tcBase();
switch (dest_idx) {
case MISCREG_AT_S1E1R_Xt:
return addressTranslation64(tc, MMU::S1E1Tran, BaseMMU::Read, 0, val);
case MISCREG_AT_S1E1W_Xt:
return addressTranslation64(tc, MMU::S1E1Tran, BaseMMU::Write, 0, val);
case MISCREG_AT_S1E0R_Xt:
return addressTranslation64(tc, MMU::S1E0Tran, BaseMMU::Read,
MMU::UserMode, val);
case MISCREG_AT_S1E0W_Xt:
return addressTranslation64(tc, MMU::S1E0Tran, BaseMMU::Write,
MMU::UserMode, val);
case MISCREG_AT_S1E2R_Xt:
return addressTranslation64(tc, MMU::S1E2Tran, BaseMMU::Read, 0, val);
case MISCREG_AT_S1E2W_Xt:
return addressTranslation64(tc, MMU::S1E2Tran, BaseMMU::Write, 0, val);
case MISCREG_AT_S12E1R_Xt:
return addressTranslation64(tc, MMU::S12E1Tran, BaseMMU::Read, 0, val);
case MISCREG_AT_S12E1W_Xt:
return addressTranslation64(tc, MMU::S12E1Tran, BaseMMU::Write,
0, val);
case MISCREG_AT_S12E0R_Xt:
return addressTranslation64(tc, MMU::S12E0Tran, BaseMMU::Read,
MMU::UserMode, val);
case MISCREG_AT_S12E0W_Xt:
return addressTranslation64(tc, MMU::S12E0Tran, BaseMMU::Write,
MMU::UserMode, val);
case MISCREG_AT_S1E3R_Xt:
return addressTranslation64(tc, MMU::S1E3Tran, BaseMMU::Read, 0, val);
case MISCREG_AT_S1E3W_Xt:
return addressTranslation64(tc, MMU::S1E3Tran, BaseMMU::Write, 0, val);
default:
return std::make_pair(NoFault, 0);
}
return std::make_pair(NoFault, 0);
}
std::pair<Fault, uint64_t>
AtOp64::addressTranslation64(ThreadContext* tc,
ArmISA::MMU::ArmTranslationType tran_type,
BaseMMU::Mode mode, Request::Flags flags, RegVal val) const
{
// If we're in timing mode then doing the translation in
// functional mode then we're slightly distorting performance
// results obtained from simulations. The translation should be
// done in the same mode the core is running in. NOTE: This
// can't be an atomic translation because that causes problems
// with unexpected atomic snoop requests.
warn_once("Doing AT (address translation) in functional mode! Fix Me!\n");
auto req = std::make_shared<Request>(
val, 0, flags, Request::funcRequestorId,
tc->pcState().instAddr(), tc->contextId());
Fault fault = getMMUPtr(tc)->translateAtomic(
req, tc, mode, tran_type);
PAR par = 0;
bool raise_fault = false;
if (fault == NoFault) {
Addr paddr = req->getPaddr();
uint64_t attr = getMMUPtr(tc)->getAttr();
// clear LAPE bit from attribute.
attr &= ~ uint64_t(0x800);
uint64_t attr1 = attr >> 56;
if (!(attr1 >> 4) || attr1 == 0x44) {
attr |= 0x100;
attr &= ~ uint64_t(0x80);
}
par = (paddr & mask(47, 12)) | attr;
DPRINTF(MiscRegs, "AT: Translated addr %#x: PAR_EL1: %#x\n",
val, par);
} else {
ArmFault *arm_fault = static_cast<ArmFault *>(fault.get());
arm_fault->update(tc);
// Set fault bit and FSR
FSR fsr = arm_fault->getFsr(tc);
arm_fault->annotate(ArmFault::CM, 1); // CM
arm_fault->annotate(ArmFault::WnR, 1); // Force WnR as 1
par.f = 1; // F bit
par.fst = fsr.status; // FST
par.ptw = (arm_fault->iss() >> 7) & 0x1; // S1PTW
par.s = arm_fault->isStage2() ? 1 : 0; // S
// set RES1 bit [11].
par |= 0x800;
// Only raise fault for external abort and stage 2 fault,
// see R~NHWXL~ in Arm-ARM.
raise_fault = arm_fault->isExternalAbort() || par.ptw;
DPRINTF(MiscRegs, "AT: Translated addr %#x fault fsr %#x: PAR: %#x\n",
val, fsr, par);
}
// Fault filter.
if (fault != NoFault && !raise_fault) {
fault = NoFault;
}
return std::make_pair(fault, par);
}
} // namespace gem5

View File

@@ -39,6 +39,7 @@
#define __ARCH_ARM_INSTS_MISC64_HH__
#include "arch/arm/insts/static_inst.hh"
#include "arch/arm/mmu.hh"
#include "arch/arm/tlbi_op.hh"
#include "arch/arm/types.hh"
@@ -339,6 +340,23 @@ class TlbiOp64 : public MiscRegRegImmOp64
ArmISA::MiscRegIndex idx, RegVal value) const;
};
class AtOp64 : public MiscRegRegImmOp64
{
protected:
AtOp64(const char *mnem, ArmISA::ExtMachInst _machInst,
OpClass __opClass, ArmISA::MiscRegIndex _dest,
RegIndex _op1) :
MiscRegRegImmOp64(mnem, _machInst, __opClass, _dest, _op1)
{}
std::pair<Fault, uint64_t> performAt(ExecContext *xc,
ArmISA::MiscRegIndex idx, RegVal val) const;
std::pair<Fault, uint64_t> addressTranslation64(ThreadContext* tc,
ArmISA::MMU::ArmTranslationType tran_type,
BaseMMU::Mode mode, Request::Flags flags, RegVal val) const;
};
} // namespace gem5
#endif

View File

@@ -1293,46 +1293,6 @@ ISA::setMiscReg(RegIndex idx, RegVal val)
idx = MISCREG_CPSR;
}
break;
case MISCREG_AT_S1E1R_Xt:
addressTranslation64(MMU::S1E1Tran, BaseMMU::Read, 0, val);
return;
case MISCREG_AT_S1E1W_Xt:
addressTranslation64(MMU::S1E1Tran, BaseMMU::Write, 0, val);
return;
case MISCREG_AT_S1E0R_Xt:
addressTranslation64(MMU::S1E0Tran, BaseMMU::Read,
MMU::UserMode, val);
return;
case MISCREG_AT_S1E0W_Xt:
addressTranslation64(MMU::S1E0Tran, BaseMMU::Write,
MMU::UserMode, val);
return;
case MISCREG_AT_S1E2R_Xt:
addressTranslation64(MMU::S1E2Tran, BaseMMU::Read, 0, val);
return;
case MISCREG_AT_S1E2W_Xt:
addressTranslation64(MMU::S1E2Tran, BaseMMU::Write, 0, val);
return;
case MISCREG_AT_S12E1R_Xt:
addressTranslation64(MMU::S12E1Tran, BaseMMU::Read, 0, val);
return;
case MISCREG_AT_S12E1W_Xt:
addressTranslation64(MMU::S12E1Tran, BaseMMU::Write, 0, val);
return;
case MISCREG_AT_S12E0R_Xt:
addressTranslation64(MMU::S12E0Tran, BaseMMU::Read,
MMU::UserMode, val);
return;
case MISCREG_AT_S12E0W_Xt:
addressTranslation64(MMU::S12E0Tran, BaseMMU::Write,
MMU::UserMode, val);
return;
case MISCREG_AT_S1E3R_Xt:
addressTranslation64(MMU::S1E3Tran, BaseMMU::Read, 0, val);
return;
case MISCREG_AT_S1E3W_Xt:
addressTranslation64(MMU::S1E3Tran, BaseMMU::Write, 0, val);
return;
case MISCREG_L2CTLR:
warn("miscreg L2CTLR (%s) written with %#x. ignored...\n",
miscRegName[idx], uint32_t(val));
@@ -1596,57 +1556,6 @@ ISA::unserialize(CheckpointIn &cp)
updateRegMap(tmp_cpsr);
}
void
ISA::addressTranslation64(MMU::ArmTranslationType tran_type,
BaseMMU::Mode mode, Request::Flags flags, RegVal val)
{
// If we're in timing mode then doing the translation in
// functional mode then we're slightly distorting performance
// results obtained from simulations. The translation should be
// done in the same mode the core is running in. NOTE: This
// can't be an atomic translation because that causes problems
// with unexpected atomic snoop requests.
warn_once("Doing AT (address translation) in functional mode! Fix Me!\n");
auto req = std::make_shared<Request>(
val, 0, flags, Request::funcRequestorId,
tc->pcState().instAddr(), tc->contextId());
Fault fault = getMMUPtr(tc)->translateFunctional(
req, tc, mode, tran_type);
PAR par = 0;
if (fault == NoFault) {
Addr paddr = req->getPaddr();
uint64_t attr = getMMUPtr(tc)->getAttr();
uint64_t attr1 = attr >> 56;
if (!attr1 || attr1 ==0x44) {
attr |= 0x100;
attr &= ~ uint64_t(0x80);
}
par = (paddr & mask(47, 12)) | attr;
DPRINTF(MiscRegs,
"MISCREG: Translated addr %#x: PAR_EL1: %#xx\n",
val, par);
} else {
ArmFault *arm_fault = static_cast<ArmFault *>(fault.get());
arm_fault->update(tc);
// Set fault bit and FSR
FSR fsr = arm_fault->getFsr(tc);
par.f = 1; // F bit
par.fst = fsr.status; // FST
par.ptw = (arm_fault->iss() >> 7) & 0x1; // S1PTW
par.s = arm_fault->isStage2() ? 1 : 0; // S
DPRINTF(MiscRegs,
"MISCREG: Translated addr %#x fault fsr %#x: PAR: %#x\n",
val, fsr, par);
}
setMiscRegNoEffect(MISCREG_PAR_EL1, par);
return;
}
void
ISA::addressTranslation(MMU::ArmTranslationType tran_type,
BaseMMU::Mode mode, Request::Flags flags, RegVal val)

View File

@@ -173,8 +173,6 @@ namespace ArmISA
protected:
void addressTranslation(MMU::ArmTranslationType tran_type,
BaseMMU::Mode mode, Request::Flags flags, RegVal val);
void addressTranslation64(MMU::ArmTranslationType tran_type,
BaseMMU::Mode mode, Request::Flags flags, RegVal val);
public:
SelfDebug*

View File

@@ -624,6 +624,21 @@ namespace Aarch64
TLBI_CASE_VARIANTS(MISCREG_TLBI_RVALE3OS)
return new Tlbi64ShareableHub(
machInst, miscReg, rt, dec.dvmEnabled);
// AT instructions
case MISCREG_AT_S1E1R_Xt:
case MISCREG_AT_S1E1W_Xt:
case MISCREG_AT_S1E0R_Xt:
case MISCREG_AT_S1E0W_Xt:
case MISCREG_AT_S1E2R_Xt:
case MISCREG_AT_S1E2W_Xt:
case MISCREG_AT_S12E1R_Xt:
case MISCREG_AT_S12E1W_Xt:
case MISCREG_AT_S12E0R_Xt:
case MISCREG_AT_S12E0W_Xt:
case MISCREG_AT_S1E3R_Xt:
case MISCREG_AT_S1E3W_Xt:
return new At64Hub(machInst, miscReg,
MiscRegIndex::MISCREG_PAR_EL1, rt);
default:
return new Msr64(machInst, miscReg, rt);
}

View File

@@ -401,6 +401,20 @@ let {{
exec_output += DvmInitiateAcc.subst(msrTlbiSIop)
exec_output += DvmCompleteAcc.subst(msrTlbiSIop)
atCode = msr_check_code + '''
uint64_t par_result;
std::tie(fault, par_result) = performAt(xc, flat_idx, XOp1);
MiscDest_ud = XOp1;
MiscDest2_ud = par_result;
'''
msrAtIop = ArmInstObjParams("msr", "At64Hub", "AtOp64",
{
"code" : atCode
})
header_output += AtDeclare.subst(msrAtIop)
decoder_output += AtConstructor.subst(msrAtIop)
exec_output += BasicExecute.subst(msrAtIop)
msrNZCVCode = '''
CPSR cpsr = XOp1;
CondCodesNZ = cpsr.nz;

View File

@@ -1,5 +1,5 @@
// -*- mode:c++ -*-
// Copyright (c) 2010-2014, 2016-2018, 2021-2022 Arm Limited
// Copyright (c) 2010-2014, 2016-2018, 2021-2022, 2024 Arm Limited
// All rights reserved
//
// The license below extends only to copyright in the software and shall
@@ -470,6 +470,7 @@ def operands {{
#Abstracted control reg operands
'MiscDest': CntrlReg('dest'),
'MiscDest2': CntrlReg('dest2'),
'MiscOp1': CntrlReg('op1'),
'MiscNsBankedDest': CntrlNsBankedReg('dest'),
'MiscNsBankedOp1': CntrlNsBankedReg('op1'),

View File

@@ -1,6 +1,6 @@
// -*- mode:c++ -*-
// Copyright (c) 2011,2017-2022 Arm Limited
// Copyright (c) 2011,2017-2022,2024 Arm Limited
// All rights reserved
//
// The license below extends only to copyright in the software and shall
@@ -303,6 +303,22 @@ class %(class_name)s : public %(base_class)s
};
}};
def template AtDeclare {{
class %(class_name)s : public %(base_class)s
{
private:
ArmISA::MiscRegIndex dest2;
%(reg_idx_arr_decl)s;
public:
// Constructor
%(class_name)s(ExtMachInst machInst, MiscRegIndex _dest,
MiscRegIndex _dest2, RegIndex _op1);
Fault execute(ExecContext *, trace::InstRecord *) const override;
};
}};
def template DvmDeclare {{
/**
* Static instruction class for "%(mnemonic)s".
@@ -339,6 +355,18 @@ def template DvmTlbiConstructor {{
}
}};
def template AtConstructor {{
%(class_name)s::%(class_name)s(ExtMachInst machInst, MiscRegIndex _dest,
MiscRegIndex _dest2, RegIndex _op1) :
%(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
_dest, _op1),
dest2(_dest2)
{
%(set_reg_idx_arr)s;
%(constructor)s;
}
}};
def template DvmConstructor {{
%(class_name)s::%(class_name)s(ExtMachInst machInst, bool dvm_enabled) :
%(base_class)s("%(mnemonic)s", machInst, %(op_class)s),