diff --git a/src/arch/arm/ArmISA.py b/src/arch/arm/ArmISA.py index e7f215822c..8b9cf25b25 100644 --- a/src/arch/arm/ArmISA.py +++ b/src/arch/arm/ArmISA.py @@ -170,9 +170,9 @@ class ArmISA(BaseISA): 0x0000000000010010, "AArch64 Memory Model Feature Register 2" ) - # HAS_SDEFLT | HAS_FORCE_NS | HAS_TIDR | PMG_MAX = 64 | + # HAS_SDEFLT | HAS_FORCE_NS | HAS_TIDR | PMG_MAX = 128 | # VPMR_MAX = 7 | HAS_HCR | PARTID_MAX = 256 - mpamidr_el1 = Param.UInt64(0x34000040001E0100, "MPAM ID Register (EL1)") + 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. diff --git a/src/arch/arm/SConscript b/src/arch/arm/SConscript index 47d0b54348..948cefb4e4 100644 --- a/src/arch/arm/SConscript +++ b/src/arch/arm/SConscript @@ -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. diff --git a/src/arch/arm/mmu.cc b/src/arch/arm/mmu.cc index b0ad4f1737..f94a18423a 100644 --- a/src/arch/arm/mmu.cc +++ b/src/arch/arm/mmu.cc @@ -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) { diff --git a/src/arch/arm/mpam.cc b/src/arch/arm/mpam.cc index e47b4f80ad..4c618d2f85 100644 --- a/src/arch/arm/mpam.cc +++ b/src/arch/arm/mpam.cc @@ -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 + #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(tc, el); + return ind ? reg.partidI : reg.partidD; +} + +static PMG +getPMG(ThreadContext *tc, ExceptionLevel el, bool ind) +{ + MPAM reg = readRegister(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 +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 +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 +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(tc, ArmSystem::highestEL(tc)); + return reg.mpamEn; +} + +static std::shared_ptr +genExtensionDefault() +{ + // tag with partID data + auto ext = std::make_shared(); + ext->setPartitionID(DEFAULT_PARTITION_ID); + ext->setPartitionMonitoringID(DEFAULT_PARTITION_MONITORING_ID); + return ext; +} + +static std::shared_ptr +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 diff --git a/src/arch/arm/mpam.hh b/src/arch/arm/mpam.hh index 613a53777c..a3d47e48ef 100644 --- a/src/arch/arm/mpam.hh +++ b/src/arch/arm/mpam.hh @@ -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 ExtensiontaskId(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, diff --git a/src/arch/arm/table_walker.hh b/src/arch/arm/table_walker.hh index ee56cb81f1..32f05ac316 100644 --- a/src/arch/arm/table_walker.hh +++ b/src/arch/arm/table_walker.hh @@ -1201,6 +1201,8 @@ class TableWalker : public ClockedObject static uint8_t pageSizeNtoStatBin(uint8_t N); + void mpamTagTableWalk(RequestPtr &req) const; + public: /* Testing */ TlbTestInterface *test;