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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ¶ms) :
|
||||
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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user