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,