arch-arm: Implement FEAT_MPAM
With this patch we are adding the specific logic required to tag memory requests from the PE with MPAM data. This is happening within the MMU before a translation is completed Change-Id: Ifecb285244dd469a639150d69a7e884fe3c441be Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com> Reviewed-by: Richard Cooper <richard.cooper@arm.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,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