From d63282a9da002c541539491f6deea8f4a5e2cf15 Mon Sep 17 00:00:00 2001 From: Giacomo Travaglini Date: Thu, 29 Feb 2024 15:34:16 +0000 Subject: [PATCH] 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 --- src/dev/arm/RealView.py | 8 ++++++-- src/dev/arm/SMMUv3.py | 25 +++++++++++++++++++++++- src/dev/arm/smmu_v3.cc | 4 +++- src/dev/arm/smmu_v3.hh | 8 +++++++- src/dev/arm/smmu_v3_defs.hh | 36 +++++++++++++++++++++++++++++++++-- src/dev/arm/smmu_v3_transl.cc | 33 ++++++++++++++++++++++++++++---- src/dev/arm/smmu_v3_transl.hh | 1 + 7 files changed, 104 insertions(+), 11 deletions(-) diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index a0540ec50c..bff79c15df 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -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 diff --git a/src/dev/arm/SMMUv3.py b/src/dev/arm/SMMUv3.py index 28c2c6fe24..e79fa0fcd5 100644 --- a/src/dev/arm/SMMUv3.py +++ b/src/dev/arm/SMMUv3.py @@ -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) diff --git a/src/dev/arm/smmu_v3.cc b/src/dev/arm/smmu_v3.cc index 8ce8bd92b2..00e1867eeb 100644 --- a/src/dev/arm/smmu_v3.cc +++ b/src/dev/arm/smmu_v3.cc @@ -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), diff --git a/src/dev/arm/smmu_v3.hh b/src/dev/arm/smmu_v3.hh index 8721352c47..6a235fa385 100644 --- a/src/dev/arm/smmu_v3.hh +++ b/src/dev/arm/smmu_v3.hh @@ -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; diff --git a/src/dev/arm/smmu_v3_defs.hh b/src/dev/arm/smmu_v3_defs.hh index c2f8d23e1d..c20343d9e0 100644 --- a/src/dev/arm/smmu_v3_defs.hh +++ b/src/dev/arm/smmu_v3_defs.hh @@ -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]; diff --git a/src/dev/arm/smmu_v3_transl.cc b/src/dev/arm/smmu_v3_transl.cc index 204268efad..2d519375b0 100644 --- a/src/dev/arm/smmu_v3_transl.cc +++ b/src/dev/arm/smmu_v3_transl.cc @@ -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 diff --git a/src/dev/arm/smmu_v3_transl.hh b/src/dev/arm/smmu_v3_transl.hh index bc6b4bc9a7..93877a54b1 100644 --- a/src/dev/arm/smmu_v3_transl.hh +++ b/src/dev/arm/smmu_v3_transl.hh @@ -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,