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:
Giacomo Travaglini
2024-01-26 23:15:24 +00:00
parent c988642ca8
commit b8e414f14d
7 changed files with 241 additions and 5 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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

View File

@@ -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__

View File

@@ -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,

View File

@@ -1201,6 +1201,8 @@ class TableWalker : public ClockedObject
static uint8_t pageSizeNtoStatBin(uint8_t N);
void mpamTagTableWalk(RequestPtr &req) const;
public: /* Testing */
TlbTestInterface *test;