dev-arm: Implement wired interrupt for SMMU event queue

See https://github.com/orgs/gem5/discussions/898

The SMMUv3 Event Queue is basically unused at the moment.  Whenever a
transaction fails we actually abort simulation.  The sendEvent method
could be used to actually report the failure to the driver but it is
lacking interrupt support to notify the PE there is an event to handle.
The SMMUv3 spec allows both wired and MSI interrupts to be used.

We add the eventq_irq SPI param to the SMMU object and we draft an
initial sendInterrupt utility that makes use of it whenever it is
needed.

Change-Id: I6d103919ca8bf53794ae4bc922cbdc7156adf37a
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
This commit is contained in:
Giacomo Travaglini
2024-02-29 15:34:16 +00:00
parent 63c815b5fc
commit d63282a9da
7 changed files with 104 additions and 11 deletions

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2009-2022 Arm Limited
# Copyright (c) 2009-2022, 2024 Arm Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
@@ -1283,6 +1283,7 @@ class VExpress_GEM5_Base(RealView):
95 : HDLCD
96- 98: GPU (reserved)
100-103: PCI
106 : SMMU event queue
130 : System Watchdog (SP805)
256-319: MSI frame 0 (gem5-specific, SPIs)
320-511: Unused
@@ -1508,7 +1509,10 @@ class VExpress_GEM5_Base(RealView):
if hasattr(self, "smmu"):
m5.fatal("A SMMU has already been instantiated\n")
self.smmu = SMMUv3(reg_map=AddrRange(0x2B400000, size=0x00020000))
self.smmu = SMMUv3(
reg_map=AddrRange(0x2B400000, size=0x00020000),
eventq_irq=ArmSPI(num=106),
)
self.smmu.request = bus.cpu_side_ports
self.smmu.control = bus.mem_side_ports

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2013, 2018-2020 ARM Limited
# Copyright (c) 2013, 2018-2020, 2024 Arm Limited
# All rights reserved
#
# The license below extends only to copyright in the software and shall
@@ -200,6 +200,13 @@ class SMMUv3(ClockedObject):
# [7:0] (0 = SMMUv3.0) (1 = SMMUv3.1)
smmu_aidr = Param.UInt32(0, "SMMU_AIDR register")
eventq_irq = Param.ArmSPI(
NULL,
"Event Queue Interrupt. If set to NULL it means a wired "
"implementation of the interrupt is not supported. "
"In that case MSIs should be used",
)
def generateDeviceTree(self, state):
reg_addr = self.reg_map.start
reg_size = self.reg_map.size()
@@ -210,6 +217,22 @@ class SMMUv3(ClockedObject):
"reg", state.addrCells(reg_addr) + state.sizeCells(reg_size)
)
)
gic = self._parent.unproxy(self).gic
wired_interrupts = []
if self.eventq_irq != NULL:
wired_interrupts += self.eventq_irq.generateFdtProperty(gic)
if wired_interrupts:
node.append(FdtPropertyWords("interrupts", wired_interrupts))
node.append(
FdtPropertyWords(
"interrupt-names",
["eventq"],
)
)
node.append(FdtPropertyWords("#iommu-cells", [1]))
node.appendPhandle(self)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2018-2020 ARM Limited
* Copyright (c) 2013, 2018-2020, 2024 Arm Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -48,6 +48,7 @@
#include "base/types.hh"
#include "debug/Checkpoint.hh"
#include "debug/SMMUv3.hh"
#include "dev/arm/base_gic.hh"
#include "dev/arm/smmu_v3_transl.hh"
#include "mem/packet_access.hh"
#include "sim/system.hh"
@@ -62,6 +63,7 @@ SMMUv3::SMMUv3(const SMMUv3Params &params) :
requestPort(name() + ".request", *this),
tableWalkPort(name() + ".walker", *this),
controlPort(name() + ".control", *this, params.reg_map),
eventqInterrupt(params.eventq_irq ? params.eventq_irq->get() : nullptr),
irqInterfaceEnable(params.irq_interface_enable),
tlb(params.tlb_entries, params.tlb_assoc, params.tlb_policy, this),
configCache(params.cfg_entries, params.cfg_assoc, params.cfg_policy, this),

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2018-2020 ARM Limited
* Copyright (c) 2013, 2018-2020, 2024 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -79,6 +79,8 @@
namespace gem5
{
class ArmInterruptPin;
class SMMUTranslationProcess;
class SMMUv3 : public ClockedObject
@@ -97,6 +99,10 @@ class SMMUv3 : public ClockedObject
SMMUTableWalkPort tableWalkPort;
SMMUControlPort controlPort;
// This could be nullptr if wired implementation of the
// event queue interrupt is not supported
ArmInterruptPin * const eventqInterrupt;
const bool irqInterfaceEnable;
ARMArchTLB tlb;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2018-2019 ARM Limited
* Copyright (c) 2013, 2018-2019, 2024 Arm Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
@@ -102,10 +102,42 @@ enum
Q_BASE_ADDR_MASK = 0x0000ffffffffffe0ULL,
Q_BASE_SIZE_MASK = 0x000000000000001fULL,
E_BASE_ENABLE_MASK = 0x8000000000000000ULL,
E_BASE_ADDR_MASK = 0x0000fffffffffffcULL,
};
BitUnion32(IDR0)
Bitfield<0> s2p;
Bitfield<1> s1p;
Bitfield<3, 2> ttf;
Bitfield<4> cohacc;
Bitfield<5> btm;
Bitfield<7, 6> httu;
Bitfield<8> dormhint;
Bitfield<9> hyp;
Bitfield<10> ats;
Bitfield<11> ns1ats;
Bitfield<12> asid16;
Bitfield<13> msi;
Bitfield<14> sev;
Bitfield<15> atos;
Bitfield<16> pri;
Bitfield<17> vmw;
Bitfield<18> vmid16;
Bitfield<19> cd2l;
Bitfield<20> vatos;
Bitfield<22, 21> ttEndian;
Bitfield<23> atsRecErr;
Bitfield<25, 24> stallModel;
Bitfield<26> termModel;
Bitfield<28, 27> stLevel;
EndBitUnion(IDR0)
BitUnion32(IRQCtrl)
Bitfield<0> gerrorIrqEn;
Bitfield<1> priqIrqEn;
Bitfield<2> eventqIrqEn;
EndBitUnion(IRQCtrl)
union SMMURegs
{
uint8_t data[SMMU_REG_SIZE];

View File

@@ -41,6 +41,7 @@
#include "debug/SMMUv3.hh"
#include "debug/SMMUv3Hazard.hh"
#include "dev/arm/amba.hh"
#include "dev/arm/base_gic.hh"
#include "dev/arm/smmu_v3.hh"
#include "sim/system.hh"
@@ -1387,16 +1388,40 @@ SMMUTranslationProcess::sendEvent(Yield &yield, const SMMUEvent &ev)
DPRINTF(SMMUv3, "Sending event to addr=%#08x (pos=%d): %s\n",
event_addr, smmu.regs.eventq_prod, ev.print());
bool empty_queue = (smmu.regs.eventq_prod & sizeMask) ==
(smmu.regs.eventq_cons & sizeMask);
// This deliberately resets the overflow field in eventq_prod!
smmu.regs.eventq_prod = (smmu.regs.eventq_prod + 1) & sizeMask;
doWrite(yield, event_addr, &ev.data, sizeof(ev.data));
if (!(smmu.regs.eventq_irq_cfg0 & E_BASE_ENABLE_MASK))
panic("eventq msi not enabled\n");
// Send an event queue interrupt when transitioning from empty to
// non empty queue
if (IRQCtrl irq_ctrl = smmu.regs.irq_ctrl;
irq_ctrl.eventqIrqEn && empty_queue) {
doWrite(yield, smmu.regs.eventq_irq_cfg0 & E_BASE_ADDR_MASK,
&smmu.regs.eventq_irq_cfg1, sizeof(smmu.regs.eventq_irq_cfg1));
sendEventInterrupt(yield);
}
}
void
SMMUTranslationProcess::sendEventInterrupt(Yield &yield)
{
Addr msi_addr = smmu.regs.eventq_irq_cfg0 & E_BASE_ADDR_MASK;
// Check if MSIs are enabled by inspecting the SMMU_IDR.MSI bit
// According to the SMMUv3 spec, using an address equal to 0
// disables the sending of the MSI
if (IDR0 idr0 = smmu.regs.idr0; idr0.msi && msi_addr != 0) {
DPRINTF(SMMUv3, "Raise Event queue MSI\n");
doWrite(yield, msi_addr,
&smmu.regs.eventq_irq_cfg1, sizeof(smmu.regs.eventq_irq_cfg1));
}
if (smmu.eventqInterrupt) {
DPRINTF(SMMUv3, "Raise Event queue wired interrupt\n");
smmu.eventqInterrupt->raise();
}
}
void

View File

@@ -226,6 +226,7 @@ class SMMUTranslationProcess : public SMMUProcess
SMMUEvent generateEvent(const TranslResult &tr);
void sendEvent(Yield &yield, const SMMUEvent &ev);
void sendEventInterrupt(Yield &yield);
void doReadSTE(Yield &yield, StreamTableEntry &ste, uint32_t sid);
TranslResult doReadCD(Yield &yield, ContextDescriptor &cd,