arch-arm: Implement FEAT_MPAM in CPU (#1082)
This PR implements FEAT_MPAM on the CPU side. We define a MPAM system registers and a mechanism for tagging memory requests with the MPAM information bundle as specified in existing documentation [1]. What this PR is *not* covering is the MPAM implementation in a MSC (Memory System Component). Which means at the moment it's only possible to have static partitioning schemes (via the PartitioningPolicies already part of gem5) and there is currently no way to dynamically program partitions at runtime. [1]: https://developer.arm.com/documentation/ddi0487/latest/
This commit is contained in:
@@ -170,6 +170,10 @@ class ArmISA(BaseISA):
|
||||
0x0000000000010010, "AArch64 Memory Model Feature Register 2"
|
||||
)
|
||||
|
||||
# HAS_SDEFLT | HAS_FORCE_NS | HAS_TIDR | PMG_MAX = 128 |
|
||||
# VPMR_MAX = 7 | HAS_HCR | PARTID_MAX = 256
|
||||
mpamidr_el1 = Param.UInt64(0x34000080001E0100, "MPAM ID Register (EL1)")
|
||||
|
||||
# Any access (read/write) to an unimplemented
|
||||
# Implementation Defined registers is not causing an Undefined Instruction.
|
||||
# It is rather executed as a NOP.
|
||||
|
||||
@@ -115,6 +115,7 @@ class ArmExtension(ScopedEnum):
|
||||
"LPAE",
|
||||
"VIRTUALIZATION",
|
||||
"TME",
|
||||
"FEAT_MPAM",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- mode:python -*-
|
||||
|
||||
# Copyright (c) 2009, 2012-2013, 2017-2018, 2020 ARM Limited
|
||||
# Copyright (c) 2009, 2012-2013, 2017-2018, 2020, 2024 Arm Limited
|
||||
# All rights reserved.
|
||||
#
|
||||
# The license below extends only to copyright in the software and shall
|
||||
@@ -134,6 +134,7 @@ SimObject('ArmCPU.py', sim_objects=[], tags='arm isa')
|
||||
|
||||
DebugFlag('Arm', tags='arm isa')
|
||||
DebugFlag('ArmTme', 'Transactional Memory Extension', tags='arm isa')
|
||||
DebugFlag('MPAM', 'MPAM debug flag', tags='arm isa')
|
||||
DebugFlag('PMUVerbose', "Performance Monitor", tags='arm isa')
|
||||
|
||||
# Add files generated by the ISA description.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2023 Arm Limited
|
||||
* Copyright (c) 2010-2024 Arm Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "arch/arm/mmu.hh"
|
||||
#include "arch/arm/pmu.hh"
|
||||
#include "arch/arm/regs/misc.hh"
|
||||
#include "arch/arm/regs/misc_accessors.hh"
|
||||
#include "arch/arm/self_debug.hh"
|
||||
#include "arch/arm/system.hh"
|
||||
#include "arch/arm/utility.hh"
|
||||
@@ -72,6 +73,8 @@ namespace gem5
|
||||
namespace ArmISA
|
||||
{
|
||||
|
||||
using namespace misc_regs;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@@ -106,6 +109,7 @@ ISA::ISA(const Params &p) : BaseISA(p, "arm"), system(NULL),
|
||||
// Cache system-level properties
|
||||
if (FullSystem && system) {
|
||||
highestELIs64 = system->highestELIs64();
|
||||
highestEL = system->highestEL();
|
||||
haveLargeAsid64 = system->haveLargeAsid64();
|
||||
physAddrRange = system->physAddrRange();
|
||||
sveVL = system->sveVL();
|
||||
@@ -114,6 +118,7 @@ ISA::ISA(const Params &p) : BaseISA(p, "arm"), system(NULL),
|
||||
release = system->releaseFS();
|
||||
} else {
|
||||
highestELIs64 = true; // ArmSystem::highestELIs64 does the same
|
||||
highestEL = EL1; // ArmSystem::highestEL does the same
|
||||
haveLargeAsid64 = false;
|
||||
physAddrRange = 32; // dummy value
|
||||
sveVL = p.sve_vl_se;
|
||||
@@ -267,6 +272,8 @@ ISA::redirectRegVHE(int misc_reg)
|
||||
return currEL() == EL2 ? MISCREG_CONTEXTIDR_EL2 : misc_reg;
|
||||
case MISCREG_CNTKCTL_EL1:
|
||||
return currEL() == EL2 ? MISCREG_CNTHCTL_EL2 : misc_reg;
|
||||
case MISCREG_MPAM1_EL1:
|
||||
return currEL() == EL2 ? MISCREG_MPAM2_EL2 : misc_reg;
|
||||
case MISCREG_CNTP_TVAL:
|
||||
case MISCREG_CNTP_TVAL_EL0:
|
||||
if (ELIsInHost(tc, currEL())) {
|
||||
@@ -356,6 +363,8 @@ ISA::redirectRegVHE(int misc_reg)
|
||||
return MISCREG_CONTEXTIDR_EL1;
|
||||
case MISCREG_CNTKCTL_EL12:
|
||||
return MISCREG_CNTKCTL_EL1;
|
||||
case MISCREG_MPAM1_EL12:
|
||||
return MISCREG_MPAM1_EL1;
|
||||
// _EL02 registers
|
||||
case MISCREG_CNTP_TVAL_EL02:
|
||||
return MISCREG_CNTP_TVAL_EL0;
|
||||
@@ -600,6 +609,23 @@ ISA::readMiscReg(RegIndex idx)
|
||||
case MISCREG_HIFAR: // alias for secure IFAR
|
||||
return readMiscRegNoEffect(MISCREG_IFAR_S);
|
||||
|
||||
case MISCREG_MPAM1_EL1:
|
||||
{
|
||||
MPAM mpam1 = readMiscRegNoEffect(MISCREG_MPAM1_EL1);
|
||||
mpam1.mpamEn = readRegisterNoEffect<MpamAccessor>(
|
||||
tc, highestEL).mpamEn;
|
||||
mpam1.el1.forcedNs = isSecure(tc) ?
|
||||
readRegisterNoEffect<MpamAccessor>(tc, EL3).el3.forceNs : 0;
|
||||
return mpam1;
|
||||
}
|
||||
case MISCREG_MPAM2_EL2:
|
||||
{
|
||||
MPAM mpam2 = readMiscRegNoEffect(MISCREG_MPAM2_EL2);
|
||||
mpam2.mpamEn = readRegisterNoEffect<MpamAccessor>(
|
||||
tc, highestEL).mpamEn;
|
||||
return mpam2;
|
||||
}
|
||||
|
||||
case MISCREG_RNDR:
|
||||
tc->setReg(cc_reg::Nz, (RegVal)0);
|
||||
tc->setReg(cc_reg::C, (RegVal)0);
|
||||
@@ -731,8 +757,8 @@ ISA::setMiscReg(RegIndex idx, RegVal val)
|
||||
|
||||
const uint32_t ones = (uint32_t)(-1);
|
||||
CPACR cpacrMask = 0;
|
||||
// Only cp10, cp11, and ase are implemented, nothing else should
|
||||
// be writable
|
||||
// Only cp10, cp11, and ase are implemented
|
||||
// nothing else should be writable
|
||||
cpacrMask.cp10 = ones;
|
||||
cpacrMask.cp11 = ones;
|
||||
cpacrMask.asedis = ones;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2012-2023 Arm Limited
|
||||
* Copyright (c) 2010, 2012-2024 Arm Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
@@ -90,6 +90,7 @@ namespace ArmISA
|
||||
|
||||
// Cached copies of system-level properties
|
||||
bool highestELIs64;
|
||||
ExceptionLevel highestEL;
|
||||
bool haveLargeAsid64;
|
||||
uint8_t physAddrRange;
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "arch/arm/mmu.hh"
|
||||
|
||||
#include "arch/arm/isa.hh"
|
||||
#include "arch/arm/mpam.hh"
|
||||
#include "arch/arm/reg_abi.hh"
|
||||
#include "arch/arm/stage2_lookup.hh"
|
||||
#include "arch/arm/table_walker.hh"
|
||||
@@ -208,6 +209,8 @@ MMU::testAndFinalize(const RequestPtr &req,
|
||||
// is not enabled
|
||||
auto domain = te ? te-> domain : TlbEntry::DomainType::NoAccess;
|
||||
|
||||
mpam::tagRequest(tc, req, mode == Execute);
|
||||
|
||||
// Check for a tester generated address fault
|
||||
Fault fault = testTranslation(req, mode, domain, state);
|
||||
if (fault != NoFault) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ARM Limited
|
||||
* Copyright (c) 2024 Arm Limited
|
||||
* All rights reserved.
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
@@ -35,8 +35,20 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "arch/arm/mpam.hh"
|
||||
|
||||
#include "arch/arm/regs/misc.hh"
|
||||
#include "arch/arm/regs/misc_accessors.hh"
|
||||
#include "arch/arm/regs/misc_types.hh"
|
||||
#include "arch/arm/system.hh"
|
||||
#include "arch/arm/types.hh"
|
||||
#include "arch/arm/utility.hh"
|
||||
#include "cpu/base.hh"
|
||||
#include "cpu/thread_context.hh"
|
||||
#include "debug/MPAM.hh"
|
||||
|
||||
namespace gem5::ArmISA::mpam
|
||||
{
|
||||
|
||||
@@ -58,6 +70,12 @@ PartitionFieldExtension::getPartitionMonitoringID() const
|
||||
return this->_partitionMonitoringID;
|
||||
}
|
||||
|
||||
bool
|
||||
PartitionFieldExtension::getMpamNS() const
|
||||
{
|
||||
return this->_ns;
|
||||
}
|
||||
|
||||
void
|
||||
PartitionFieldExtension::setPartitionID(uint64_t id)
|
||||
{
|
||||
@@ -70,4 +88,180 @@ PartitionFieldExtension::setPartitionMonitoringID(uint64_t id)
|
||||
this->_partitionMonitoringID = id;
|
||||
}
|
||||
|
||||
void
|
||||
PartitionFieldExtension::setMpamNS(bool ns)
|
||||
{
|
||||
this->_ns = ns;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace misc_regs;
|
||||
|
||||
static PartID
|
||||
getPARTID(ThreadContext *tc, ExceptionLevel el, bool ind)
|
||||
{
|
||||
MPAM reg = readRegister<MpamAccessor>(tc, el);
|
||||
return ind ? reg.partidI : reg.partidD;
|
||||
}
|
||||
|
||||
static PMG
|
||||
getPMG(ThreadContext *tc, ExceptionLevel el, bool ind)
|
||||
{
|
||||
MPAM reg = readRegister<MpamAccessor>(tc, el);
|
||||
return ind ? reg.pmgI : reg.pmgD;
|
||||
}
|
||||
|
||||
static bool
|
||||
useVirtualPartitions(ThreadContext *tc, ExceptionLevel el, MPAMIDR mpamidr)
|
||||
{
|
||||
const MPAMHCR mpamhcr = tc->readMiscReg(MISCREG_MPAMHCR_EL2);
|
||||
return mpamidr.hasHcr && EL2Enabled(tc) &&
|
||||
((el == EL0 && !ELIsInHost(tc, EL0) && mpamhcr.el0Vpmen) || // EL0 case
|
||||
(el == EL1 && mpamhcr.el1Vpmen)); // EL1 case
|
||||
}
|
||||
|
||||
static PartID
|
||||
mapVpmv(ThreadContext *tc, PartID vpartid)
|
||||
{
|
||||
uint8_t reg_index = vpartid / 4;
|
||||
uint8_t reg_field = vpartid % 4;
|
||||
|
||||
// Register field size in bits
|
||||
size_t reg_field_size = sizeof(PartID) * 8;
|
||||
|
||||
// LSB of the register field (Every field is 16bits)
|
||||
uint8_t lsb = reg_field * reg_field_size;
|
||||
uint8_t msb = lsb + reg_field_size - 1;
|
||||
|
||||
const RegVal vpmv = tc->readMiscReg(MISCREG_MPAMVPM0_EL2 + reg_index);
|
||||
return bits(vpmv, msb ,lsb);
|
||||
}
|
||||
|
||||
static std::optional<PartID>
|
||||
virtToPhysPart(ThreadContext *tc, PartID vpartid, MPAMIDR mpamidr)
|
||||
{
|
||||
// vpmrMax refers to the register index. Extract vpartid max
|
||||
const uint8_t vpartid_max = (mpamidr.vpmrMax << 2) + 3;
|
||||
const RegVal mpam_vpmv = tc->readMiscReg(MISCREG_MPAMVPMV_EL2);
|
||||
|
||||
if (vpartid > vpartid_max) {
|
||||
vpartid = vpartid % (vpartid_max + 1);
|
||||
}
|
||||
|
||||
PartID phys_partid = 0;
|
||||
if (bits(mpam_vpmv, vpartid)) {
|
||||
// Valid mapping entry for virtual partition vpartid
|
||||
phys_partid = mapVpmv(tc, vpartid);
|
||||
} else if (bits(mpam_vpmv, 0)) {
|
||||
// Default virtual partition valid
|
||||
phys_partid = mapVpmv(tc, 0);
|
||||
} else {
|
||||
// Error
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return phys_partid > mpamidr.partidMax ?
|
||||
std::nullopt : std::make_optional(phys_partid);
|
||||
}
|
||||
|
||||
static std::optional<PartID>
|
||||
genPARTID(ThreadContext *tc, ExceptionLevel el, bool ind)
|
||||
{
|
||||
const MPAMIDR mpamidr = tc->readMiscReg(MISCREG_MPAMIDR_EL1);
|
||||
auto partid = getPARTID(tc, el, ind);
|
||||
|
||||
if (partid > mpamidr.partidMax) {
|
||||
return std::nullopt;
|
||||
} else if (useVirtualPartitions(tc, el, mpamidr)) {
|
||||
return virtToPhysPart(tc, partid, mpamidr);
|
||||
} else {
|
||||
return partid;
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<PMG>
|
||||
genPMG(ThreadContext *tc, ExceptionLevel el, bool ind)
|
||||
{
|
||||
const MPAMIDR mpamidr = tc->readMiscReg(MISCREG_MPAMIDR_EL1);
|
||||
PMG pgroup = getPMG(tc, el, ind);
|
||||
return pgroup > mpamidr.pmgMax ? std::nullopt : std::make_optional(pgroup);
|
||||
}
|
||||
|
||||
static bool
|
||||
isEnabled(ThreadContext *tc)
|
||||
{
|
||||
MPAM reg = readRegister<MpamAccessor>(tc, ArmSystem::highestEL(tc));
|
||||
return reg.mpamEn;
|
||||
}
|
||||
|
||||
static std::shared_ptr<PartitionFieldExtension>
|
||||
genExtensionDefault()
|
||||
{
|
||||
// tag with partID data
|
||||
auto ext = std::make_shared<PartitionFieldExtension>();
|
||||
ext->setPartitionID(DEFAULT_PARTITION_ID);
|
||||
ext->setPartitionMonitoringID(DEFAULT_PARTITION_MONITORING_ID);
|
||||
return ext;
|
||||
}
|
||||
|
||||
static std::shared_ptr<PartitionFieldExtension>
|
||||
genExtension(ThreadContext *tc, bool ind)
|
||||
{
|
||||
ExceptionLevel curr_el = currEL(tc);
|
||||
const MPAMHCR mpamhcr = tc->readMiscReg(MISCREG_MPAMHCR_EL2);
|
||||
const HCR hcr = tc->readMiscReg(MISCREG_HCR_EL2);
|
||||
|
||||
bool gstplk = curr_el == EL0 && EL2Enabled(tc) &&
|
||||
mpamhcr.gstappPlk && !hcr.tge;
|
||||
if (gstplk) {
|
||||
curr_el = EL1;
|
||||
}
|
||||
|
||||
// tag with partID data
|
||||
auto ext = std::make_shared<
|
||||
PartitionFieldExtension>();
|
||||
|
||||
auto part_id = genPARTID(tc, curr_el, ind).value_or(
|
||||
DEFAULT_PARTITION_ID);
|
||||
auto part_mon_id = genPMG(tc, curr_el, ind).value_or(
|
||||
DEFAULT_PARTITION_MONITORING_ID);
|
||||
|
||||
ext->setPartitionID(part_id);
|
||||
ext->setPartitionMonitoringID(part_mon_id);
|
||||
return ext;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
tagRequest(ThreadContext *tc, const RequestPtr &req, bool ind)
|
||||
{
|
||||
if (!HaveExt(tc, ArmExtension::FEAT_MPAM) || !isEnabled(tc))
|
||||
return;
|
||||
|
||||
const MPAMIDR mpamidr = tc->readMiscReg(MISCREG_MPAMIDR_EL1);
|
||||
const MPAM mpam3 = tc->readMiscReg(MISCREG_MPAM3_EL3);
|
||||
auto ext = mpamidr.hasSdeflt && mpam3.el3.sdeflt && isSecure(tc) ?
|
||||
genExtensionDefault() :
|
||||
genExtension(tc, ind);
|
||||
|
||||
DPRINTFS(MPAM, tc->getCpuPtr(),
|
||||
"MPAM Tagging req %#x => PART_ID: %d, PART_MON_ID: %d\n",
|
||||
req->getPaddr(),
|
||||
ext->getPartitionID(),
|
||||
ext->getPartitionMonitoringID());
|
||||
|
||||
bool mpam_ns = !isSecure(tc);
|
||||
if (!mpam_ns && mpam3.el3.forceNs) {
|
||||
mpam_ns = true;
|
||||
}
|
||||
|
||||
ext->setMpamNS(mpam_ns);
|
||||
|
||||
req->setExtension(ext);
|
||||
}
|
||||
|
||||
} // namespace gem5::ArmISA::mpam
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ARM Limited
|
||||
* Copyright (c) 2024 Arm Limited
|
||||
* All rights reserved.
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
@@ -67,6 +67,13 @@ class PartitionFieldExtension : public Extension<Request,
|
||||
*/
|
||||
uint64_t getPartitionMonitoringID() const;
|
||||
|
||||
/**
|
||||
* MPAM_NS getter
|
||||
* @return True if targeting Non-Secure MPAM partition
|
||||
*/
|
||||
bool getMpamNS() const;
|
||||
|
||||
|
||||
/**
|
||||
* _partitionID setter
|
||||
* @param id Partition ID to set for the extension
|
||||
@@ -79,11 +86,31 @@ class PartitionFieldExtension : public Extension<Request,
|
||||
*/
|
||||
void setPartitionMonitoringID(uint64_t id);
|
||||
|
||||
/**
|
||||
* MPAM_NS setter
|
||||
* @param ns True if targeting Non-Secure MPAM partition
|
||||
*/
|
||||
void setMpamNS(bool ns);
|
||||
|
||||
private:
|
||||
uint64_t _partitionID = DEFAULT_PARTITION_ID;
|
||||
uint64_t _partitionMonitoringID = DEFAULT_PARTITION_MONITORING_ID;
|
||||
bool _ns = true;
|
||||
};
|
||||
|
||||
/** Partition ID data type */
|
||||
using PartID = uint16_t;
|
||||
/** Partition Manager data type */
|
||||
using PMG = uint8_t;
|
||||
|
||||
/** Tag a memory request with MPAM information
|
||||
* @param tc pointer to the ThreadContext
|
||||
* @param req reference to the pointer of the memory request to be tagged
|
||||
* @param ind "instruction not data" to differentiate between
|
||||
* a fetch request and a data memory access
|
||||
*/
|
||||
void tagRequest(ThreadContext *tc, const RequestPtr &req, bool ind);
|
||||
|
||||
} // namespace gem5::ArmISA::mpam
|
||||
|
||||
#endif // __ARCH_ARM_MPAM_HH__
|
||||
|
||||
@@ -41,11 +41,12 @@
|
||||
|
||||
#include "arch/arm/insts/misc64.hh"
|
||||
#include "arch/arm/isa.hh"
|
||||
#include "base/bitfield.hh"
|
||||
#include "base/logging.hh"
|
||||
#include "cpu/thread_context.hh"
|
||||
#include "dev/arm/gic_v3_cpu_interface.hh"
|
||||
#include "sim/full_system.hh"
|
||||
#include "params/ArmISA.hh"
|
||||
#include "sim/full_system.hh"
|
||||
|
||||
namespace gem5
|
||||
{
|
||||
@@ -735,7 +736,6 @@ checkFaultAccessAArch64SysReg(MiscRegIndex reg, CPSR cpsr,
|
||||
std::vector<struct MiscRegLUTEntry> lookUpMiscReg(NUM_MISCREGS);
|
||||
|
||||
namespace {
|
||||
|
||||
// The map is translating a MiscRegIndex into AArch64 system register
|
||||
// numbers (op0, op1, crn, crm, op2)
|
||||
std::unordered_map<MiscRegIndex, MiscRegNum64> idxToMiscRegNum;
|
||||
@@ -1032,6 +1032,9 @@ std::unordered_map<MiscRegNum64, MiscRegIndex> miscRegNumToIdx{
|
||||
{ MiscRegNum64(3, 0, 9, 14, 2), MISCREG_PMINTENCLR_EL1 },
|
||||
{ MiscRegNum64(3, 0, 10, 2, 0), MISCREG_MAIR_EL1 },
|
||||
{ MiscRegNum64(3, 0, 10, 3, 0), MISCREG_AMAIR_EL1 },
|
||||
{ MiscRegNum64(3, 0, 10, 4, 4), MISCREG_MPAMIDR_EL1 },
|
||||
{ MiscRegNum64(3, 0, 10, 5, 0), MISCREG_MPAM1_EL1 },
|
||||
{ MiscRegNum64(3, 0, 10, 5, 1), MISCREG_MPAM0_EL1 },
|
||||
{ MiscRegNum64(3, 0, 10, 5, 3), MISCREG_MPAMSM_EL1 },
|
||||
{ MiscRegNum64(3, 0, 12, 0, 0), MISCREG_VBAR_EL1 },
|
||||
{ MiscRegNum64(3, 0, 12, 0, 1), MISCREG_RVBAR_EL1 },
|
||||
@@ -1181,6 +1184,17 @@ std::unordered_map<MiscRegNum64, MiscRegIndex> miscRegNumToIdx{
|
||||
{ MiscRegNum64(3, 4, 6, 0, 4), MISCREG_HPFAR_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 2, 0), MISCREG_MAIR_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 3, 0), MISCREG_AMAIR_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 4, 0), MISCREG_MPAMHCR_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 4, 1), MISCREG_MPAMVPMV_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 5, 0), MISCREG_MPAM2_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 6, 0), MISCREG_MPAMVPM0_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 6, 1), MISCREG_MPAMVPM1_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 6, 2), MISCREG_MPAMVPM2_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 6, 3), MISCREG_MPAMVPM3_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 6, 4), MISCREG_MPAMVPM4_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 6, 5), MISCREG_MPAMVPM5_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 6, 6), MISCREG_MPAMVPM6_EL2 },
|
||||
{ MiscRegNum64(3, 4, 10, 6, 7), MISCREG_MPAMVPM7_EL2 },
|
||||
{ MiscRegNum64(3, 4, 12, 0, 0), MISCREG_VBAR_EL2 },
|
||||
{ MiscRegNum64(3, 4, 12, 0, 1), MISCREG_RVBAR_EL2 },
|
||||
{ MiscRegNum64(3, 4, 12, 1, 1), MISCREG_VDISR_EL2 },
|
||||
@@ -1248,6 +1262,7 @@ std::unordered_map<MiscRegNum64, MiscRegIndex> miscRegNumToIdx{
|
||||
{ MiscRegNum64(3, 5, 6, 0, 0), MISCREG_FAR_EL12 },
|
||||
{ MiscRegNum64(3, 5, 10, 2, 0), MISCREG_MAIR_EL12 },
|
||||
{ MiscRegNum64(3, 5, 10, 3, 0), MISCREG_AMAIR_EL12 },
|
||||
{ MiscRegNum64(3, 5, 10, 5, 0), MISCREG_MPAM1_EL12 },
|
||||
{ MiscRegNum64(3, 5, 12, 0, 0), MISCREG_VBAR_EL12 },
|
||||
{ MiscRegNum64(3, 5, 13, 0, 1), MISCREG_CONTEXTIDR_EL12 },
|
||||
{ MiscRegNum64(3, 5, 14, 1, 0), MISCREG_CNTKCTL_EL12 },
|
||||
@@ -1277,6 +1292,7 @@ std::unordered_map<MiscRegNum64, MiscRegIndex> miscRegNumToIdx{
|
||||
{ MiscRegNum64(3, 6, 6, 0, 0), MISCREG_FAR_EL3 },
|
||||
{ MiscRegNum64(3, 6, 10, 2, 0), MISCREG_MAIR_EL3 },
|
||||
{ MiscRegNum64(3, 6, 10, 3, 0), MISCREG_AMAIR_EL3 },
|
||||
{ MiscRegNum64(3, 6, 10, 5, 0), MISCREG_MPAM3_EL3 },
|
||||
{ MiscRegNum64(3, 6, 12, 0, 0), MISCREG_VBAR_EL3 },
|
||||
{ MiscRegNum64(3, 6, 12, 0, 1), MISCREG_RVBAR_EL3 },
|
||||
{ MiscRegNum64(3, 6, 12, 0, 2), MISCREG_RMR_EL3 },
|
||||
@@ -2620,6 +2636,113 @@ faultIdst(const MiscRegLUTEntry &entry,
|
||||
}
|
||||
}
|
||||
|
||||
Fault
|
||||
faultMpamIdrEL1(const MiscRegLUTEntry &entry,
|
||||
ThreadContext *tc, const MiscRegOp64 &inst)
|
||||
{
|
||||
if (HaveExt(tc, ArmExtension::FEAT_MPAM)) {
|
||||
MPAM mpam2 = tc->readMiscReg(MISCREG_MPAM2_EL2);
|
||||
MPAM mpam3 = tc->readMiscReg(MISCREG_MPAM3_EL3);
|
||||
MPAMIDR mpamidr = tc->readMiscReg(MISCREG_MPAMIDR_EL1);
|
||||
MPAMHCR mpamhcr = tc->readMiscReg(MISCREG_MPAMHCR_EL2);
|
||||
if (ArmSystem::haveEL(tc, EL3) && mpam3.el3.trapLower) {
|
||||
return inst.generateTrap(EL3);
|
||||
} else if (EL2Enabled(tc) && mpamidr.hasHcr && mpamhcr.trapMpamIdrEL1) {
|
||||
return inst.generateTrap(EL2);
|
||||
} else if (EL2Enabled(tc) && mpamidr.hasTidr && mpam2.el2.tidr) {
|
||||
return inst.generateTrap(EL2);
|
||||
} else {
|
||||
return NoFault;
|
||||
}
|
||||
} else {
|
||||
return inst.undefined();
|
||||
}
|
||||
}
|
||||
|
||||
Fault
|
||||
faultMpam0EL1(const MiscRegLUTEntry &entry,
|
||||
ThreadContext *tc, const MiscRegOp64 &inst)
|
||||
{
|
||||
if (HaveExt(tc, ArmExtension::FEAT_MPAM)) {
|
||||
MPAM mpam2 = tc->readMiscReg(MISCREG_MPAM2_EL2);
|
||||
MPAM mpam3 = tc->readMiscReg(MISCREG_MPAM3_EL3);
|
||||
if (ArmSystem::haveEL(tc, EL3) && mpam3.el3.trapLower) {
|
||||
return inst.generateTrap(EL3);
|
||||
} else if (EL2Enabled(tc) && mpam2.el2.trapMpam0EL1) {
|
||||
return inst.generateTrap(EL2);
|
||||
} else {
|
||||
return NoFault;
|
||||
}
|
||||
} else {
|
||||
return inst.undefined();
|
||||
}
|
||||
}
|
||||
|
||||
Fault
|
||||
faultMpam1EL1(const MiscRegLUTEntry &entry,
|
||||
ThreadContext *tc, const MiscRegOp64 &inst)
|
||||
{
|
||||
if (HaveExt(tc, ArmExtension::FEAT_MPAM)) {
|
||||
MPAM mpam2 = tc->readMiscReg(MISCREG_MPAM2_EL2);
|
||||
MPAM mpam3 = tc->readMiscReg(MISCREG_MPAM3_EL3);
|
||||
if (ArmSystem::haveEL(tc, EL3) && mpam3.el3.trapLower) {
|
||||
return inst.generateTrap(EL3);
|
||||
} else if (EL2Enabled(tc) && mpam2.el2.trapMpam1EL1) {
|
||||
return inst.generateTrap(EL2);
|
||||
} else {
|
||||
return NoFault;
|
||||
}
|
||||
} else {
|
||||
return inst.undefined();
|
||||
}
|
||||
}
|
||||
|
||||
Fault
|
||||
faultMpamEL2(const MiscRegLUTEntry &entry,
|
||||
ThreadContext *tc, const MiscRegOp64 &inst)
|
||||
{
|
||||
if (HaveExt(tc, ArmExtension::FEAT_MPAM)) {
|
||||
MPAM mpam3 = tc->readMiscReg(MISCREG_MPAM3_EL3);
|
||||
if (ArmSystem::haveEL(tc, EL3) && mpam3.el3.trapLower) {
|
||||
return inst.generateTrap(EL3);
|
||||
} else {
|
||||
return NoFault;
|
||||
}
|
||||
} else {
|
||||
return inst.undefined();
|
||||
}
|
||||
}
|
||||
|
||||
Fault
|
||||
faultMpam12EL2(const MiscRegLUTEntry &entry,
|
||||
ThreadContext *tc, const MiscRegOp64 &inst)
|
||||
{
|
||||
if (ELIsInHost(tc, EL2)) {
|
||||
return faultMpamEL2(entry, tc, inst);
|
||||
} else {
|
||||
return inst.undefined();
|
||||
}
|
||||
}
|
||||
|
||||
Fault
|
||||
faultMpamsmEL1(const MiscRegLUTEntry &entry,
|
||||
ThreadContext *tc, const MiscRegOp64 &inst)
|
||||
{
|
||||
if (HaveExt(tc, ArmExtension::FEAT_MPAM)) {
|
||||
MPAM mpam2 = tc->readMiscReg(MISCREG_MPAM2_EL2);
|
||||
MPAM mpam3 = tc->readMiscReg(MISCREG_MPAM3_EL3);
|
||||
if (ArmSystem::haveEL(tc, EL3) && mpam3.el3.trapLower) {
|
||||
return inst.generateTrap(EL3);
|
||||
} else if (EL2Enabled(tc) && mpam2.el2.enMpamSm) {
|
||||
return inst.generateTrap(EL2);
|
||||
} else {
|
||||
return NoFault;
|
||||
}
|
||||
} else {
|
||||
return inst.undefined();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MiscRegIndex
|
||||
@@ -4567,10 +4690,14 @@ ISA::initializeMiscRegMetadata()
|
||||
AA64PFR0 pfr0_el1 = 0;
|
||||
pfr0_el1.el0 = 0x2;
|
||||
pfr0_el1.el1 = 0x2;
|
||||
pfr0_el1.el2 = release->has(ArmExtension::VIRTUALIZATION) ? 0x2 : 0x0;
|
||||
pfr0_el1.el2 = release->has(ArmExtension::VIRTUALIZATION)
|
||||
? 0x2 : 0x0;
|
||||
pfr0_el1.el3 = release->has(ArmExtension::SECURITY) ? 0x2 : 0x0;
|
||||
pfr0_el1.sve = release->has(ArmExtension::FEAT_SVE) ? 0x1 : 0x0;
|
||||
pfr0_el1.sel2 = release->has(ArmExtension::FEAT_SEL2) ? 0x1 : 0x0;
|
||||
// See MPAM frac in MISCREG_ID_AA64PFR1_EL1. Currently supporting
|
||||
// MPAMv0p1
|
||||
pfr0_el1.mpam = 0x0;
|
||||
pfr0_el1.gic = FullSystem && getGICv3CPUInterface(tc) ? 0x1 : 0;
|
||||
return pfr0_el1;
|
||||
}())
|
||||
@@ -4579,8 +4706,13 @@ ISA::initializeMiscRegMetadata()
|
||||
.faultRead(EL1, faultHcrEL1<&HCR::tid3>)
|
||||
.allPrivileges().writes(0);
|
||||
InitReg(MISCREG_ID_AA64PFR1_EL1)
|
||||
.reset(release->has(ArmExtension::FEAT_SME) ?
|
||||
0x1 << 24 : 0)
|
||||
.reset([release=release](){
|
||||
AA64PFR1 pfr1_el1 = 0;
|
||||
pfr1_el1.sme = release->has(ArmExtension::FEAT_SME) ? 0x1 : 0x0;
|
||||
pfr1_el1.mpamFrac = release->has(ArmExtension::FEAT_MPAM) ?
|
||||
0x1 : 0x0;
|
||||
return pfr1_el1;
|
||||
}())
|
||||
.unserialize(0)
|
||||
.faultRead(EL0, faultIdst)
|
||||
.faultRead(EL1, faultHcrEL1<&HCR::tid3>)
|
||||
@@ -6314,8 +6446,6 @@ ISA::initializeMiscRegMetadata()
|
||||
.allPrivileges().exceptUserMode();
|
||||
InitReg(MISCREG_TPIDR2_EL0)
|
||||
.allPrivileges();
|
||||
InitReg(MISCREG_MPAMSM_EL1)
|
||||
.allPrivileges().exceptUserMode();
|
||||
|
||||
InitReg(MISCREG_RNDR)
|
||||
.faultRead(EL0, faultRng)
|
||||
@@ -6396,6 +6526,73 @@ ISA::initializeMiscRegMetadata()
|
||||
.warnNotFail()
|
||||
.fault(faultUnimplemented);
|
||||
|
||||
// MPAM extension
|
||||
InitReg(MISCREG_MPAMIDR_EL1)
|
||||
.reset(p.mpamidr_el1)
|
||||
.res0(mask(63, 62) | mask(56, 40) | mask(31, 21) | mask(16, 16))
|
||||
.faultRead(EL1, faultMpamIdrEL1)
|
||||
.faultRead(EL2, faultMpamEL2)
|
||||
.allPrivileges().exceptUserMode().writes(0);
|
||||
InitReg(MISCREG_MPAM0_EL1)
|
||||
.res0(mask(63, 48))
|
||||
.fault(EL1, faultMpam0EL1)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.priv().hyp().mon();
|
||||
InitReg(MISCREG_MPAM1_EL1)
|
||||
.res0(mask(62, 61) | mask(59, 48))
|
||||
.fault(EL1, faultMpam1EL1)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.priv().hyp().mon();
|
||||
InitReg(MISCREG_MPAM1_EL12)
|
||||
.res0(mask(59, 48))
|
||||
.fault(EL2, faultMpam12EL2)
|
||||
.fault(EL3, defaultFaultE2H_EL3)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAM2_EL2)
|
||||
.res0(mask(62, 59) | mask(57, 50))
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMHCR_EL2)
|
||||
.res0(mask(63, 32) | mask(30, 9) | mask(7, 2))
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMVPM0_EL2)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMVPM1_EL2)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMVPM2_EL2)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMVPM3_EL2)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMVPM4_EL2)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMVPM5_EL2)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMVPM6_EL2)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMVPM7_EL2)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAMVPMV_EL2)
|
||||
.res0(mask(63, 32))
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.hyp().mon();
|
||||
InitReg(MISCREG_MPAM3_EL3)
|
||||
.res0(mask(59, 48))
|
||||
.mon();
|
||||
InitReg(MISCREG_MPAMSM_EL1)
|
||||
.res0(mask(63, 48) | mask(39, 32) | mask(15, 0))
|
||||
.fault(EL1, faultMpamsmEL1)
|
||||
.fault(EL2, faultMpamEL2)
|
||||
.allPrivileges().exceptUserMode();
|
||||
|
||||
// Register mappings for some unimplemented registers:
|
||||
// ESR_EL1 -> DFSR
|
||||
// RMR_EL1 -> RMR
|
||||
|
||||
@@ -1142,6 +1142,24 @@ namespace ArmISA
|
||||
MISCREG_HDFGRTR_EL2,
|
||||
MISCREG_HDFGWTR_EL2,
|
||||
|
||||
// FEAT_MPAM
|
||||
MISCREG_MPAMIDR_EL1,
|
||||
MISCREG_MPAM0_EL1,
|
||||
MISCREG_MPAM1_EL1,
|
||||
MISCREG_MPAM2_EL2,
|
||||
MISCREG_MPAM3_EL3,
|
||||
MISCREG_MPAM1_EL12,
|
||||
MISCREG_MPAMHCR_EL2,
|
||||
MISCREG_MPAMVPMV_EL2,
|
||||
MISCREG_MPAMVPM0_EL2,
|
||||
MISCREG_MPAMVPM1_EL2,
|
||||
MISCREG_MPAMVPM2_EL2,
|
||||
MISCREG_MPAMVPM3_EL2,
|
||||
MISCREG_MPAMVPM4_EL2,
|
||||
MISCREG_MPAMVPM5_EL2,
|
||||
MISCREG_MPAMVPM6_EL2,
|
||||
MISCREG_MPAMVPM7_EL2,
|
||||
|
||||
// NUM_PHYS_MISCREGS specifies the number of actual physical
|
||||
// registers, not considering the following pseudo-registers
|
||||
// (dummy registers), like MISCREG_UNKNOWN, MISCREG_IMPDEF_UNIMPL.
|
||||
@@ -2859,6 +2877,24 @@ namespace ArmISA
|
||||
"hdfgrtr_el2",
|
||||
"hdfgwtr_el2",
|
||||
|
||||
// FEAT_MPAM
|
||||
"mpamidr_el1",
|
||||
"mpam0_el1",
|
||||
"mpam1_el1",
|
||||
"mpam2_el2",
|
||||
"mpam3_el3",
|
||||
"mpam1_el12",
|
||||
"mpamhcr_el2",
|
||||
"mpamvpmv_el2",
|
||||
"mpamvpm0_el2",
|
||||
"mpamvpm1_el2",
|
||||
"mpamvpm2_el2",
|
||||
"mpamvpm3_el2",
|
||||
"mpamvpm4_el2",
|
||||
"mpamvpm5_el2",
|
||||
"mpamvpm6_el2",
|
||||
"mpamvpm7_el2",
|
||||
|
||||
"num_phys_regs",
|
||||
|
||||
// Dummy registers
|
||||
|
||||
@@ -59,6 +59,15 @@ struct FarAccessor
|
||||
static const MiscRegIndex el3 = MISCREG_FAR_EL3;
|
||||
};
|
||||
|
||||
struct MpamAccessor
|
||||
{
|
||||
using type = MPAM;
|
||||
static const MiscRegIndex el0 = MISCREG_MPAM0_EL1;
|
||||
static const MiscRegIndex el1 = MISCREG_MPAM1_EL1;
|
||||
static const MiscRegIndex el2 = MISCREG_MPAM2_EL2;
|
||||
static const MiscRegIndex el3 = MISCREG_MPAM3_EL3;
|
||||
};
|
||||
|
||||
template <typename RegAccessor>
|
||||
MiscRegIndex
|
||||
getRegVersion(ExceptionLevel el)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2023 Arm Limited
|
||||
* Copyright (c) 2010-2024 Arm Limited
|
||||
* All rights reserved
|
||||
*
|
||||
* The license below extends only to copyright in the software and shall
|
||||
@@ -226,6 +226,11 @@ namespace ArmISA
|
||||
Bitfield<3, 0> el0;
|
||||
EndBitUnion(AA64PFR0)
|
||||
|
||||
BitUnion64(AA64PFR1)
|
||||
Bitfield<27, 24> sme;
|
||||
Bitfield<19, 16> mpamFrac;
|
||||
EndBitUnion(AA64PFR1)
|
||||
|
||||
BitUnion64(AA64ZFR0)
|
||||
Bitfield<59, 56> f64mm;
|
||||
Bitfield<55, 52> f32mm;
|
||||
@@ -1085,6 +1090,53 @@ namespace ArmISA
|
||||
Bitfield<14> tcr2En;
|
||||
EndBitUnion(HCRX)
|
||||
|
||||
BitUnion64(MPAMIDR)
|
||||
Bitfield<61> hasSdeflt;
|
||||
Bitfield<60> hasForceNs;
|
||||
Bitfield<58> hasTidr;
|
||||
Bitfield<39,32> pmgMax;
|
||||
Bitfield<20,18> vpmrMax;
|
||||
Bitfield<17> hasHcr;
|
||||
Bitfield<15,0> partidMax;
|
||||
EndBitUnion(MPAMIDR)
|
||||
|
||||
// Generic view of MPAMx_ELy
|
||||
BitUnion64(MPAM)
|
||||
Bitfield<63> mpamEn;
|
||||
|
||||
// MPAM1_EL1 only
|
||||
SubBitUnion(el1, 62, 48)
|
||||
Bitfield<60> forcedNs;
|
||||
EndSubBitUnion(el1)
|
||||
|
||||
// MPAM2_EL2 only
|
||||
SubBitUnion(el2, 62, 48)
|
||||
Bitfield<58> tidr;
|
||||
Bitfield<50> enMpamSm;
|
||||
Bitfield<49> trapMpam0EL1;
|
||||
Bitfield<48> trapMpam1EL1;
|
||||
EndSubBitUnion(el2)
|
||||
|
||||
// MPAM3_EL3 only
|
||||
SubBitUnion(el3, 62, 48)
|
||||
Bitfield<62> trapLower;
|
||||
Bitfield<61> sdeflt;
|
||||
Bitfield<60> forceNs;
|
||||
EndSubBitUnion(el3)
|
||||
|
||||
Bitfield<47,40> pmgD;
|
||||
Bitfield<39,32> pmgI;
|
||||
Bitfield<31,16> partidD;
|
||||
Bitfield<15,0> partidI;
|
||||
EndBitUnion(MPAM)
|
||||
|
||||
BitUnion64(MPAMHCR)
|
||||
Bitfield<31> trapMpamIdrEL1;
|
||||
Bitfield<8> gstappPlk;
|
||||
Bitfield<1> el1Vpmen;
|
||||
Bitfield<0> el0Vpmen;
|
||||
EndBitUnion(MPAMHCR)
|
||||
|
||||
} // namespace ArmISA
|
||||
} // namespace gem5
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
#include "arch/arm/faults.hh"
|
||||
#include "arch/arm/mmu.hh"
|
||||
#include "arch/arm/mpam.hh"
|
||||
#include "arch/arm/pagetable.hh"
|
||||
#include "arch/arm/system.hh"
|
||||
#include "arch/arm/tlb.hh"
|
||||
@@ -2114,6 +2115,8 @@ TableWalker::fetchDescriptor(Addr desc_addr,
|
||||
desc_addr, num_bytes, flags, requestorId);
|
||||
req->taskId(context_switch_task_id::DMA);
|
||||
|
||||
mpamTagTableWalk(req);
|
||||
|
||||
Fault fault = testWalk(req, descriptor.domain(),
|
||||
lookup_level);
|
||||
|
||||
@@ -2387,6 +2390,12 @@ TableWalker::readDataUntimed(ThreadContext *tc, Addr vaddr, Addr desc_addr,
|
||||
return fault;
|
||||
}
|
||||
|
||||
void
|
||||
TableWalker::mpamTagTableWalk(RequestPtr &req) const
|
||||
{
|
||||
mpam::tagRequest(currState->tc, req, currState->isFetch);
|
||||
}
|
||||
|
||||
void
|
||||
TableWalker::readDataTimed(ThreadContext *tc, Addr desc_addr,
|
||||
Stage2Walk *translation, int num_bytes,
|
||||
|
||||
@@ -1201,6 +1201,8 @@ class TableWalker : public ClockedObject
|
||||
|
||||
static uint8_t pageSizeNtoStatBin(uint8_t N);
|
||||
|
||||
void mpamTagTableWalk(RequestPtr &req) const;
|
||||
|
||||
public: /* Testing */
|
||||
TlbTestInterface *test;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user