From: Andrew Jones <andrew.jones@oss.qualcomm.com>
To: linux-riscv@lists.infradead.org, iommu@lists.linux.dev
Cc: linux-kernel@vger.kernel.org, tomasz.jeznach@linux.dev,
tjeznach@rivosinc.com, jgg@ziepe.ca, jgg@nvidia.com,
joro@8bytes.org, will@kernel.org, robin.murphy@arm.com,
pjw@kernel.org, palmer@dabbelt.com, anup@brainfault.org,
tglx@kernel.org, kevin.tian@intel.com,
fangyu.yu@linux.alibaba.com
Subject: [PATCH v3 07/19] iommu/riscv: Add IRQ domain for interrupt remapping
Date: Fri, 7 Aug 2026 20:17:01 +0200 [thread overview]
Message-ID: <20260807181713.228535-8-andrew.jones@oss.qualcomm.com> (raw)
In-Reply-To: <20260807181713.228535-1-andrew.jones@oss.qualcomm.com>
Create a per-device MSI parent irqdomain as the hierarchy hook for
future interrupt remapping. The remapping tables will be owned by the
attached paging domain since the MSI IOVA mappings live in its page
tables.
The domain is installed from probe_device() and removed from
release_device(). This is only the initial skeleton: it does not
remap interrupts yet, and non-paging IOMMU domains will fall back to
the raw IMSIC physical address when remapping is added.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/Makefile | 2 +-
drivers/iommu/riscv/iommu-ir.c | 119 ++++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.c | 46 ++++++-----
drivers/iommu/riscv/iommu.h | 27 +++++++
include/linux/irqchip/riscv-imsic.h | 7 ++
5 files changed, 182 insertions(+), 19 deletions(-)
create mode 100644 drivers/iommu/riscv/iommu-ir.c
diff --git a/drivers/iommu/riscv/Makefile b/drivers/iommu/riscv/Makefile
index b5929f9f23e6..9c83f877d50f 100644
--- a/drivers/iommu/riscv/Makefile
+++ b/drivers/iommu/riscv/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y += iommu.o iommu-platform.o
+obj-y += iommu.o iommu-ir.o iommu-platform.o
obj-$(CONFIG_RISCV_IOMMU_PCI) += iommu-pci.o
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
new file mode 100644
index 000000000000..5873addf2a1b
--- /dev/null
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IOMMU Interrupt Remapping
+ *
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ */
+#include <linux/cleanup.h>
+#include <linux/msi.h>
+#include <linux/slab.h>
+
+#include "iommu.h"
+
+static struct irq_chip riscv_iommu_ir_irq_chip = {
+ .name = "IOMMU-IR",
+ .irq_ack = irq_chip_ack_parent,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+};
+
+static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain,
+ unsigned int irq_base, unsigned int nr_irqs,
+ void *arg)
+{
+ struct irq_data *data;
+ int i, ret;
+
+ ret = irq_domain_alloc_irqs_parent(irqdomain, irq_base, nr_irqs, arg);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++) {
+ data = irq_domain_get_irq_data(irqdomain, irq_base + i);
+ data->chip = &riscv_iommu_ir_irq_chip;
+ }
+
+ return 0;
+}
+
+static const struct irq_domain_ops riscv_iommu_ir_irq_domain_ops = {
+ .alloc = riscv_iommu_ir_irq_domain_alloc_irqs,
+ .free = irq_domain_free_irqs_parent,
+};
+
+static const struct msi_parent_ops riscv_iommu_ir_msi_parent_ops = {
+ .prefix = "IR-",
+ .supported_flags = MSI_GENERIC_FLAGS_MASK |
+ MSI_FLAG_PCI_MSIX,
+ .required_flags = MSI_FLAG_USE_DEF_DOM_OPS |
+ MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_PCI_MSI_MASK_PARENT,
+ .chip_flags = MSI_CHIP_FLAG_SET_ACK,
+ .init_dev_msi_info = msi_parent_init_dev_msi_info,
+};
+
+struct irq_domain *riscv_iommu_ir_irq_domain_create(struct device *dev,
+ struct riscv_iommu_info *info)
+{
+ struct irq_domain *irqparent = dev_get_msi_domain(dev);
+ struct irq_domain *irqdomain;
+ struct fwnode_handle *fn;
+ char *fwname __free(kfree) = NULL;
+
+ if (!irqparent)
+ return NULL;
+
+ fwname = kasprintf(GFP_KERNEL, "IOMMU-IR-%s", dev_name(dev));
+ if (!fwname)
+ return ERR_PTR(-ENOMEM);
+
+ fn = irq_domain_alloc_named_fwnode(fwname);
+ if (!fn)
+ return ERR_PTR(-ENOMEM);
+
+ irqdomain = irq_domain_create_hierarchy(irqparent, 0, 0, fn,
+ &riscv_iommu_ir_irq_domain_ops,
+ info);
+ if (!irqdomain) {
+ irq_domain_free_fwnode(fn);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /*
+ * The RISC-V IOMMU doesn't validate MSI data, so we can't set
+ * IRQ_DOMAIN_FLAG_ISOLATED_MSI. This means VFIO requires
+ * allow_unsafe_interrupts.
+ */
+ irqdomain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+ irqdomain->msi_parent_ops = &riscv_iommu_ir_msi_parent_ops;
+ irq_domain_update_bus_token(irqdomain, DOMAIN_BUS_MSI_REMAP);
+
+ dev_set_msi_domain(dev, irqdomain);
+
+ return irqdomain;
+}
+
+void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_info *info)
+{
+ struct fwnode_handle *fn;
+
+ if (!info->irqdomain)
+ return;
+
+ dev_set_msi_domain(dev, info->irqdomain->parent);
+ fn = info->irqdomain->fwnode;
+ irq_domain_remove(info->irqdomain);
+ info->irqdomain = NULL;
+ irq_domain_free_fwnode(fn);
+}
+
+int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
+ struct iommu_domain *old)
+{
+ return 0;
+}
+
+void riscv_iommu_ir_free_paging_domain(struct iommu_domain *iommu_domain)
+{
+}
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index cec3ddd7ab10..bfe606a0cb9a 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -19,9 +19,9 @@
#include <linux/init.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
+#include <linux/irqchip/riscv-imsic.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/generic_pt/iommu.h>
#include "../iommu-pages.h"
#include "iommu-bits.h"
@@ -810,26 +810,9 @@ static int riscv_iommu_iodir_set_mode(struct riscv_iommu_device *iommu,
return 0;
}
-/* This struct contains protection domain specific IOMMU driver data. */
-struct riscv_iommu_domain {
- union {
- struct iommu_domain domain;
- struct pt_iommu_riscv_64 riscvpt;
- };
- struct list_head bonds;
- spinlock_t lock; /* protect bonds list updates. */
- int pscid;
-};
-PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
-
#define iommu_domain_to_riscv(iommu_domain) \
container_of(iommu_domain, struct riscv_iommu_domain, domain)
-/* Private IOMMU data for managed devices, dev_iommu_priv_* */
-struct riscv_iommu_info {
- struct riscv_iommu_domain *domain;
-};
-
/*
* Linkage between an iommu_domain and attached devices.
*
@@ -1258,6 +1241,8 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
WARN_ON(!list_empty(&domain->bonds));
+ riscv_iommu_ir_free_paging_domain(iommu_domain);
+
if ((int)domain->pscid > 0)
ida_free(&riscv_iommu_pscids, domain->pscid);
@@ -1289,6 +1274,7 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
struct pt_iommu_riscv_64_hw_info pt_info;
u64 fsc, ta;
+ int ret;
pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
@@ -1303,6 +1289,12 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
if (riscv_iommu_bond_link(domain, dev))
return -ENOMEM;
+ ret = riscv_iommu_ir_attach_paging_domain(iommu_domain, dev, old);
+ if (ret) {
+ riscv_iommu_bond_unlink(domain, dev);
+ return ret;
+ }
+
riscv_iommu_iodir_update(iommu, dev, fsc, ta);
riscv_iommu_bond_unlink(info->domain, dev);
info->domain = domain;
@@ -1430,6 +1422,7 @@ static int riscv_iommu_of_xlate(struct device *dev, const struct of_phandle_args
static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
{
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct irq_domain *irqdomain = NULL;
struct riscv_iommu_device *iommu;
struct riscv_iommu_info *info;
struct riscv_iommu_dc *dc;
@@ -1453,6 +1446,21 @@ static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
info = kzalloc_obj(*info);
if (!info)
return ERR_PTR(-ENOMEM);
+
+ if (imsic_enabled()) {
+ irqdomain = riscv_iommu_ir_irq_domain_create(dev, info);
+ if (IS_ERR(irqdomain)) {
+ kfree(info);
+ return ERR_CAST(irqdomain);
+ }
+ }
+
+ /*
+ * irqdomain is NULL when it's not necessary; either there aren't
+ * any IMSICs or no MSI domain has been set up for the device.
+ */
+ info->irqdomain = irqdomain;
+
/*
* Allocate and pre-configure device context entries in
* the device directory. Do not mark the context valid yet.
@@ -1461,6 +1469,7 @@ static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
for (i = 0; i < fwspec->num_ids; i++) {
dc = riscv_iommu_get_dc(iommu, fwspec->ids[i]);
if (!dc) {
+ riscv_iommu_ir_irq_domain_remove(dev, info);
kfree(info);
return ERR_PTR(-ENODEV);
}
@@ -1478,6 +1487,7 @@ static void riscv_iommu_release_device(struct device *dev)
{
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+ riscv_iommu_ir_irq_domain_remove(dev, info);
kfree_rcu_mightsleep(info);
}
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd5495..5d83537911b6 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -14,9 +14,29 @@
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
+#include <linux/irqdomain.h>
+#include <linux/generic_pt/iommu.h>
#include "iommu-bits.h"
+/* This struct contains protection domain specific IOMMU driver data. */
+struct riscv_iommu_domain {
+ union {
+ struct iommu_domain domain;
+ struct pt_iommu_riscv_64 riscvpt;
+ };
+ struct list_head bonds;
+ spinlock_t lock; /* protect bonds list updates. */
+ int pscid;
+};
+PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
+
+/* Private IOMMU data for managed devices, dev_iommu_priv_* */
+struct riscv_iommu_info {
+ struct riscv_iommu_domain *domain;
+ struct irq_domain *irqdomain;
+};
+
struct riscv_iommu_device;
struct riscv_iommu_queue {
@@ -66,6 +86,13 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu);
void riscv_iommu_remove(struct riscv_iommu_device *iommu);
void riscv_iommu_disable(struct riscv_iommu_device *iommu);
+struct irq_domain *riscv_iommu_ir_irq_domain_create(struct device *dev,
+ struct riscv_iommu_info *info);
+void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_info *info);
+int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
+ struct iommu_domain *old);
+void riscv_iommu_ir_free_paging_domain(struct iommu_domain *iommu_domain);
+
#define riscv_iommu_readl(iommu, addr) \
readl_relaxed((iommu)->reg + (addr))
diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
index 61af3a5bea09..ce8fe1ead7a0 100644
--- a/include/linux/irqchip/riscv-imsic.h
+++ b/include/linux/irqchip/riscv-imsic.h
@@ -91,6 +91,13 @@ static inline const struct imsic_global_config *imsic_get_global_config(void)
#endif
+static inline bool imsic_enabled(void)
+{
+ const struct imsic_global_config *imsic_global = imsic_get_global_config();
+
+ return imsic_global && imsic_global->nr_ids;
+}
+
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_RISCV_IMSIC)
int imsic_platform_acpi_probe(struct fwnode_handle *fwnode);
struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev);
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
next prev parent reply other threads:[~2026-08-07 18:17 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
2026-08-07 18:16 ` [PATCH v3 01/19] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
2026-08-07 18:16 ` [PATCH v3 02/19] iommufd: Add iommufd_sw_map_msi() Andrew Jones
2026-08-07 18:16 ` [PATCH v3 03/19] iommu/dma: Add iommu_dma_sw_map_msi() Andrew Jones
2026-08-07 18:16 ` [PATCH v3 04/19] iommu/dma: Add iommu_dma_map_msi() Andrew Jones
2026-08-07 18:16 ` [PATCH v3 05/19] genirq/msi: Provide DOMAIN_BUS_MSI_REMAP Andrew Jones
2026-08-07 18:17 ` [PATCH v3 06/19] irqchip/riscv-imsic: Compose MSI updates through the hierarchy Andrew Jones
2026-08-07 20:25 ` Thomas Gleixner
2026-08-07 18:17 ` Andrew Jones [this message]
2026-08-07 20:32 ` [PATCH v3 07/19] iommu/riscv: Add IRQ domain for interrupt remapping Thomas Gleixner
2026-08-07 20:36 ` Thomas Gleixner
2026-08-07 18:17 ` [PATCH v3 08/19] iommu/riscv: Prepare info->domain for concurrent RCU read access Andrew Jones
2026-08-07 18:17 ` [PATCH v3 09/19] iommu/riscv: Publish IOMMU_RESV_SW_MSI region for iommufd MSI remapping Andrew Jones
2026-08-07 18:17 ` [PATCH v3 10/19] iommu/riscv: Pre-map IMSIC MSI targets Andrew Jones
2026-08-07 18:17 ` [PATCH v3 11/19] iommu/riscv: Copy MSI IOVA table when replacing an iommufd domain Andrew Jones
2026-08-07 18:17 ` [PATCH v3 12/19] iommu/riscv: Gate identity boundary switches with live MSIs Andrew Jones
2026-08-07 18:17 ` [PATCH v3 13/19] iommu/riscv: Implement irq_compose_msi_msg for IMSIC remapping Andrew Jones
2026-08-07 18:17 ` [PATCH v3 14/19] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V Andrew Jones
2026-08-07 18:17 ` [PATCH v3 15/19] iommu/riscv: report iommu capabilities Andrew Jones
2026-08-07 18:17 ` [PATCH v3 16/19] vfio: enable IOMMU_TYPE1 for RISC-V Andrew Jones
2026-08-07 18:17 ` [PATCH v3 17/19] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch Andrew Jones
2026-08-07 18:17 ` [PATCH v3 18/19] riscv: defconfig: Enable IOMMUFD and VFIO Andrew Jones
2026-08-07 18:17 ` [PATCH v3 19/19] selftests/vfio: Allow building on RISC-V Andrew Jones
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260807181713.228535-8-andrew.jones@oss.qualcomm.com \
--to=andrew.jones@oss.qualcomm.com \
--cc=anup@brainfault.org \
--cc=fangyu.yu@linux.alibaba.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@nvidia.com \
--cc=jgg@ziepe.ca \
--cc=joro@8bytes.org \
--cc=kevin.tian@intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=palmer@dabbelt.com \
--cc=pjw@kernel.org \
--cc=robin.murphy@arm.com \
--cc=tglx@kernel.org \
--cc=tjeznach@rivosinc.com \
--cc=tomasz.jeznach@linux.dev \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox