git: 5967352a923e - main - x86 iommu: move DMAR-independent parts of the interrupt setup code into common

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Wed, 04 Sep 2024 21:50:46 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=5967352a923efe6676bdf794d6b73f7354719a43

commit 5967352a923efe6676bdf794d6b73f7354719a43
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2024-06-09 19:36:10 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2024-09-04 21:50:19 +0000

    x86 iommu: move DMAR-independent parts of the interrupt setup code into common
    
    Sponsored by:   Advanced Micro Devices (AMD)
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
---
 sys/x86/iommu/intel_dmar.h  |  24 ++-----
 sys/x86/iommu/intel_drv.c   | 151 ++++++++++++--------------------------------
 sys/x86/iommu/intel_fault.c |  16 +++--
 sys/x86/iommu/intel_qi.c    |  14 ++--
 sys/x86/iommu/iommu_utils.c | 105 ++++++++++++++++++++++++++++++
 sys/x86/iommu/x86_iommu.h   |  23 +++++++
 6 files changed, 193 insertions(+), 140 deletions(-)

diff --git a/sys/x86/iommu/intel_dmar.h b/sys/x86/iommu/intel_dmar.h
index 7b4a7900fa25..0242e0cb954f 100644
--- a/sys/x86/iommu/intel_dmar.h
+++ b/sys/x86/iommu/intel_dmar.h
@@ -103,20 +103,6 @@ struct dmar_ctx {
 #define	CTX2DMAR(ctx)		(CTX2DOM(ctx)->dmar)
 #define	DOM2DMAR(domain)	((domain)->dmar)
 
-struct dmar_msi_data {
-	int irq;
-	int irq_rid;
-	struct resource *irq_res;
-	void *intr_handle;
-	int (*handler)(void *);
-	int msi_data_reg;
-	int msi_addr_reg;
-	int msi_uaddr_reg;
-	void (*enable_intr)(struct dmar_unit *);
-	void (*disable_intr)(struct dmar_unit *);
-	const char *name;
-};
-
 #define	DMAR_INTR_FAULT		0
 #define	DMAR_INTR_QI		1
 #define	DMAR_INTR_TOTAL		2
@@ -131,8 +117,6 @@ struct dmar_unit {
 	int reg_rid;
 	struct resource *regs;
 
-	struct dmar_msi_data intrs[DMAR_INTR_TOTAL];
-
 	/* Hardware registers cache */
 	uint32_t hw_ver;
 	uint64_t hw_cap;
@@ -216,14 +200,14 @@ uint64_t dmar_get_timeout(void);
 void dmar_update_timeout(uint64_t newval);
 
 int dmar_fault_intr(void *arg);
-void dmar_enable_fault_intr(struct dmar_unit *unit);
-void dmar_disable_fault_intr(struct dmar_unit *unit);
+void dmar_enable_fault_intr(struct iommu_unit *unit);
+void dmar_disable_fault_intr(struct iommu_unit *unit);
 int dmar_init_fault_log(struct dmar_unit *unit);
 void dmar_fini_fault_log(struct dmar_unit *unit);
 
 int dmar_qi_intr(void *arg);
-void dmar_enable_qi_intr(struct dmar_unit *unit);
-void dmar_disable_qi_intr(struct dmar_unit *unit);
+void dmar_enable_qi_intr(struct iommu_unit *unit);
+void dmar_disable_qi_intr(struct iommu_unit *unit);
 int dmar_init_qi(struct dmar_unit *unit);
 void dmar_fini_qi(struct dmar_unit *unit);
 void dmar_qi_invalidate_locked(struct dmar_domain *domain,
diff --git a/sys/x86/iommu/intel_drv.c b/sys/x86/iommu/intel_drv.c
index 66d99748888f..2439ef9e4ef5 100644
--- a/sys/x86/iommu/intel_drv.c
+++ b/sys/x86/iommu/intel_drv.c
@@ -230,22 +230,6 @@ dmar_probe(device_t dev)
 	return (BUS_PROBE_NOWILDCARD);
 }
 
-static void
-dmar_release_intr(device_t dev, struct dmar_unit *unit, int idx)
-{
-	struct dmar_msi_data *dmd;
-
-	dmd = &unit->intrs[idx];
-	if (dmd->irq == -1)
-		return;
-	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
-	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
-	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
-	PCIB_RELEASE_MSIX(device_get_parent(device_get_parent(dev)),
-	    dev, dmd->irq);
-	dmd->irq = -1;
-}
-
 static void
 dmar_release_resources(device_t dev, struct dmar_unit *unit)
 {
@@ -256,7 +240,7 @@ dmar_release_resources(device_t dev, struct dmar_unit *unit)
 	dmar_fini_qi(unit);
 	dmar_fini_fault_log(unit);
 	for (i = 0; i < DMAR_INTR_TOTAL; i++)
-		dmar_release_intr(dev, unit, i);
+		iommu_release_intr(DMAR2IOMMU(unit), i);
 	if (unit->regs != NULL) {
 		bus_deactivate_resource(dev, SYS_RES_MEMORY, unit->reg_rid,
 		    unit->regs);
@@ -274,84 +258,19 @@ dmar_release_resources(device_t dev, struct dmar_unit *unit)
 	}
 }
 
-static int
-dmar_alloc_irq(device_t dev, struct dmar_unit *unit, int idx)
-{
-	device_t pcib;
-	struct dmar_msi_data *dmd;
-	uint64_t msi_addr;
-	uint32_t msi_data;
-	int error;
-
-	dmd = &unit->intrs[idx];
-	pcib = device_get_parent(device_get_parent(dev)); /* Really not pcib */
-	error = PCIB_ALLOC_MSIX(pcib, dev, &dmd->irq);
-	if (error != 0) {
-		device_printf(dev, "cannot allocate %s interrupt, %d\n",
-		    dmd->name, error);
-		goto err1;
-	}
-	error = bus_set_resource(dev, SYS_RES_IRQ, dmd->irq_rid,
-	    dmd->irq, 1);
-	if (error != 0) {
-		device_printf(dev, "cannot set %s interrupt resource, %d\n",
-		    dmd->name, error);
-		goto err2;
-	}
-	dmd->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
-	    &dmd->irq_rid, RF_ACTIVE);
-	if (dmd->irq_res == NULL) {
-		device_printf(dev,
-		    "cannot allocate resource for %s interrupt\n", dmd->name);
-		error = ENXIO;
-		goto err3;
-	}
-	error = bus_setup_intr(dev, dmd->irq_res, INTR_TYPE_MISC,
-	    dmd->handler, NULL, unit, &dmd->intr_handle);
-	if (error != 0) {
-		device_printf(dev, "cannot setup %s interrupt, %d\n",
-		    dmd->name, error);
-		goto err4;
-	}
-	bus_describe_intr(dev, dmd->irq_res, dmd->intr_handle, "%s", dmd->name);
-	error = PCIB_MAP_MSI(pcib, dev, dmd->irq, &msi_addr, &msi_data);
-	if (error != 0) {
-		device_printf(dev, "cannot map %s interrupt, %d\n",
-		    dmd->name, error);
-		goto err5;
-	}
-	dmar_write4(unit, dmd->msi_data_reg, msi_data);
-	dmar_write4(unit, dmd->msi_addr_reg, msi_addr);
-	/* Only for xAPIC mode */
-	dmar_write4(unit, dmd->msi_uaddr_reg, msi_addr >> 32);
-	return (0);
-
-err5:
-	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
-err4:
-	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
-err3:
-	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
-err2:
-	PCIB_RELEASE_MSIX(pcib, dev, dmd->irq);
-	dmd->irq = -1;
-err1:
-	return (error);
-}
-
 #ifdef DEV_APIC
 static int
 dmar_remap_intr(device_t dev, device_t child, u_int irq)
 {
 	struct dmar_unit *unit;
-	struct dmar_msi_data *dmd;
+	struct iommu_msi_data *dmd;
 	uint64_t msi_addr;
 	uint32_t msi_data;
 	int i, error;
 
 	unit = device_get_softc(dev);
 	for (i = 0; i < DMAR_INTR_TOTAL; i++) {
-		dmd = &unit->intrs[i];
+		dmd = &unit->x86c.intrs[i];
 		if (irq == dmd->irq) {
 			error = PCIB_MAP_MSI(device_get_parent(
 			    device_get_parent(dev)),
@@ -359,11 +278,14 @@ dmar_remap_intr(device_t dev, device_t child, u_int irq)
 			if (error != 0)
 				return (error);
 			DMAR_LOCK(unit);
-			(dmd->disable_intr)(unit);
-			dmar_write4(unit, dmd->msi_data_reg, msi_data);
-			dmar_write4(unit, dmd->msi_addr_reg, msi_addr);
-			dmar_write4(unit, dmd->msi_uaddr_reg, msi_addr >> 32);
-			(dmd->enable_intr)(unit);
+			dmd->msi_data = msi_data;
+			dmd->msi_addr = msi_addr;
+			(dmd->disable_intr)(DMAR2IOMMU(unit));
+			dmar_write4(unit, dmd->msi_data_reg, dmd->msi_data);
+			dmar_write4(unit, dmd->msi_addr_reg, dmd->msi_addr);
+			dmar_write4(unit, dmd->msi_uaddr_reg,
+			    dmd->msi_addr >> 32);
+			(dmd->enable_intr)(DMAR2IOMMU(unit));
 			DMAR_UNLOCK(unit);
 			return (0);
 		}
@@ -407,6 +329,7 @@ dmar_attach(device_t dev)
 {
 	struct dmar_unit *unit;
 	ACPI_DMAR_HARDWARE_UNIT *dmaru;
+	struct iommu_msi_data *dmd;
 	uint64_t timeout;
 	int disable_pmr;
 	int i, error;
@@ -439,37 +362,47 @@ dmar_attach(device_t dev)
 	dmar_update_timeout(timeout);
 
 	for (i = 0; i < DMAR_INTR_TOTAL; i++)
-		unit->intrs[i].irq = -1;
-
-	unit->intrs[DMAR_INTR_FAULT].name = "fault";
-	unit->intrs[DMAR_INTR_FAULT].irq_rid = DMAR_FAULT_IRQ_RID;
-	unit->intrs[DMAR_INTR_FAULT].handler = dmar_fault_intr;
-	unit->intrs[DMAR_INTR_FAULT].msi_data_reg = DMAR_FEDATA_REG;
-	unit->intrs[DMAR_INTR_FAULT].msi_addr_reg = DMAR_FEADDR_REG;
-	unit->intrs[DMAR_INTR_FAULT].msi_uaddr_reg = DMAR_FEUADDR_REG;
-	unit->intrs[DMAR_INTR_FAULT].enable_intr = dmar_enable_fault_intr;
-	unit->intrs[DMAR_INTR_FAULT].disable_intr = dmar_disable_fault_intr;
-	error = dmar_alloc_irq(dev, unit, DMAR_INTR_FAULT);
+		unit->x86c.intrs[i].irq = -1;
+
+	dmd = &unit->x86c.intrs[DMAR_INTR_FAULT];
+	dmd->name = "fault";
+	dmd->irq_rid = DMAR_FAULT_IRQ_RID;
+	dmd->handler = dmar_fault_intr;
+	dmd->msi_data_reg = DMAR_FEDATA_REG;
+	dmd->msi_addr_reg = DMAR_FEADDR_REG;
+	dmd->msi_uaddr_reg = DMAR_FEUADDR_REG;
+	dmd->enable_intr = dmar_enable_fault_intr;
+	dmd->disable_intr = dmar_disable_fault_intr;
+	error = iommu_alloc_irq(DMAR2IOMMU(unit), DMAR_INTR_FAULT);
 	if (error != 0) {
 		dmar_release_resources(dev, unit);
 		dmar_devs[unit->iommu.unit] = NULL;
 		return (error);
 	}
+	dmar_write4(unit, dmd->msi_data_reg, dmd->msi_data);
+	dmar_write4(unit, dmd->msi_addr_reg, dmd->msi_addr);
+	dmar_write4(unit, dmd->msi_uaddr_reg, dmd->msi_addr >> 32);
+
 	if (DMAR_HAS_QI(unit)) {
-		unit->intrs[DMAR_INTR_QI].name = "qi";
-		unit->intrs[DMAR_INTR_QI].irq_rid = DMAR_QI_IRQ_RID;
-		unit->intrs[DMAR_INTR_QI].handler = dmar_qi_intr;
-		unit->intrs[DMAR_INTR_QI].msi_data_reg = DMAR_IEDATA_REG;
-		unit->intrs[DMAR_INTR_QI].msi_addr_reg = DMAR_IEADDR_REG;
-		unit->intrs[DMAR_INTR_QI].msi_uaddr_reg = DMAR_IEUADDR_REG;
-		unit->intrs[DMAR_INTR_QI].enable_intr = dmar_enable_qi_intr;
-		unit->intrs[DMAR_INTR_QI].disable_intr = dmar_disable_qi_intr;
-		error = dmar_alloc_irq(dev, unit, DMAR_INTR_QI);
+		dmd = &unit->x86c.intrs[DMAR_INTR_QI];
+		dmd->name = "qi";
+		dmd->irq_rid = DMAR_QI_IRQ_RID;
+		dmd->handler = dmar_qi_intr;
+		dmd->msi_data_reg = DMAR_IEDATA_REG;
+		dmd->msi_addr_reg = DMAR_IEADDR_REG;
+		dmd->msi_uaddr_reg = DMAR_IEUADDR_REG;
+		dmd->enable_intr = dmar_enable_qi_intr;
+		dmd->disable_intr = dmar_disable_qi_intr;
+		error = iommu_alloc_irq(DMAR2IOMMU(unit), DMAR_INTR_QI);
 		if (error != 0) {
 			dmar_release_resources(dev, unit);
 			dmar_devs[unit->iommu.unit] = NULL;
 			return (error);
 		}
+
+		dmar_write4(unit, dmd->msi_data_reg, dmd->msi_data);
+		dmar_write4(unit, dmd->msi_addr_reg, dmd->msi_addr);
+		dmar_write4(unit, dmd->msi_uaddr_reg, dmd->msi_addr >> 32);
 	}
 
 	mtx_init(&unit->iommu.lock, "dmarhw", NULL, MTX_DEF);
diff --git a/sys/x86/iommu/intel_fault.c b/sys/x86/iommu/intel_fault.c
index 59b482720cf1..1064165ea5d7 100644
--- a/sys/x86/iommu/intel_fault.c
+++ b/sys/x86/iommu/intel_fault.c
@@ -127,7 +127,7 @@ dmar_fault_intr(void *arg)
 	int fri, frir, faultp;
 	bool enqueue;
 
-	unit = arg;
+	unit = IOMMU2DMAR((struct iommu_unit *)arg);
 	enqueue = false;
 	fsts = dmar_read4(unit, DMAR_FSTS_REG);
 	dmar_fault_intr_clear(unit, fsts);
@@ -276,9 +276,9 @@ dmar_init_fault_log(struct dmar_unit *unit)
 	    "dmar%d fault taskq", unit->iommu.unit);
 
 	DMAR_LOCK(unit);
-	dmar_disable_fault_intr(unit);
+	dmar_disable_fault_intr(&unit->iommu);
 	dmar_clear_faults(unit);
-	dmar_enable_fault_intr(unit);
+	dmar_enable_fault_intr(&unit->iommu);
 	DMAR_UNLOCK(unit);
 
 	return (0);
@@ -292,7 +292,7 @@ dmar_fini_fault_log(struct dmar_unit *unit)
 		return;
 
 	DMAR_LOCK(unit);
-	dmar_disable_fault_intr(unit);
+	dmar_disable_fault_intr(&unit->iommu);
 	DMAR_UNLOCK(unit);
 
 	taskqueue_drain(unit->fault_taskqueue, &unit->fault_task);
@@ -306,10 +306,12 @@ dmar_fini_fault_log(struct dmar_unit *unit)
 }
 
 void
-dmar_enable_fault_intr(struct dmar_unit *unit)
+dmar_enable_fault_intr(struct iommu_unit *iommu)
 {
+	struct dmar_unit *unit;
 	uint32_t fectl;
 
+	unit = IOMMU2DMAR(iommu);
 	DMAR_ASSERT_LOCKED(unit);
 	fectl = dmar_read4(unit, DMAR_FECTL_REG);
 	fectl &= ~DMAR_FECTL_IM;
@@ -317,10 +319,12 @@ dmar_enable_fault_intr(struct dmar_unit *unit)
 }
 
 void
-dmar_disable_fault_intr(struct dmar_unit *unit)
+dmar_disable_fault_intr(struct iommu_unit *iommu)
 {
+	struct dmar_unit *unit;
 	uint32_t fectl;
 
+	unit = IOMMU2DMAR(iommu);
 	DMAR_ASSERT_LOCKED(unit);
 	fectl = dmar_read4(unit, DMAR_FECTL_REG);
 	dmar_write4(unit, DMAR_FECTL_REG, fectl | DMAR_FECTL_IM);
diff --git a/sys/x86/iommu/intel_qi.c b/sys/x86/iommu/intel_qi.c
index 8b5cfe3c0205..c11946ad9447 100644
--- a/sys/x86/iommu/intel_qi.c
+++ b/sys/x86/iommu/intel_qi.c
@@ -293,7 +293,7 @@ dmar_qi_intr(void *arg)
 {
 	struct dmar_unit *unit;
 
-	unit = arg;
+	unit = IOMMU2DMAR((struct iommu_unit *)arg);
 	KASSERT(unit->qi_enabled, ("dmar%d: QI is not enabled",
 	    unit->iommu.unit));
 	taskqueue_enqueue(unit->x86c.qi_taskqueue, &unit->x86c.qi_task);
@@ -373,7 +373,7 @@ dmar_init_qi(struct dmar_unit *unit)
 		ics = DMAR_ICS_IWC;
 		dmar_write4(unit, DMAR_ICS_REG, ics);
 	}
-	dmar_enable_qi_intr(unit);
+	dmar_enable_qi_intr(DMAR2IOMMU(unit));
 	DMAR_UNLOCK(unit);
 
 	return (0);
@@ -382,7 +382,7 @@ dmar_init_qi(struct dmar_unit *unit)
 static void
 dmar_fini_qi_helper(struct iommu_unit *iommu)
 {
-	dmar_disable_qi_intr(IOMMU2DMAR(iommu));
+	dmar_disable_qi_intr(iommu);
 	dmar_disable_qi(IOMMU2DMAR(iommu));
 }
 
@@ -396,10 +396,12 @@ dmar_fini_qi(struct dmar_unit *unit)
 }
 
 void
-dmar_enable_qi_intr(struct dmar_unit *unit)
+dmar_enable_qi_intr(struct iommu_unit *iommu)
 {
+	struct dmar_unit *unit;
 	uint32_t iectl;
 
+	unit = IOMMU2DMAR(iommu);
 	DMAR_ASSERT_LOCKED(unit);
 	KASSERT(DMAR_HAS_QI(unit), ("dmar%d: QI is not supported",
 	    unit->iommu.unit));
@@ -409,10 +411,12 @@ dmar_enable_qi_intr(struct dmar_unit *unit)
 }
 
 void
-dmar_disable_qi_intr(struct dmar_unit *unit)
+dmar_disable_qi_intr(struct iommu_unit *iommu)
 {
+	struct dmar_unit *unit;
 	uint32_t iectl;
 
+	unit = IOMMU2DMAR(iommu);
 	DMAR_ASSERT_LOCKED(unit);
 	KASSERT(DMAR_HAS_QI(unit), ("dmar%d: QI is not supported",
 	    unit->iommu.unit));
diff --git a/sys/x86/iommu/iommu_utils.c b/sys/x86/iommu/iommu_utils.c
index 571e5a2e65cd..04d42799310e 100644
--- a/sys/x86/iommu/iommu_utils.c
+++ b/sys/x86/iommu/iommu_utils.c
@@ -28,7 +28,15 @@
  * SUCH DAMAGE.
  */
 
+#include "opt_acpi.h"
+#if defined(__amd64__)
+#define	DEV_APIC
+#else
+#include "opt_apic.h"
+#endif
+
 #include <sys/systm.h>
+#include <sys/bus.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
@@ -38,6 +46,7 @@
 #include <sys/sysctl.h>
 #include <sys/proc.h>
 #include <sys/sched.h>
+#include <sys/rman.h>
 #include <sys/rwlock.h>
 #include <sys/taskqueue.h>
 #include <sys/tree.h>
@@ -48,6 +57,7 @@
 #include <vm/vm_object.h>
 #include <vm/vm_page.h>
 #include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
 #include <machine/atomic.h>
 #include <machine/bus.h>
 #include <machine/cpu.h>
@@ -56,6 +66,12 @@
 #include <dev/iommu/iommu.h>
 #include <x86/iommu/x86_iommu.h>
 #include <x86/iommu/iommu_intrmap.h>
+#ifdef DEV_APIC
+#include "pcib_if.h"
+#include <machine/intr_machdep.h>
+#include <x86/apicreg.h>
+#include <x86/apicvar.h>
+#endif
 
 vm_page_t
 iommu_pgalloc(vm_object_t obj, vm_pindex_t idx, int flags)
@@ -483,3 +499,92 @@ iommu_qi_common_fini(struct iommu_unit *unit, void (*disable_qi)(
 	x86c->inv_queue = NULL;
 	x86c->inv_queue_size = 0;
 }
+
+int
+iommu_alloc_irq(struct iommu_unit *unit, int idx)
+{
+	device_t dev, pcib;
+	struct iommu_msi_data *dmd;
+	uint64_t msi_addr;
+	uint32_t msi_data;
+	int error;
+
+	MPASS(idx >= 0 || idx < IOMMU_MAX_MSI);
+
+	dev = unit->dev;
+	dmd = &IOMMU2X86C(unit)->intrs[idx];
+	pcib = device_get_parent(device_get_parent(dev)); /* Really not pcib */
+	error = PCIB_ALLOC_MSIX(pcib, dev, &dmd->irq);
+	if (error != 0) {
+		device_printf(dev, "cannot allocate %s interrupt, %d\n",
+		    dmd->name, error);
+		goto err1;
+	}
+	error = bus_set_resource(dev, SYS_RES_IRQ, dmd->irq_rid,
+	    dmd->irq, 1);
+	if (error != 0) {
+		device_printf(dev, "cannot set %s interrupt resource, %d\n",
+		    dmd->name, error);
+		goto err2;
+	}
+	dmd->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+	    &dmd->irq_rid, RF_ACTIVE);
+	if (dmd->irq_res == NULL) {
+		device_printf(dev,
+		    "cannot allocate resource for %s interrupt\n", dmd->name);
+		error = ENXIO;
+		goto err3;
+	}
+	error = bus_setup_intr(dev, dmd->irq_res, INTR_TYPE_MISC,
+	    dmd->handler, NULL, unit, &dmd->intr_handle);
+	if (error != 0) {
+		device_printf(dev, "cannot setup %s interrupt, %d\n",
+		    dmd->name, error);
+		goto err4;
+	}
+	bus_describe_intr(dev, dmd->irq_res, dmd->intr_handle, "%s", dmd->name);
+	error = PCIB_MAP_MSI(pcib, dev, dmd->irq, &msi_addr, &msi_data);
+	if (error != 0) {
+		device_printf(dev, "cannot map %s interrupt, %d\n",
+		    dmd->name, error);
+		goto err5;
+	}
+
+	dmd->msi_data = msi_data;
+	dmd->msi_addr = msi_addr;
+
+	return (0);
+
+err5:
+	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
+err4:
+	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
+err3:
+	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
+err2:
+	PCIB_RELEASE_MSIX(pcib, dev, dmd->irq);
+	dmd->irq = -1;
+err1:
+	return (error);
+}
+
+void
+iommu_release_intr(struct iommu_unit *unit, int idx)
+{
+	device_t dev;
+	struct iommu_msi_data *dmd;
+
+	MPASS(idx >= 0 || idx < IOMMU_MAX_MSI);
+
+	dmd = &IOMMU2X86C(unit)->intrs[idx];
+	if (dmd->handler == NULL || dmd->irq == -1)
+		return;
+	dev = unit->dev;
+
+	bus_teardown_intr(dev, dmd->irq_res, dmd->intr_handle);
+	bus_release_resource(dev, SYS_RES_IRQ, dmd->irq_rid, dmd->irq_res);
+	bus_delete_resource(dev, SYS_RES_IRQ, dmd->irq_rid);
+	PCIB_RELEASE_MSIX(device_get_parent(device_get_parent(dev)),
+	    dev, dmd->irq);
+	dmd->irq = -1;
+}
diff --git a/sys/x86/iommu/x86_iommu.h b/sys/x86/iommu/x86_iommu.h
index 966a13c19b6e..d6e3ea56bd2c 100644
--- a/sys/x86/iommu/x86_iommu.h
+++ b/sys/x86/iommu/x86_iommu.h
@@ -93,6 +93,24 @@ struct x86_iommu {
 void set_x86_iommu(struct x86_iommu *);
 struct x86_iommu *get_x86_iommu(void);
 
+struct iommu_msi_data {
+	int irq;
+	int irq_rid;
+	struct resource *irq_res;
+	void *intr_handle;
+	int (*handler)(void *);
+	int msi_data_reg;
+	int msi_addr_reg;
+	int msi_uaddr_reg;
+	uint64_t msi_addr;
+	uint32_t msi_data;
+	void (*enable_intr)(struct iommu_unit *);
+	void (*disable_intr)(struct iommu_unit *);
+	const char *name;
+};
+
+#define	IOMMU_MAX_MSI	3
+
 struct x86_unit_common {
 	uint32_t qi_buf_maxsz;
 	uint32_t qi_cmd_sz;
@@ -145,6 +163,8 @@ struct x86_unit_common {
 	struct iommu_map_entry *tlb_flush_tail;
 	struct task qi_task;
 	struct taskqueue *qi_taskqueue;
+
+	struct iommu_msi_data intrs[IOMMU_MAX_MSI];
 };
 
 void iommu_qi_emit_wait_seq(struct iommu_unit *unit, struct iommu_qi_genseq *
@@ -160,4 +180,7 @@ void iommu_qi_common_init(struct iommu_unit *unit, task_fn_t taskfunc);
 void iommu_qi_common_fini(struct iommu_unit *unit, void (*disable_qi)(
     struct iommu_unit *));
 
+int iommu_alloc_irq(struct iommu_unit *unit, int idx);
+void iommu_release_intr(struct iommu_unit *unit, int idx);
+
 #endif