[RFC PATCH v4 01/16] iommu/arm-smmu-v3: Discover RME support and realm IRQ topology
Aneesh Kumar K.V (Arm)
aneesh.kumar at kernel.org
Mon Apr 27 01:53:29 PDT 2026
Detect RME-capable SMMUv3 instances from IDR0.RME_IMPL and record the
capability in arm_smmu_device.
When RMM is active, query RMI_PSMMU_INFO to discover how realm-side
notifications are delivered. For pSMMUs that expose realm interrupts, store
the reported evtq/gerror/priq IRQs, reserve extra MSI vectors when RMM uses
MSI delivery, and register threaded handlers that acknowledge notifications
through RMI_PSMMU_IRQ_NOTIFY / RMI_PSMMU_EVENT_CONSUME.
Also add the RMI command/structure definitions needed for PSMMU_INFO and
interrupt notification, along with arm_smmu_device state for the physical
base address and realm IRQs.
If RMM reports a CMDQ sync interrupt requirement, keep the IRQ plumbing
but leave ARM_SMMU_FEAT_RME disabled.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar at kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 52 ++++++++
arch/arm64/include/asm/rmi_smc.h | 32 ++++-
drivers/iommu/arm/arm-smmu-v3/Makefile | 2 +-
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c | 124 ++++++++++++++++++
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 81 +++++++++++-
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 10 ++
6 files changed, 297 insertions(+), 4 deletions(-)
create mode 100644 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index c82d4d9cbc06..75eb59d4fa84 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -811,4 +811,56 @@ static inline unsigned long rmi_pdev_stream_disconnect(unsigned long pdev1_phys,
return res.a0;
}
+static inline unsigned long rmi_psmmu_info(unsigned long psmmu_phys,
+ unsigned long psmmu_info_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PSMMU_INFO,
+ psmmu_phys, psmmu_info_phys, &res);
+
+ return res.a0;
+}
+
+struct rmi_psmmu_event_details {
+ u64 flags;
+ u64 event_num;
+ u64 stream_id; /* valid only if we have VSMMU event */
+ u64 fetch_addr;
+ u64 input_addr;
+ u64 syndrome;
+};
+
+static inline unsigned long rmi_psmmu_irq_notify(unsigned long psmmu_phys,
+ unsigned long irqs, struct rmi_psmmu_event_details *event)
+{
+ struct arm_smccc_1_2_regs regs = {
+ .a0 = SMC_RMI_PSMMU_IRQ_NOTIFY,
+ .a1 = psmmu_phys,
+ .a2 = irqs,
+ };
+
+ arm_smccc_1_2_invoke(®s, ®s);
+
+ event->flags = regs.a1;
+ event->event_num = regs.a2;
+ event->stream_id = regs.a3;
+ event->fetch_addr = regs.a4;
+ event->input_addr = regs.a5;
+ event->syndrome = regs.a6;
+
+ return regs.a0;
+}
+
+static inline unsigned long rmi_psmmu_event_consume(unsigned long psmmu_phys,
+ unsigned long irqs)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PSMMU_EVENT_CONSUME,
+ psmmu_phys, irqs, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 7b16f1540a0e..be1b1e95a937 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -101,7 +101,7 @@
#define SMC_RMI_PDEV_MEC_UPDATE SMC_RMI_CALL(0x01ed)
#define SMC_RMI_VSMMU_EVENT_COMPLETE SMC_RMI_CALL(0x01ee)
-#define SMC_RMI_PSMMU_EVENT_DISCARD SMC_RMI_CALL(0x01f0)
+#define SMC_RMI_PSMMU_EVENT_CONSUME SMC_RMI_CALL(0x01f0)
#define SMC_RMI_GRANULE_RANGE_DELEGATE SMC_RMI_CALL(0x01f1)
#define SMC_RMI_GRANULE_RANGE_UNDELEGATE SMC_RMI_CALL(0x01f2)
#define SMC_RMI_GPT_L1_CREATE SMC_RMI_CALL(0x01f3)
@@ -129,6 +129,7 @@
#define SMC_RMI_OP_MEM_RECLAIM SMC_RMI_CALL(0x0209)
#define SMC_RMI_OP_CANCEL SMC_RMI_CALL(0x020a)
#define SMC_RMI_PDEV_SET_PROT SMC_RMI_CALL(0x020b)
+#define SMC_RMI_PSMMU_INFO SMC_RMI_CALL(0x020e)
#define RMI_ABI_MAJOR_VERSION 2
#define RMI_ABI_MINOR_VERSION 0
@@ -595,4 +596,33 @@ struct rmi_pdev_stream_params {
};
};
+#define RMI_PSMMU_IRQCFG_IRQ_DISABLED 0x0
+#define RMI_PSMMU_IRQCFG_IRQ_WIRED 0x1
+#define RMI_PSMMU_IRQCFG_IRQ_MSI 0x2
+#define RMI_PSMMU_IRQCFG_MASK GENMASK(1, 0)
+struct rmi_psmmu_info {
+ union {
+ struct {
+ u64 flags;
+ union {
+ u32 gerror_intr_num;
+ u8 padding1[8];
+ };
+ union {
+ u32 eventq_intr_num;
+ u8 padding2[8];
+ };
+ union {
+ u32 priq_intr_num;
+ u8 padding3[8];
+ };
+ union {
+ u32 cmdq_sync_intr_num;
+ u8 padding4[8];
+ };
+ };
+ u8 padding5[0x1000];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/iommu/arm/arm-smmu-v3/Makefile b/drivers/iommu/arm/arm-smmu-v3/Makefile
index 493a659cc66b..23bd794ebeda 100644
--- a/drivers/iommu/arm/arm-smmu-v3/Makefile
+++ b/drivers/iommu/arm/arm-smmu-v3/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_ARM_SMMU_V3) += arm_smmu_v3.o
-arm_smmu_v3-y := arm-smmu-v3.o
+arm_smmu_v3-y := arm-smmu-v3.o arm-smmu-v3-realm.o
arm_smmu_v3-$(CONFIG_ARM_SMMU_V3_IOMMUFD) += arm-smmu-v3-iommufd.o
arm_smmu_v3-$(CONFIG_ARM_SMMU_V3_SVA) += arm-smmu-v3-sva.o
arm_smmu_v3-$(CONFIG_TEGRA241_CMDQV) += tegra241-cmdqv.o
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
new file mode 100644
index 000000000000..fec1a32de53c
--- /dev/null
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-realm.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#include <linux/interrupt.h>
+#include <asm/rmi_smc.h>
+#include <asm/rmi_cmds.h>
+
+#include "arm-smmu-v3.h"
+
+#define RMI_PSMMU_IRQ_GERROR BIT(0)
+#define RMI_PSMMU_IRQ_EVENTQ BIT(1)
+#define RMI_PSMMU_IRQ_PRIQ BIT(2)
+#define RMI_PSMMU_IRQ_CMDQ BIT(3)
+
+#define RMI_PSMMU_IRQ_EVENT_NONE 0
+#define RMI_PSMMU_IRQ_EVENT_ERROR 1
+#define RMI_PSMMU_IRQ_EVENT_PSMMU 2
+#define RMI_PSMMU_IRQ_EVENT_VSMMU 3
+#define RMI_PSMMU_IRQ_EVENT_MASK GENMASK(2, 1)
+#define RMI_PSMMU_IRQ_EVENT_SHIFT 1
+#define RMI_PSMMU_IRQ_EVENT_PENDING 0x1
+
+static irqreturn_t arm_smmu_realm_notify_thread(int irq, void *dev)
+{
+ int rmi_psmmu_event;
+ unsigned long notify_flags;
+ struct arm_smmu_device *smmu = dev;
+ struct rmi_psmmu_event_details event;
+
+ if (irq == smmu->realm_evtq_irq)
+ notify_flags = RMI_PSMMU_IRQ_EVENTQ;
+ else if (irq == smmu->realm_gerr_irq)
+ notify_flags = RMI_PSMMU_IRQ_GERROR;
+ else if (irq == smmu->realm_pri_irq)
+ notify_flags = RMI_PSMMU_IRQ_PRIQ;
+ else
+ return IRQ_HANDLED;
+
+ do {
+ if (rmi_psmmu_irq_notify(smmu->base_phys,
+ notify_flags, &event)) {
+ dev_warn(smmu->dev,
+ "failed to notify RMM of a SMMU event\n");
+ /* there is nothing much we could do. Mark it handled. */
+ return IRQ_HANDLED;
+ }
+ rmi_psmmu_event = (event.flags & RMI_PSMMU_IRQ_EVENT_MASK) >>
+ RMI_PSMMU_IRQ_EVENT_SHIFT;
+ switch (rmi_psmmu_event) {
+ case RMI_PSMMU_IRQ_EVENT_NONE:
+ break;
+ case RMI_PSMMU_IRQ_EVENT_ERROR:
+ dev_warn(smmu->dev, "SMMU Error reported\n");
+ rmi_psmmu_event_consume(smmu->base_phys, notify_flags);
+ break;
+ case RMI_PSMMU_IRQ_EVENT_PSMMU:
+ dev_warn(smmu->dev,
+ "SMMU event (event num: 0x%llx syndrome 0x%llx "
+ "fetch_addr 0x%llx input_addr 0x%llx) reported\n",
+ event.event_num, event.syndrome,
+ event.fetch_addr, event.input_addr);
+ rmi_psmmu_event_consume(smmu->base_phys, notify_flags);
+ break;
+ case RMI_PSMMU_IRQ_EVENT_VSMMU:
+ dev_warn(smmu->dev, "Wrong VSMMU event on stream 0x%llx, ignoring\n",
+ event.stream_id);
+ rmi_psmmu_event_consume(smmu->base_phys, notify_flags);
+ break;
+ }
+
+ } while (event.flags & RMI_PSMMU_IRQ_EVENT_PENDING);
+
+ return IRQ_HANDLED;
+}
+
+void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu)
+{
+ int irq, ret;
+
+ irq = smmu->realm_evtq_irq;
+ if (irq) {
+ ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
+ arm_smmu_realm_notify_thread,
+ IRQF_ONESHOT,
+ "arm-smmu-v3-realm-evtq",
+ smmu);
+ if (ret < 0)
+ dev_warn(smmu->dev, "failed to enable realm evtq irq\n");
+ } else {
+ dev_warn(smmu->dev, "no realm evtq irq - events will not be reported!\n");
+ }
+
+ irq = smmu->realm_gerr_irq;
+ if (irq) {
+ ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
+ arm_smmu_realm_notify_thread,
+ IRQF_ONESHOT,
+ "arm-smmu-v3-realm-gerror",
+ smmu);
+ if (ret < 0)
+ dev_warn(smmu->dev, "failed to enable realm gerror irq\n");
+ } else {
+ dev_warn(smmu->dev, "no realm gerr irq - errors will not be reported!\n");
+ }
+
+ if (smmu->features & ARM_SMMU_FEAT_PRI) {
+ irq = smmu->realm_pri_irq;
+ if (irq) {
+
+ ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
+ arm_smmu_realm_notify_thread,
+ IRQF_ONESHOT,
+ "arm-smmu-v3-realm-priq",
+ smmu);
+ if (ret < 0)
+ dev_warn(smmu->dev,
+ "failed to enable realm priq irq\n");
+ } else {
+ dev_warn(smmu->dev, "no realm priq irq - PRI will be broken\n");
+ }
+ }
+}
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 4d00d796f078..d5b9ab95beea 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -29,6 +29,9 @@
#include <linux/string_choices.h>
#include <kunit/visibility.h>
#include <uapi/linux/iommufd.h>
+#include <linux/irq.h>
+#include <linux/msi.h>
+#include <asm/rmi_cmds.h>
#include "arm-smmu-v3.h"
#include "../../dma-iommu.h"
@@ -4000,10 +4003,20 @@ static void arm_smmu_free_msis(void *data)
static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
{
+ int max_config_index = GERROR_MSI_INDEX;
phys_addr_t doorbell;
struct device *dev = msi_desc_to_dev(desc);
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
- phys_addr_t *cfg = arm_smmu_msi_cfg[desc->msi_index];
+ phys_addr_t *cfg;
+
+ if (smmu->features & ARM_SMMU_FEAT_PRI)
+ max_config_index = PRIQ_MSI_INDEX;
+
+ /* Don't try to config for Realm interrupts. */
+ if (desc->msi_index > max_config_index)
+ return;
+
+ cfg = arm_smmu_msi_cfg[desc->msi_index];
doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
doorbell &= MSI_CFG0_ADDR_MASK;
@@ -4015,6 +4028,7 @@ static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
{
+ int irq_index;
int ret, nvec = ARM_SMMU_MAX_MSIS;
struct device *dev = smmu->dev;
@@ -4035,6 +4049,13 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
return;
}
+ /*
+ * Request for realm side non secure interrupts too. Should this be condition
+ * on non-secure gic?
+ */
+ if (smmu->features & ARM_SMMU_FEAT_RME_MSI)
+ nvec = nvec * 2;
+
/* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
ret = platform_device_msi_init_and_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
if (ret) {
@@ -4044,7 +4065,19 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
smmu->evtq.q.irq = msi_get_virq(dev, EVTQ_MSI_INDEX);
smmu->gerr_irq = msi_get_virq(dev, GERROR_MSI_INDEX);
- smmu->priq.q.irq = msi_get_virq(dev, PRIQ_MSI_INDEX);
+ irq_index = 2;
+ if (smmu->features & ARM_SMMU_FEAT_PRI) {
+ smmu->priq.q.irq = msi_get_virq(dev, PRIQ_MSI_INDEX);
+ irq_index++;
+ }
+
+ if (smmu->features & ARM_SMMU_FEAT_RME_MSI) {
+ smmu->realm_evtq_irq = msi_get_virq(dev, irq_index++);
+ smmu->realm_gerr_irq = msi_get_virq(dev, irq_index++);
+ // fixme, we should check for pri rmm capability
+ if (smmu->features & ARM_SMMU_FEAT_PRI)
+ smmu->realm_pri_irq = msi_get_virq(dev, irq_index++);
+ }
/* Add callback to free MSIs on teardown */
devm_add_action_or_reset(dev, arm_smmu_free_msis, dev);
@@ -4094,6 +4127,9 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
dev_warn(smmu->dev, "no priq irq - PRI will be broken\n");
}
}
+
+ if (smmu->features & ARM_SMMU_FEAT_RME_IRQ)
+ arm_smmu_setup_realm_irqs(smmu);
}
static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
@@ -4464,6 +4500,9 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
smmu->asid_bits = reg & IDR0_ASID16 ? 16 : 8;
smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8;
+ if (reg & IDR0_RME_IMPL)
+ smmu->features |= ARM_SMMU_FEAT_RME;
+
/* IDR1 */
reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) {
@@ -4852,6 +4891,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
return -EINVAL;
}
ioaddr = res->start;
+ smmu->base_phys = ioaddr;
/*
* Don't map the IMPLEMENTATION DEFINED regions, since they may contain
@@ -4893,6 +4933,43 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
if (ret)
return ret;
+ if (rmm_is_active()) {
+ struct rmi_psmmu_info *psmmu_info;
+
+ psmmu_info = (struct rmi_psmmu_info *)get_zeroed_page(GFP_KERNEL);
+ if (!psmmu_info)
+ goto skip_rmm_config;
+
+ if (rmi_psmmu_info(smmu->base_phys, virt_to_phys(psmmu_info)))
+ smmu->features &= ~ARM_SMMU_FEAT_RME;
+
+ if ((psmmu_info->flags & RMI_PSMMU_IRQCFG_MASK) ==
+ RMI_PSMMU_IRQCFG_IRQ_DISABLED) {
+ free_page((unsigned long)psmmu_info);
+ goto skip_rmm_config;
+ }
+
+ smmu->features |= ARM_SMMU_FEAT_RME_IRQ;
+
+ if ((psmmu_info->flags & RMI_PSMMU_IRQCFG_MASK) ==
+ RMI_PSMMU_IRQCFG_IRQ_WIRED) {
+ smmu->realm_gerr_irq = psmmu_info->gerror_intr_num;
+ smmu->realm_evtq_irq = psmmu_info->eventq_intr_num;
+ smmu->realm_pri_irq = psmmu_info->priq_intr_num;
+
+ /* Disable RME FEAT because RMM need cmdq sync interrupt*/
+ if (psmmu_info->cmdq_sync_intr_num)
+ smmu->features &= ~ARM_SMMU_FEAT_RME;
+
+ } else if ((psmmu_info->flags & RMI_PSMMU_IRQCFG_MASK) ==
+ RMI_PSMMU_IRQCFG_IRQ_MSI) {
+ smmu->features |= ARM_SMMU_FEAT_RME_MSI;
+ }
+
+ free_page((unsigned long)psmmu_info);
+ }
+skip_rmm_config:
+
/* Initialise in-memory data structures */
ret = arm_smmu_init_structures(smmu);
if (ret)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 3c6d65d36164..6680516b571b 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -20,6 +20,7 @@ struct arm_vsmmu;
/* MMIO registers */
#define ARM_SMMU_IDR0 0x0
+#define IDR0_RME_IMPL (1 << 30)
#define IDR0_ST_LVL GENMASK(28, 27)
#define IDR0_ST_LVL_2LVL 1
#define IDR0_STALL_MODEL GENMASK(25, 24)
@@ -739,6 +740,7 @@ struct arm_smmu_device {
struct device *impl_dev;
const struct arm_smmu_impl_ops *impl_ops;
+ phys_addr_t base_phys;
void __iomem *base;
void __iomem *page1;
@@ -767,6 +769,9 @@ struct arm_smmu_device {
#define ARM_SMMU_FEAT_HD (1 << 22)
#define ARM_SMMU_FEAT_S2FWB (1 << 23)
#define ARM_SMMU_FEAT_BBML2 (1 << 24)
+#define ARM_SMMU_FEAT_RME (1 << 25)
+#define ARM_SMMU_FEAT_RME_IRQ (1 << 26)
+#define ARM_SMMU_FEAT_RME_MSI (1 << 27)
u32 features;
#define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0)
@@ -782,6 +787,9 @@ struct arm_smmu_device {
int gerr_irq;
int combined_irq;
+ int realm_gerr_irq;
+ int realm_evtq_irq;
+ int realm_pri_irq;
unsigned long oas; /* PA */
unsigned long pgsize_bitmap;
@@ -1096,4 +1104,6 @@ static inline int arm_vmaster_report_event(struct arm_smmu_vmaster *vmaster,
}
#endif /* CONFIG_ARM_SMMU_V3_IOMMUFD */
+void arm_smmu_setup_realm_irqs(struct arm_smmu_device *smmu);
+
#endif /* _ARM_SMMU_V3_H */
--
2.43.0
More information about the linux-arm-kernel
mailing list