* [PATCH v3 01/22] iommu/amd: Make amd_iommu_completion_wait() non-static
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 02/22] iommu/amd: Introduce vIOMMU-specific events and event Suravee Suthikulpanit
` (21 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
This will be reused in a new iommufd.c file for nested translation.
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 1 +
drivers/iommu/amd/iommu.c | 26 ++++++++++++--------------
2 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index af720bf14914..9f961ccbe3b4 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -199,6 +199,7 @@ void amd_iommu_set_dte_v1(struct iommu_dev_data *dev_data,
void amd_iommu_update_dte(struct amd_iommu *iommu,
struct iommu_dev_data *dev_data,
struct dev_table_entry *new);
+int amd_iommu_completion_wait(struct amd_iommu *iommu);
static inline void
amd_iommu_make_clear_dte(struct iommu_dev_data *dev_data, struct dev_table_entry *new)
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 84cad43dc188..4b4dd20ebec6 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -90,8 +90,6 @@ static int amd_iommu_set_dirty_tracking(struct iommu_domain *domain,
static void clone_aliases(struct amd_iommu *iommu, struct device *dev);
-static int iommu_completion_wait(struct amd_iommu *iommu);
-
/****************************************************************************
*
* Helper functions
@@ -216,7 +214,7 @@ void amd_iommu_update_dte(struct amd_iommu *iommu,
update_dte256(iommu, dev_data, new);
clone_aliases(iommu, dev_data->dev);
device_flush_dte(dev_data);
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
}
static void get_dte256(struct amd_iommu *iommu, struct iommu_dev_data *dev_data,
@@ -1449,7 +1447,7 @@ static u64 get_cmdsem_val(struct amd_iommu *iommu)
* This function queues a completion wait command into the command
* buffer of an IOMMU
*/
-static int iommu_completion_wait(struct amd_iommu *iommu)
+int amd_iommu_completion_wait(struct amd_iommu *iommu)
{
struct iommu_cmd cmd;
unsigned long flags;
@@ -1487,7 +1485,7 @@ static void domain_flush_complete(struct protection_domain *domain)
* We need to wait for completion of all commands.
*/
xa_for_each(&domain->iommu_array, i, pdom_iommu_info)
- iommu_completion_wait(pdom_iommu_info->iommu);
+ amd_iommu_completion_wait(pdom_iommu_info->iommu);
}
static int iommu_flush_dte(struct amd_iommu *iommu, u16 devid)
@@ -1505,7 +1503,7 @@ static void iommu_flush_dte_sync(struct amd_iommu *iommu, u16 devid)
ret = iommu_flush_dte(iommu, devid);
if (!ret)
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
}
static void amd_iommu_flush_dte_all(struct amd_iommu *iommu)
@@ -1516,7 +1514,7 @@ static void amd_iommu_flush_dte_all(struct amd_iommu *iommu)
for (devid = 0; devid <= last_bdf; ++devid)
iommu_flush_dte(iommu, devid);
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
}
/*
@@ -1535,7 +1533,7 @@ static void amd_iommu_flush_tlb_all(struct amd_iommu *iommu)
iommu_queue_command(iommu, &cmd);
}
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
}
static void amd_iommu_flush_tlb_domid(struct amd_iommu *iommu, u32 dom_id)
@@ -1546,7 +1544,7 @@ static void amd_iommu_flush_tlb_domid(struct amd_iommu *iommu, u32 dom_id)
dom_id, IOMMU_NO_PASID, false);
iommu_queue_command(iommu, &cmd);
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
}
static int iommu_flush_pages_v1_hdom_ids(struct protection_domain *pdom, u64 address, size_t size)
@@ -1582,7 +1580,7 @@ static void amd_iommu_flush_all(struct amd_iommu *iommu)
build_inv_all(&cmd);
iommu_queue_command(iommu, &cmd);
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
}
static void iommu_flush_irt(struct amd_iommu *iommu, u16 devid)
@@ -1605,7 +1603,7 @@ static void amd_iommu_flush_irt_all(struct amd_iommu *iommu)
for (devid = 0; devid <= last_bdf; devid++)
iommu_flush_irt(iommu, devid);
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
}
void amd_iommu_flush_all_caches(struct amd_iommu *iommu)
@@ -1841,7 +1839,7 @@ void amd_iommu_dev_flush_pasid_pages(struct iommu_dev_data *dev_data,
if (dev_data->ats_enabled)
device_flush_iotlb(dev_data, address, size, pasid, true);
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
}
static void dev_flush_pasid_all(struct iommu_dev_data *dev_data,
@@ -2495,7 +2493,7 @@ static struct iommu_device *amd_iommu_probe_device(struct device *dev)
goto out_err;
}
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
if (FEATURE_NUM_INT_REMAP_SUP_2K(amd_iommu_efr2))
dev_data->max_irqs = MAX_IRQS_PER_TABLE_2K;
@@ -3392,7 +3390,7 @@ static struct irq_remap_table *alloc_irq_table(struct amd_iommu *iommu,
set_remap_table_entry(iommu, alias, table);
out_wait:
- iommu_completion_wait(iommu);
+ amd_iommu_completion_wait(iommu);
out_unlock:
spin_unlock_irqrestore(&iommu_table_lock, flags);
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 02/22] iommu/amd: Introduce vIOMMU-specific events and event
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 01/22] iommu/amd: Make amd_iommu_completion_wait() non-static Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 03/22] iommu/amd: Detect and initialize AMD vIOMMU feature Suravee Suthikulpanit
` (20 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Adding support for new vIOMMU events:
* Guest Event Fault event
* vIOMMU Hardware Error event
Also, adding support for the additional vIOMMU related flags
in existing events.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu_types.h | 7 ++++
drivers/iommu/amd/iommu.c | 58 ++++++++++++++++++++++-------
2 files changed, 52 insertions(+), 13 deletions(-)
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index d2c64e2e9f05..4df6a50128de 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -149,6 +149,9 @@
#define EVENT_TYPE_IOTLB_INV_TO 0x7
#define EVENT_TYPE_INV_DEV_REQ 0x8
#define EVENT_TYPE_INV_PPR_REQ 0x9
+#define EVENT_TYPE_GUEST_EVENT_FAULT 0xb
+#define EVENT_TYPE_VIOMMU_HW_ERR 0xc
+
#define EVENT_TYPE_RMP_FAULT 0xd
#define EVENT_TYPE_RMP_HW_ERR 0xe
#define EVENT_DEVID_MASK 0xffff
@@ -261,6 +264,10 @@
#define EVTLOG_SIZE_MAX SZ_512K /* 32K entries */
#define EVTLOG_LEN_MASK_MAX (0xFULL << EVTLOG_SIZE_SHIFT)
+/* Constants for IO_PAGE_FAULT event */
+#define IO_PAGE_FAULT_VFLAGS_SHIFT 27
+#define IO_PAGE_FAULT_VFLAGS_MASK GENMASK_ULL(27, 23)
+
/* Constants for PPR Log handling */
#define PPRLOG_ENTRY_SIZE 0x10
#define PPRLOG_SIZE_SHIFT 56
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 4b4dd20ebec6..50f26c8123f3 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -854,7 +854,7 @@ static void amd_iommu_report_rmp_fault(struct amd_iommu *iommu, volatile u32 *ev
static void amd_iommu_report_page_fault(struct amd_iommu *iommu,
u16 devid, u16 domain_id,
- u64 address, int flags)
+ u64 address, int flags, u8 vflags)
{
struct iommu_dev_data *dev_data = NULL;
struct pci_dev *pdev;
@@ -889,13 +889,13 @@ static void amd_iommu_report_page_fault(struct amd_iommu *iommu,
}
if (__ratelimit(&dev_data->rs)) {
- pci_err(pdev, "Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%llx flags=0x%04x]\n",
- domain_id, address, flags);
+ pci_err(pdev, "Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%llx flags=0x%04x vflags=%#x]\n",
+ domain_id, address, flags, vflags);
}
} else {
- pr_err_ratelimited("Event logged [IO_PAGE_FAULT device=%04x:%02x:%02x.%x domain=0x%04x address=0x%llx flags=0x%04x]\n",
+ pr_err_ratelimited("Event logged [IO_PAGE_FAULT device=%04x:%02x:%02x.%x domain=0x%04x address=0x%llx flags=0x%04x vflags=%#x]\n",
iommu->pci_seg->id, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
- domain_id, address, flags);
+ domain_id, address, flags, vflags);
}
out:
@@ -932,29 +932,42 @@ static void iommu_print_event(struct amd_iommu *iommu, void *__evt)
}
if (type == EVENT_TYPE_IO_FAULT) {
- amd_iommu_report_page_fault(iommu, devid, pasid, address, flags);
+ u8 vflags = FIELD_GET(IO_PAGE_FAULT_VFLAGS_MASK, event[0]);
+
+ amd_iommu_report_page_fault(iommu, devid, pasid, address, flags, vflags);
return;
}
switch (type) {
case EVENT_TYPE_ILL_DEV:
- dev_err(dev, "Event logged [ILLEGAL_DEV_TABLE_ENTRY device=%04x:%02x:%02x.%x pasid=0x%05x address=0x%llx flags=0x%04x]\n",
+ {
+ u8 vflags = FIELD_GET(IO_PAGE_FAULT_VFLAGS_MASK, event[0]);
+
+ dev_err(dev, "Event logged [ILLEGAL_DEV_TABLE_ENTRY deice=%04x:%02x:%02x.%x pasid=0x%05x address=0x%llx flags=0x%04x vflags=%#x]\n",
iommu->pci_seg->id, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
- pasid, address, flags);
+ pasid, address, flags, vflags);
dev_err(dev, "Control Reg : 0x%llx\n", ctrl);
dump_dte_entry(iommu, devid);
break;
+ }
case EVENT_TYPE_DEV_TAB_ERR:
- dev_err(dev, "Event logged [DEV_TAB_HARDWARE_ERROR device=%04x:%02x:%02x.%x "
- "address=0x%llx flags=0x%04x]\n",
+ {
+ u8 vflags = FIELD_GET(IO_PAGE_FAULT_VFLAGS_MASK, event[0]);
+
+ dev_err(dev, "Event logged [DEV_TAB_HARDWARE_ERROR device=%04x:%02x:%02x.%x address=%#llx flags=%#04x vlfags=%#x]\n",
iommu->pci_seg->id, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
- address, flags);
+ address, flags, vflags);
break;
+ }
case EVENT_TYPE_PAGE_TAB_ERR:
- dev_err(dev, "Event logged [PAGE_TAB_HARDWARE_ERROR device=%04x:%02x:%02x.%x pasid=0x%04x address=0x%llx flags=0x%04x]\n",
+ {
+ u8 vflags = FIELD_GET(IO_PAGE_FAULT_VFLAGS_MASK, event[0]);
+
+ dev_err(dev, "Event logged [PAGE_TAB_HARDWARE_ERROR device=%04x:%02x:%02x.%x pasid=0x%04x address=0x%llx flags=0x%04x vflags=%#x]\n",
iommu->pci_seg->id, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
- pasid, address, flags);
+ pasid, address, flags, vflags);
break;
+ }
case EVENT_TYPE_ILL_CMD:
dev_err(dev, "Event logged [ILLEGAL_COMMAND_ERROR address=0x%llx]\n", address);
dump_command(address);
@@ -986,6 +999,25 @@ static void iommu_print_event(struct amd_iommu *iommu, void *__evt)
iommu->pci_seg->id, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
pasid, address, flags, tag);
break;
+ case EVENT_TYPE_GUEST_EVENT_FAULT:
+ {
+ u8 gid = event[1] & 0xFFFF;
+ u8 vflags = FIELD_GET(IO_PAGE_FAULT_VFLAGS_MASK, event[0]);
+
+ dev_err(dev, "Event logged [GUEST_EVENT_FAULT gid=#%x flags=0x%04x vflags=%#x]\n",
+ gid, flags, vflags);
+ break;
+ }
+ case EVENT_TYPE_VIOMMU_HW_ERR:
+ {
+ u16 gid = event[0] & 0xFFFF;
+ u8 src = (event[0] >> 16) & 0x3;
+ u8 vflags = FIELD_GET(IO_PAGE_FAULT_VFLAGS_MASK, event[0]);
+
+ dev_err(dev, "Event logged [VIOMMU_HW_ERR gid=%#x address=%#llx src=%#x flags=%#x vflags=%#x]\n",
+ gid, address, src, flags, vflags);
+ break;
+ }
default:
dev_err(dev, "Event logged [UNKNOWN event[0]=0x%08x event[1]=0x%08x event[2]=0x%08x event[3]=0x%08x\n",
event[0], event[1], event[2], event[3]);
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 03/22] iommu/amd: Detect and initialize AMD vIOMMU feature
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 01/22] iommu/amd: Make amd_iommu_completion_wait() non-static Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 02/22] iommu/amd: Introduce vIOMMU-specific events and event Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 04/22] iommu/amd: Introduce IOMMUFD vIOMMU support for AMD Suravee Suthikulpanit
` (19 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
The feature is advertised w/ EFR[VIOMMUSup]. Please see the AMD IOMMU
specification[1] for more detail.
Introduce a new global variable amd_iommu_viommu, which is used to
control the feature enablement in the driver. Currently, the feature
is default to disabled. Once the feature is fully supported, it will be
changed to enabled by default.
[1] https://docs.amd.com/v/u/en-US/48882_3.11_IOMMU_PUB
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/Makefile | 2 +-
drivers/iommu/amd/amd_iommu.h | 2 ++
drivers/iommu/amd/amd_iommu_types.h | 1 +
drivers/iommu/amd/amd_viommu.h | 22 ++++++++++++++++++++++
drivers/iommu/amd/init.c | 15 +++++++++++++++
drivers/iommu/amd/viommu.c | 29 +++++++++++++++++++++++++++++
6 files changed, 70 insertions(+), 1 deletion(-)
create mode 100644 drivers/iommu/amd/amd_viommu.h
create mode 100644 drivers/iommu/amd/viommu.c
diff --git a/drivers/iommu/amd/Makefile b/drivers/iommu/amd/Makefile
index 94b8ef2acb18..e1e824b9c7b0 100644
--- a/drivers/iommu/amd/Makefile
+++ b/drivers/iommu/amd/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += iommu.o init.o quirks.o ppr.o pasid.o
-obj-$(CONFIG_AMD_IOMMU_IOMMUFD) += iommufd.o nested.o
+obj-$(CONFIG_AMD_IOMMU_IOMMUFD) += iommufd.o nested.o viommu.o
obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += debugfs.o
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 9f961ccbe3b4..17fc0b5b3fa8 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -35,6 +35,8 @@ void amd_iommu_debugfs_setup(void);
static inline void amd_iommu_debugfs_setup(void) {}
#endif
+extern bool amd_iommu_viommu;
+
/* Needed for interrupt remapping */
int amd_iommu_prepare(void);
int amd_iommu_enable(void);
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 4df6a50128de..b5327bf6814b 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -103,6 +103,7 @@
#define FEATURE_HASUP BIT_ULL(49)
#define FEATURE_EPHSUP BIT_ULL(50)
#define FEATURE_HDSUP BIT_ULL(52)
+#define FEATURE_VIOMMU BIT_ULL(55)
#define FEATURE_SNP BIT_ULL(63)
diff --git a/drivers/iommu/amd/amd_viommu.h b/drivers/iommu/amd/amd_viommu.h
new file mode 100644
index 000000000000..f08ab9ef23a9
--- /dev/null
+++ b/drivers/iommu/amd/amd_viommu.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#ifndef AMD_VIOMMU_H
+#define AMD_VIOMMU_H
+
+#if IS_ENABLED(CONFIG_AMD_IOMMU_IOMMUFD)
+
+int amd_viommu_init(struct amd_iommu *iommu);
+
+#else
+
+static inline int amd_viommu_init(struct amd_iommu *iommu)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_AMD_IOMMU_IOMMUFD */
+
+#endif /* AMD_VIOMMU_H */
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index d4dc9b2a50f3..5ac883429ced 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -34,6 +34,7 @@
#include <linux/crash_dump.h>
#include "amd_iommu.h"
+#include "amd_viommu.h"
#include "../irq_remapping.h"
#include "../iommu-pages.h"
@@ -196,6 +197,9 @@ bool amdr_ivrs_remap_support __read_mostly;
bool amd_iommu_force_isolation __read_mostly;
+/* VIOMMU enabling flag */
+bool amd_iommu_viommu;
+
unsigned long amd_iommu_pgsize_bitmap __ro_after_init = AMD_IOMMU_PGSIZES;
enum iommu_init_state {
@@ -2188,6 +2192,12 @@ static int __init iommu_init_pci(struct amd_iommu *iommu)
if (check_feature(FEATURE_PPR) && amd_iommu_alloc_ppr_log(iommu))
return -ENOMEM;
+ ret = amd_viommu_init(iommu);
+ if (ret) {
+ pr_err("Failed to initialize vIOMMU.\n");
+ amd_iommu_viommu = false;
+ }
+
if (iommu->cap & (1UL << IOMMU_CAP_NPCACHE)) {
pr_info("Using strict mode due to virtualization\n");
iommu_set_dma_strict();
@@ -2281,6 +2291,9 @@ static void print_iommu_info(void)
if (check_feature2(FEATURE_SEVSNPIO_SUP))
pr_cont(" SEV-TIO");
+ if (check_feature(FEATURE_VIOMMU))
+ pr_cont(" vIOMMU");
+
pr_cont("\n");
}
@@ -2293,6 +2306,8 @@ static void print_iommu_info(void)
pr_info("V2 page table enabled (Paging mode : %d level)\n",
amd_iommu_gpt_level);
}
+ if (amd_iommu_viommu)
+ pr_info("AMD-Vi: vIOMMU enabled\n");
}
static int __init amd_iommu_init_pci(void)
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
new file mode 100644
index 000000000000..f4b5f96d4785
--- /dev/null
+++ b/drivers/iommu/amd/viommu.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Advanced Micro Devices, Inc.
+ */
+
+#define pr_fmt(fmt) "AMD-Vi: " fmt
+#define dev_fmt(fmt) pr_fmt(fmt)
+
+#include <linux/iommu.h>
+#include <linux/iommufd.h>
+#include <linux/amd-iommu.h>
+#include <uapi/linux/iommufd.h>
+
+#include <asm/iommu.h>
+#include <asm/set_memory.h>
+
+#include "iommufd.h"
+#include "amd_iommu.h"
+#include "amd_iommu_types.h"
+#include "amd_viommu.h"
+
+int __init amd_viommu_init(struct amd_iommu *iommu)
+{
+ if (!amd_iommu_viommu ||
+ !check_feature(FEATURE_VIOMMU))
+ return 0;
+
+ return 0;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 04/22] iommu/amd: Introduce IOMMUFD vIOMMU support for AMD
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (2 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 03/22] iommu/amd: Detect and initialize AMD vIOMMU feature Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 05/22] iommu/amd: Allocate Guest IDs for IOMMUFD vIOMMU instances Suravee Suthikulpanit
` (18 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Introduce a new enum iommu_viommu_type (IOMMU_VIOMMU_TYPE_AMD) for AMD
vIOMMU along with the struct iommu_viommu_amd, which is used to initialize
IOMMUFD vIOMMU instance when calling struct iommu_ops.viommu_init().
Also, hook up struct iomufd_viomu_ops.alloc_domain_nested to connect
nested domain allocation with AMD vIOMMU implementation.
Additional initialization will be added in subsequent patches.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/iommufd.c | 4 ++++
include/uapi/linux/iommufd.h | 10 ++++++++++
2 files changed, 14 insertions(+)
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index 52300b867c1f..eee29c26169a 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -34,6 +34,9 @@ void *amd_iommufd_hw_info(struct device *dev, u32 *length, enum iommu_hw_info_ty
size_t amd_iommufd_get_viommu_size(struct device *dev, enum iommu_viommu_type viommu_type)
{
+ if (!amd_iommu_viommu || (viommu_type != IOMMU_VIOMMU_TYPE_AMD))
+ return 0;
+
return VIOMMU_STRUCT_SIZE(struct amd_iommu_viommu, core);
}
@@ -73,5 +76,6 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)
* struct iommufd_viommu_ops - vIOMMU specific operations
*/
static const struct iommufd_viommu_ops amd_viommu_ops = {
+ .alloc_domain_nested = amd_iommu_alloc_domain_nested,
.destroy = amd_iommufd_viommu_destroy,
};
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index e998dfbd6960..52bcd92975da 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -1052,6 +1052,7 @@ struct iommu_fault_alloc {
* @IOMMU_VIOMMU_TYPE_ARM_SMMUV3: ARM SMMUv3 driver specific type
* @IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV: NVIDIA Tegra241 CMDQV (extension for ARM
* SMMUv3) enabled ARM SMMUv3 type
+ * @IOMMU_VIOMMU_TYPE_AMD: AMD HW-vIOMMU type
*/
enum iommu_viommu_type {
IOMMU_VIOMMU_TYPE_DEFAULT = 0,
@@ -1062,6 +1063,7 @@ enum iommu_viommu_type {
* VMM must wire the HYP_OWN bit to 0 in guest VINTF_CONFIG register
*/
IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV = 2,
+ IOMMU_VIOMMU_TYPE_AMD = 3,
};
/**
@@ -1080,6 +1082,14 @@ struct iommu_viommu_tegra241_cmdqv {
__aligned_u64 out_vintf_mmap_length;
};
+/**
+ * struct iommu_viommu_amd - AMD vIOMMU Interface (IOMMU_VIOMMU_TYPE_AMD)
+ * @out_vfmmio_mmap_offset: (out) mmap offset for vIOMMU VF-MMIO
+ */
+struct iommu_viommu_amd {
+ __aligned_u64 out_vfmmio_mmap_offset;
+};
+
/**
* struct iommu_viommu_alloc - ioctl(IOMMU_VIOMMU_ALLOC)
* @size: sizeof(struct iommu_viommu_alloc)
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 05/22] iommu/amd: Allocate Guest IDs for IOMMUFD vIOMMU instances
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (3 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 04/22] iommu/amd: Introduce IOMMUFD vIOMMU support for AMD Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 06/22] iommu/amd: Map vIOMMU VF and VF Control MMIO BARs Suravee Suthikulpanit
` (17 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Hardware vIOMMU uses a 16-bit Guest ID (GID) per guest IOMMU to
index driver and hardware state. Allocate one GID per IOMMUFD vIOMMU
from a per-amd_iommu IDA (unique within that IOMMU; a VM behind
multiple IOMMUs may hold more than one GID).
Add amd_iommu_gid_alloc() and amd_iommu_gid_free(), store the ID in
amd_iommu_viommu::gid, and call them from amd_iommufd_viommu_init() and
destroy after copying struct iommu_viommu_amd to userspace.
ida_init() for gid_ida is done when vIOMMU MMIO is mapped (next patch).
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 4 ++++
drivers/iommu/amd/amd_iommu_types.h | 8 ++++++++
drivers/iommu/amd/iommu.c | 19 +++++++++++++++++
drivers/iommu/amd/iommufd.c | 32 +++++++++++++++++++++++++++++
4 files changed, 63 insertions(+)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 17fc0b5b3fa8..9f2a1a8a6d3c 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -228,4 +228,8 @@ amd_iommu_make_clear_dte(struct iommu_dev_data *dev_data, struct dev_table_entry
struct iommu_domain *
amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data);
+
+/* Guest ID for vIOMMU */
+int amd_iommu_gid_alloc(struct amd_iommu *iommu);
+void amd_iommu_gid_free(struct amd_iommu *iommu, int gid);
#endif /* AMD_IOMMU_H */
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index b5327bf6814b..00f964d5b149 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -21,6 +21,7 @@
#include <linux/iommufd.h>
#include <linux/irqreturn.h>
#include <linux/generic_pt/iommu.h>
+#include <linux/idr.h>
#include <uapi/linux/iommufd.h>
@@ -413,6 +414,9 @@
#define MAX_DOMAIN_ID 65536
+/* For vIOMMU, the GID is 16-bit. */
+#define VIOMMU_MAX_GID 0xFFFF
+
/* Timeout stuff */
#define LOOP_TIMEOUT 100000
#define MMIO_STATUS_TIMEOUT 2000000
@@ -509,6 +513,7 @@ struct amd_iommu_viommu {
struct iommufd_viommu core;
struct protection_domain *parent; /* nest parent domain for this viommu */
struct list_head pdom_list; /* For protection_domain->viommu_list */
+ u16 gid; /* Guest ID for the vIOMMU */
/*
* Per-vIOMMU guest domain ID to host domain ID mapping.
@@ -768,6 +773,9 @@ struct amd_iommu {
/* IOPF support */
struct iopf_queue *iopf_queue;
unsigned char iopfq_name[32];
+
+ struct ida gid_ida; /* guest IDs for this IOMMU */
+ bool gid_ida_inited;
};
static inline struct amd_iommu *dev_to_amd_iommu(struct device *dev)
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 50f26c8123f3..73fba8be40d1 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -252,6 +252,25 @@ static inline bool pdom_is_sva_capable(struct protection_domain *pdom)
return pdom_is_v2_pgtbl_mode(pdom) || pdom_is_in_pt_mode(pdom);
}
+int amd_iommu_gid_alloc(struct amd_iommu *iommu)
+{
+ int ret = ida_alloc_range(&iommu->gid_ida, 1, VIOMMU_MAX_GID, GFP_KERNEL);
+
+ if (ret < 0)
+ pr_err("%s: Failed to allocate guest ID (devid=%#x)\n",
+ __func__, iommu->devid);
+ else
+ pr_debug("%s: iommu devid=%#x, gid=%u\n", __func__, iommu->devid, ret);
+
+ return ret;
+}
+
+void amd_iommu_gid_free(struct amd_iommu *iommu, int gid)
+{
+ pr_debug("%s: iommu devid=%#x, gid=%u\n", __func__, iommu->devid, gid);
+ ida_free(&iommu->gid_ida, gid);
+}
+
static inline int get_acpihid_device_id(struct device *dev,
struct acpihid_map_entry **entry)
{
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index eee29c26169a..ec3c14e18c7b 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -43,13 +43,37 @@ size_t amd_iommufd_get_viommu_size(struct device *dev, enum iommu_viommu_type vi
int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *parent,
const struct iommu_user_data *user_data)
{
+ int ret;
unsigned long flags;
+ struct iommu_viommu_amd data = {};
struct protection_domain *pdom = to_pdomain(parent);
struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
+ struct amd_iommu *iommu = container_of(viommu->iommu_dev, struct amd_iommu, iommu);
xa_init_flags(&aviommu->gdomid_array, XA_FLAGS_ALLOC1);
aviommu->parent = pdom;
+ if (!user_data)
+ return -EINVAL;
+
+ ret = iommu_copy_struct_from_user(&data, user_data,
+ IOMMU_VIOMMU_TYPE_AMD,
+ out_vfmmio_mmap_offset);
+ if (ret)
+ return ret;
+
+ ret = amd_iommu_gid_alloc(iommu);
+ if (ret < 0)
+ goto err_gid;
+ aviommu->gid = ret;
+ pr_debug("%s: gid=%#x", __func__, aviommu->gid);
+
+ ret = iommu_copy_struct_to_user(user_data, &data,
+ IOMMU_VIOMMU_TYPE_AMD,
+ out_vfmmio_mmap_offset);
+ if (ret)
+ goto err_init;
+
viommu->ops = &amd_viommu_ops;
spin_lock_irqsave(&pdom->lock, flags);
@@ -57,6 +81,10 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
spin_unlock_irqrestore(&pdom->lock, flags);
return 0;
+err_init:
+ amd_iommu_gid_free(iommu, aviommu->gid);
+err_gid:
+ return ret;
}
static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)
@@ -64,11 +92,15 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)
unsigned long flags;
struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
struct protection_domain *pdom = aviommu->parent;
+ struct amd_iommu *iommu = container_of(viommu->iommu_dev, struct amd_iommu, iommu);
+
+ pr_debug("%s: gid=%#x, iommu devid=%#x\n", __func__, aviommu->gid, iommu->devid);
spin_lock_irqsave(&pdom->lock, flags);
list_del(&aviommu->pdom_list);
spin_unlock_irqrestore(&pdom->lock, flags);
xa_destroy(&aviommu->gdomid_array);
+ amd_iommu_gid_free(iommu, aviommu->gid);
}
/*
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 06/22] iommu/amd: Map vIOMMU VF and VF Control MMIO BARs
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (4 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 05/22] iommu/amd: Allocate Guest IDs for IOMMUFD vIOMMU instances Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 07/22] iommu/amd: Add support for AMD vIOMMU VF MMIO region Suravee Suthikulpanit
` (16 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Enable hardware vIOMMU on an IOMMU by locating its PCI vendor-specific
capability (VSC), reading the VF and VF Control BAR addresses, and
mapping them for host access (256MB VF, 4MB VF Control).
VF Control covers the first 4K of guest IOMMU MMIO (control registers,
trapped by QEMU). VF MMIO covers the third 4K (virtualized by the
IOMMU). Per-guest bases use the Guest ID from the previous patch.
Initialize the per-amd_iommu gid_ida here so amd_iommu_gid_alloc() can
run when IOMMUFD creates a vIOMMU instance. Export MMIO map helpers and
call amd_viommu_uninit() from IOMMU teardown.
Signed-off-by: Vasant Hegde <vasant.hegde@amd.com>
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 2 +
drivers/iommu/amd/amd_iommu_types.h | 34 ++++++++
drivers/iommu/amd/amd_viommu.h | 6 ++
drivers/iommu/amd/init.c | 5 +-
drivers/iommu/amd/viommu.c | 124 ++++++++++++++++++++++++++++
5 files changed, 169 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 9f2a1a8a6d3c..044bc9a634a1 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -28,6 +28,8 @@ void amd_iommu_set_rlookup_table(struct amd_iommu *iommu, u16 devid);
void iommu_feature_enable(struct amd_iommu *iommu, u8 bit);
void *__init iommu_alloc_4k_pages(struct amd_iommu *iommu,
gfp_t gfp, size_t size);
+u8 __iomem * __init iommu_map_mmio_space(u64 address, u64 end);
+void __init iommu_unmap_mmio_space(struct amd_iommu *iommu);
#ifdef CONFIG_AMD_IOMMU_DEBUGFS
void amd_iommu_debugfs_setup(void);
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 00f964d5b149..e88e0bacd1a9 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -40,6 +40,12 @@
#define MMIO_RANGE_OFFSET 0x0c
#define MMIO_MISC_OFFSET 0x10
+/* vIOMMU Capability offsets (from IOMMU Capability Header) */
+#define MMIO_VSC_VF_BAR_LO_OFFSET 0x08
+#define MMIO_VSC_VF_BAR_HI_OFFSET 0x0c
+#define MMIO_VSC_VF_CNTL_BAR_LO_OFFSET 0x10
+#define MMIO_VSC_VF_CNTL_BAR_HI_OFFSET 0x14
+
/* Masks, shifts and macros to parse the device range capability */
#define MMIO_RANGE_LD_MASK 0xff000000
#define MMIO_RANGE_FD_MASK 0x00ff0000
@@ -473,6 +479,20 @@ extern bool amdr_ivrs_remap_support;
#define for_each_ivhd_dte_flags(entry) \
list_for_each_entry((entry), &amd_ivhd_dev_flags_list, list)
+/* VIOMMU stuff */
+#define VIOMMU_VF_MMIO_ENTRY_SIZE 4096
+#define VIOMMU_VFCTRL_MMIO_ENTRY_SIZE 64
+
+/* Host ioremap/request_mem_region sizes for VF / VF_CNTL BARs */
+#define VIOMMU_VF_MMIO_MAP_SIZE 0x10000000UL
+#define VIOMMU_VF_CNTL_MMIO_MAP_SIZE 0x400000UL
+
+#define VIOMMU_VF_MMIO_BASE(iommu, guestId) \
+ (iommu->vf_base + (guestId * VIOMMU_VF_MMIO_ENTRY_SIZE))
+
+#define VIOMMU_VFCTRL_MMIO_BASE(iommu, guestId) \
+ (iommu->vfctrl_base + (guestId * VIOMMU_VFCTRL_MMIO_ENTRY_SIZE))
+
struct amd_iommu;
struct iommu_domain;
struct irq_domain;
@@ -686,6 +706,20 @@ struct amd_iommu {
*/
u16 cap_ptr;
+ /* Vendor-Specific Capability (VSC) pointer. */
+ u16 vsc_offset;
+
+ /*
+ * VF MMIO base physical address. This is needed to calculate/pass
+ * per guest VF MMIO address (3rd 4K of IOMMU MMIO space)
+ */
+ u64 vf_base_phys;
+ u64 vf_cntl_phys;
+
+ /* virtual addresses of vIOMMU VF/VF_CNTL BAR */
+ u8 __iomem *vf_base;
+ u8 __iomem *vfctrl_base;
+
/* pci domain of this IOMMU */
struct amd_iommu_pci_seg *pci_seg;
diff --git a/drivers/iommu/amd/amd_viommu.h b/drivers/iommu/amd/amd_viommu.h
index f08ab9ef23a9..d0c4fdd00809 100644
--- a/drivers/iommu/amd/amd_viommu.h
+++ b/drivers/iommu/amd/amd_viommu.h
@@ -10,6 +10,8 @@
int amd_viommu_init(struct amd_iommu *iommu);
+void __init amd_viommu_uninit(struct amd_iommu *iommu);
+
#else
static inline int amd_viommu_init(struct amd_iommu *iommu)
@@ -17,6 +19,10 @@ static inline int amd_viommu_init(struct amd_iommu *iommu)
return -EOPNOTSUPP;
}
+static inline void amd_viommu_uninit(struct amd_iommu *iommu)
+{
+}
+
#endif /* CONFIG_AMD_IOMMU_IOMMUFD */
#endif /* AMD_VIOMMU_H */
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index 5ac883429ced..6e69b3dd8b1e 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -459,7 +459,7 @@ static void iommu_disable(struct amd_iommu *iommu)
* mapping and unmapping functions for the IOMMU MMIO space. Each AMD IOMMU in
* the system has one.
*/
-static u8 __iomem * __init iommu_map_mmio_space(u64 address, u64 end)
+u8 __iomem * __init iommu_map_mmio_space(u64 address, u64 end)
{
if (!request_mem_region(address, end, "amd_iommu")) {
pr_err("Can not reserve memory region %llx-%llx for mmio\n",
@@ -471,7 +471,7 @@ static u8 __iomem * __init iommu_map_mmio_space(u64 address, u64 end)
return (u8 __iomem *)ioremap(address, end);
}
-static void __init iommu_unmap_mmio_space(struct amd_iommu *iommu)
+void __init iommu_unmap_mmio_space(struct amd_iommu *iommu)
{
if (iommu->mmio_base)
iounmap(iommu->mmio_base);
@@ -1790,6 +1790,7 @@ static void __init free_iommu_one(struct amd_iommu *iommu)
free_iommu_buffers(iommu);
amd_iommu_free_ppr_log(iommu);
free_ga_log(iommu);
+ amd_viommu_uninit(iommu);
iommu_unmap_mmio_space(iommu);
amd_iommu_iopf_uninit(iommu);
}
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index f4b5f96d4785..014ae16bf58b 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -7,9 +7,15 @@
#define dev_fmt(fmt) pr_fmt(fmt)
#include <linux/iommu.h>
+#include <linux/amd-iommu.h>
+
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/ioctl.h>
#include <linux/iommufd.h>
#include <linux/amd-iommu.h>
#include <uapi/linux/iommufd.h>
+#include <linux/mem_encrypt.h>
#include <asm/iommu.h>
#include <asm/set_memory.h>
@@ -18,12 +24,130 @@
#include "amd_iommu.h"
#include "amd_iommu_types.h"
#include "amd_viommu.h"
+#include "../iommu-pages.h"
+
+LIST_HEAD(viommu_devid_map);
+
+static int viommu_init_pci_vsc(struct amd_iommu *iommu)
+{
+ iommu->vsc_offset = pci_find_capability(iommu->dev, PCI_CAP_ID_VNDR);
+ if (!iommu->vsc_offset)
+ return -ENODEV;
+
+ DUMP_printk("device:%s, vsc offset:%04x\n",
+ pci_name(iommu->dev), iommu->vsc_offset);
+ return 0;
+}
+
+static void amd_viommu_gid_ida_init(struct amd_iommu *iommu)
+{
+ ida_init(&iommu->gid_ida);
+ iommu->gid_ida_inited = true;
+}
+
+static void amd_viommu_gid_ida_fini(struct amd_iommu *iommu)
+{
+ if (!iommu->gid_ida_inited)
+ return;
+
+ ida_destroy(&iommu->gid_ida);
+ iommu->gid_ida_inited = false;
+}
+
+static void __init amd_viommu_vf_vfcntl_unmap(struct amd_iommu *iommu)
+{
+ if (iommu->vfctrl_base) {
+ iounmap(iommu->vfctrl_base);
+ iommu->vfctrl_base = NULL;
+ }
+ if (iommu->vf_cntl_phys)
+ release_mem_region(iommu->vf_cntl_phys, VIOMMU_VF_CNTL_MMIO_MAP_SIZE);
+
+ if (iommu->vf_base) {
+ iounmap(iommu->vf_base);
+ iommu->vf_base = NULL;
+ }
+ if (iommu->vf_base_phys)
+ release_mem_region(iommu->vf_base_phys, VIOMMU_VF_MMIO_MAP_SIZE);
+}
+
+void __init amd_viommu_uninit(struct amd_iommu *iommu)
+{
+ amd_viommu_gid_ida_fini(iommu);
+ amd_viommu_vf_vfcntl_unmap(iommu);
+}
+
+static int __init viommu_vf_vfcntl_init(struct amd_iommu *iommu)
+{
+ u32 lo, hi;
+ u64 vf_phys, vf_cntl_phys;
+
+ /* Setting up VF and VF_CNTL MMIOs */
+ pci_read_config_dword(iommu->dev, iommu->vsc_offset + MMIO_VSC_VF_BAR_LO_OFFSET, &lo);
+ pci_read_config_dword(iommu->dev, iommu->vsc_offset + MMIO_VSC_VF_BAR_HI_OFFSET, &hi);
+ vf_phys = hi;
+ vf_phys = (vf_phys << 32) | lo;
+ if (!(vf_phys & 1)) {
+ pr_err(FW_BUG "vf_phys disabled\n");
+ return -EINVAL;
+ }
+
+ pci_read_config_dword(iommu->dev, iommu->vsc_offset + MMIO_VSC_VF_CNTL_BAR_LO_OFFSET, &lo);
+ pci_read_config_dword(iommu->dev, iommu->vsc_offset + MMIO_VSC_VF_CNTL_BAR_HI_OFFSET, &hi);
+ vf_cntl_phys = hi;
+ vf_cntl_phys = (vf_cntl_phys << 32) | lo;
+ if (!(vf_cntl_phys & 1)) {
+ pr_err(FW_BUG "vf_cntl_phys disabled\n");
+ return -EINVAL;
+ }
+
+ if (!vf_phys || !vf_cntl_phys) {
+ pr_err(FW_BUG "AMD-Vi: Unassigned VF resources.\n");
+ return -ENOMEM;
+ }
+
+ /* Mapping 256MB of VF and 4MB of VF_CNTL BARs */
+ vf_phys &= ~1ULL;
+ iommu->vf_base = iommu_map_mmio_space(vf_phys, VIOMMU_VF_MMIO_MAP_SIZE);
+ if (!iommu->vf_base) {
+ pr_err("Can't reserve vf_base\n");
+ return -ENOMEM;
+ }
+ iommu->vf_base_phys = vf_phys;
+
+ vf_cntl_phys &= ~1ULL;
+ iommu->vfctrl_base = iommu_map_mmio_space(vf_cntl_phys, VIOMMU_VF_CNTL_MMIO_MAP_SIZE);
+ if (!iommu->vfctrl_base) {
+ pr_err("Can't reserve vfctrl_base\n");
+ goto err_out;
+ }
+ iommu->vf_cntl_phys = vf_cntl_phys;
+
+ pr_debug("%s: IOMMU device:%s, vf_base:%#llx, vfctrl_base:%#llx\n",
+ __func__, pci_name(iommu->dev), vf_phys, vf_cntl_phys);
+ return 0;
+err_out:
+ amd_viommu_uninit(iommu);
+ return -ENOMEM;
+}
int __init amd_viommu_init(struct amd_iommu *iommu)
{
+ int ret;
+
if (!amd_iommu_viommu ||
!check_feature(FEATURE_VIOMMU))
return 0;
+ ret = viommu_init_pci_vsc(iommu);
+ if (ret)
+ return ret;
+
+ ret = viommu_vf_vfcntl_init(iommu);
+ if (ret)
+ return ret;
+
+ amd_viommu_gid_ida_init(iommu);
+
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 07/22] iommu/amd: Add support for AMD vIOMMU VF MMIO region
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (5 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 06/22] iommu/amd: Map vIOMMU VF and VF Control MMIO BARs Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:33 ` Jason Gunthorpe
2026-06-29 15:35 ` [PATCH v3 08/22] iommu/amd: Introduce Reset vMMIO Command Suravee Suthikulpanit
` (15 subsequent siblings)
22 siblings, 1 reply; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit,
Vasant Hegde
The AMD vIOMMU virtualizes guest MMIO registers at the 3rd 4K region.
This is achieved using the iommufd_viommu_alloc_mmap().
Co-developed-by: Vasant Hegde <Vasant.Hegde@amd.com>
Signed-off-by: Vasant Hegde <Vasant.Hegde@amd.com>
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu_types.h | 3 +++
drivers/iommu/amd/amd_viommu.h | 7 +++++++
drivers/iommu/amd/iommufd.c | 17 ++++++++++++++++-
drivers/iommu/amd/viommu.c | 11 +++++++++++
4 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index e88e0bacd1a9..cc7049bbfa14 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -540,6 +540,9 @@ struct amd_iommu_viommu {
* Indexed by guest domain ID.
*/
struct xarray gdomid_array;
+
+ /* Offset for mmap() of guest VF MMIO; set after iommufd_viommu_alloc_mmap(). */
+ unsigned long vfmmio_mmap_offset;
};
/*
diff --git a/drivers/iommu/amd/amd_viommu.h b/drivers/iommu/amd/amd_viommu.h
index d0c4fdd00809..447692b9101c 100644
--- a/drivers/iommu/amd/amd_viommu.h
+++ b/drivers/iommu/amd/amd_viommu.h
@@ -12,6 +12,8 @@ int amd_viommu_init(struct amd_iommu *iommu);
void __init amd_viommu_uninit(struct amd_iommu *iommu);
+u64 amd_viommu_get_vfmmio_addr(struct amd_iommu *iommu, u16 gid);
+
#else
static inline int amd_viommu_init(struct amd_iommu *iommu)
@@ -23,6 +25,11 @@ static inline void amd_viommu_uninit(struct amd_iommu *iommu)
{
}
+static inline u64 amd_viommu_get_vfmmio_addr(struct amd_iommu *iommu, u16 gid)
+{
+ return 0;
+}
+
#endif /* CONFIG_AMD_IOMMU_IOMMUFD */
#endif /* AMD_VIOMMU_H */
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index ec3c14e18c7b..9f76bfa1d6ea 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -7,6 +7,7 @@
#include "iommufd.h"
#include "amd_iommu.h"
+#include "amd_viommu.h"
#include "amd_iommu_types.h"
static const struct iommufd_viommu_ops amd_viommu_ops;
@@ -44,11 +45,12 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
const struct iommu_user_data *user_data)
{
int ret;
+ phys_addr_t page_base;
unsigned long flags;
struct iommu_viommu_amd data = {};
struct protection_domain *pdom = to_pdomain(parent);
- struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
struct amd_iommu *iommu = container_of(viommu->iommu_dev, struct amd_iommu, iommu);
+ struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
xa_init_flags(&aviommu->gdomid_array, XA_FLAGS_ALLOC1);
aviommu->parent = pdom;
@@ -68,6 +70,16 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
aviommu->gid = ret;
pr_debug("%s: gid=%#x", __func__, aviommu->gid);
+ page_base = amd_viommu_get_vfmmio_addr(iommu, aviommu->gid);
+
+ ret = iommufd_viommu_alloc_mmap(&aviommu->core,
+ page_base, SZ_4K,
+ &aviommu->vfmmio_mmap_offset);
+ if (ret)
+ goto err_mmap;
+
+ data.out_vfmmio_mmap_offset = aviommu->vfmmio_mmap_offset;
+
ret = iommu_copy_struct_to_user(user_data, &data,
IOMMU_VIOMMU_TYPE_AMD,
out_vfmmio_mmap_offset);
@@ -82,6 +94,8 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
return 0;
err_init:
+ iommufd_viommu_destroy_mmap(&aviommu->core, aviommu->vfmmio_mmap_offset);
+err_mmap:
amd_iommu_gid_free(iommu, aviommu->gid);
err_gid:
return ret;
@@ -100,6 +114,7 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)
list_del(&aviommu->pdom_list);
spin_unlock_irqrestore(&pdom->lock, flags);
xa_destroy(&aviommu->gdomid_array);
+ iommufd_viommu_destroy_mmap(&aviommu->core, aviommu->vfmmio_mmap_offset);
amd_iommu_gid_free(iommu, aviommu->gid);
}
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index 014ae16bf58b..9e6eb2f977ec 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -131,6 +131,17 @@ static int __init viommu_vf_vfcntl_init(struct amd_iommu *iommu)
return -ENOMEM;
}
+/*
+ * Returns VF MMIO BAR offset for the give guest ID which will be
+ * mapped to guest vIOMMU 3rd 4K MMIO address
+ */
+u64 amd_viommu_get_vfmmio_addr(struct amd_iommu *iommu, u16 gid)
+{
+ /* TODO: Add check for sVIOMMU and set gid[bit 15] */
+ return iommu->vf_base_phys + gid * VIOMMU_VF_MMIO_ENTRY_SIZE;
+}
+EXPORT_SYMBOL(amd_viommu_get_vfmmio_addr);
+
int __init amd_viommu_init(struct amd_iommu *iommu)
{
int ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 07/22] iommu/amd: Add support for AMD vIOMMU VF MMIO region
2026-06-29 15:35 ` [PATCH v3 07/22] iommu/amd: Add support for AMD vIOMMU VF MMIO region Suravee Suthikulpanit
@ 2026-07-07 14:33 ` Jason Gunthorpe
0 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:33 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:20PM +0000, Suravee Suthikulpanit wrote:
>
> +/*
> + * Returns VF MMIO BAR offset for the give guest ID which will be
> + * mapped to guest vIOMMU 3rd 4K MMIO address
> + */
> +u64 amd_viommu_get_vfmmio_addr(struct amd_iommu *iommu, u16 gid)
> +{
> + /* TODO: Add check for sVIOMMU and set gid[bit 15] */
> + return iommu->vf_base_phys + gid * VIOMMU_VF_MMIO_ENTRY_SIZE;
> +}
> +EXPORT_SYMBOL(amd_viommu_get_vfmmio_addr);
Why is this exported?
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v3 08/22] iommu/amd: Introduce Reset vMMIO Command
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (6 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 07/22] iommu/amd: Add support for AMD vIOMMU VF MMIO region Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 09/22] iommu/amd: Introduce and map vIOMMU private IPA region Suravee Suthikulpanit
` (14 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Introduce new IOMMU commands for vIOMMU to reset
virtualized MMIO registers of a particular guest.
Reviewed-by: Weinan Liu <wnliu@google.com>
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 1 +
drivers/iommu/amd/amd_iommu_types.h | 1 +
drivers/iommu/amd/iommu.c | 22 ++++++++++++++++++++++
drivers/iommu/amd/iommufd.c | 3 +++
4 files changed, 27 insertions(+)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 044bc9a634a1..2ce207529ea0 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -11,6 +11,7 @@
#include "amd_iommu_types.h"
+void iommu_reset_vmmio(struct amd_iommu *iommu, u16 gid);
extern int amd_iommu_evtlog_size;
extern int amd_iommu_pprlog_size;
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index cc7049bbfa14..44fa1d6c64d6 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -218,6 +218,7 @@
#define CMD_INV_IRT 0x05
#define CMD_COMPLETE_PPR 0x07
#define CMD_INV_ALL 0x08
+#define CMD_RESET_VMMIO 0x0A
#define CMD_COMPL_WAIT_STORE_MASK 0x01
#define CMD_COMPL_WAIT_INT_MASK 0x02
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 73fba8be40d1..6f5ecc48f4ad 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -1428,6 +1428,18 @@ static void build_inv_irt(struct iommu_cmd *cmd, u16 devid)
CMD_SET_TYPE(cmd, CMD_INV_IRT);
}
+static void build_reset_vmmio(struct iommu_cmd *cmd, u16 gid,
+ bool vcmd, bool all)
+{
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->data[0] = gid;
+ if (all)
+ cmd->data[0] |= (1 << 28);
+ if (vcmd)
+ cmd->data[0] |= (1 << 31);
+ CMD_SET_TYPE(cmd, CMD_RESET_VMMIO);
+}
+
/*
* Writes the command to the IOMMUs command buffer and informs the
* hardware about the new command.
@@ -1668,6 +1680,16 @@ void amd_iommu_flush_all_caches(struct amd_iommu *iommu)
}
}
+void iommu_reset_vmmio(struct amd_iommu *iommu, u16 gid)
+{
+ struct iommu_cmd cmd;
+
+ build_reset_vmmio(&cmd, gid, 1, 1);
+
+ iommu_queue_command(iommu, &cmd);
+ amd_iommu_completion_wait(iommu);
+}
+
/*
* Command send function for flushing on-device TLB
*/
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index 9f76bfa1d6ea..81e0244348a2 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -80,6 +80,9 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
data.out_vfmmio_mmap_offset = aviommu->vfmmio_mmap_offset;
+ /* Reset vIOMMU MMIOs to initialize the vIOMMU */
+ iommu_reset_vmmio(iommu, aviommu->gid);
+
ret = iommu_copy_struct_to_user(user_data, &data,
IOMMU_VIOMMU_TYPE_AMD,
out_vfmmio_mmap_offset);
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 09/22] iommu/amd: Introduce and map vIOMMU private IPA region
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (7 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 08/22] iommu/amd: Introduce Reset vMMIO Command Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:07 ` Jason Gunthorpe
2026-06-29 15:35 ` [PATCH v3 10/22] iommu/amd: Pass iommu to device_flush_dte() Suravee Suthikulpanit
` (13 subsequent siblings)
22 siblings, 1 reply; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
AMD vIOMMU introduces the IOMMU Private Address (IPA) region for
guest-side IOMMU virtualization data structures.
Introduce a per-IOMMU v1 paging domain in viommu_pdom, allocate
8MB of backing memory as four 2MB subregions, map them into the
domain, and add viommu_private_space_init() /
viommu_private_space_uninit() as a matched pair.
For more info, see section vIOMMU Private Address Space of the
IOMMU specification [1].
[1] https://docs.amd.com/v/u/en-US/48882_3.10_PUB
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 8 ++
drivers/iommu/amd/amd_iommu_types.h | 11 ++
drivers/iommu/amd/iommu.c | 25 ++++-
drivers/iommu/amd/viommu.c | 165 ++++++++++++++++++++++++++++
4 files changed, 204 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 2ce207529ea0..279f458becda 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -31,6 +31,7 @@ void *__init iommu_alloc_4k_pages(struct amd_iommu *iommu,
gfp_t gfp, size_t size);
u8 __iomem * __init iommu_map_mmio_space(u64 address, u64 end);
void __init iommu_unmap_mmio_space(struct amd_iommu *iommu);
+int iommu_flush_dte(struct amd_iommu *iommu, u16 devid);
#ifdef CONFIG_AMD_IOMMU_DEBUGFS
void amd_iommu_debugfs_setup(void);
@@ -39,6 +40,8 @@ static inline void amd_iommu_debugfs_setup(void) {}
#endif
extern bool amd_iommu_viommu;
+extern const struct pt_iommu_driver_ops amd_hw_driver_ops_v1;
+extern const struct iommu_domain_ops amdv1_ops;
/* Needed for interrupt remapping */
int amd_iommu_prepare(void);
@@ -56,6 +59,8 @@ extern bool amd_iommu_hatdis;
/* Protection domain ops */
void amd_iommu_init_identity_domain(void);
struct protection_domain *protection_domain_alloc(void);
+struct iommu_domain *amd_iommu_domain_alloc_paging_v1(struct device *dev,
+ u32 flags);
struct iommu_domain *amd_iommu_domain_alloc_sva(struct device *dev,
struct mm_struct *mm);
void amd_iommu_domain_free(struct iommu_domain *dom);
@@ -100,6 +105,9 @@ void amd_iommu_domain_flush_pages(struct protection_domain *domain,
void amd_iommu_dev_flush_pasid_pages(struct iommu_dev_data *dev_data,
ioasid_t pasid, u64 address, size_t size);
+int amd_iommu_flush_private_vm_region(struct amd_iommu *iommu, struct protection_domain *pdom,
+ u64 address, size_t size);
+
#ifdef CONFIG_IRQ_REMAP
int amd_iommu_create_irq_domain(struct amd_iommu *iommu);
#else
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 44fa1d6c64d6..a5e2f32590d1 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -424,6 +424,13 @@
/* For vIOMMU, the GID is 16-bit. */
#define VIOMMU_MAX_GID 0xFFFF
+/*
+ * Total IOMMU private region is 8MB (4 x 2MB-subregion)
+ */
+#define VIOMMU_PRIV_REGION_BASE (0)
+#define VIOMMU_PRIV_SUBREGION_CNT (4)
+#define VIOMMU_PRIV_SUBREGION_SIZE (0x200000) /* 2MB */
+
/* Timeout stuff */
#define LOOP_TIMEOUT 100000
#define MMIO_STATUS_TIMEOUT 2000000
@@ -814,6 +821,10 @@ struct amd_iommu {
struct ida gid_ida; /* guest IDs for this IOMMU */
bool gid_ida_inited;
+
+ /* HW vIOMMU support */
+ struct protection_domain *viommu_pdom;
+ void *viommu_priv_region[VIOMMU_PRIV_SUBREGION_CNT];
};
static inline struct amd_iommu *dev_to_amd_iommu(struct device *dev)
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 6f5ecc48f4ad..8b441f68bc47 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -1551,7 +1551,7 @@ static void domain_flush_complete(struct protection_domain *domain)
amd_iommu_completion_wait(pdom_iommu_info->iommu);
}
-static int iommu_flush_dte(struct amd_iommu *iommu, u16 devid)
+int iommu_flush_dte(struct amd_iommu *iommu, u16 devid)
{
struct iommu_cmd cmd;
@@ -1808,6 +1808,22 @@ static int domain_flush_pages_v1(struct protection_domain *pdom,
return ret;
}
+int amd_iommu_flush_private_vm_region(struct amd_iommu *iommu, struct protection_domain *pdom,
+ u64 address, size_t size)
+{
+ int ret;
+ struct iommu_cmd cmd;
+
+ build_inv_iommu_pages(&cmd, address, size, pdom->id, 0, false);
+
+ ret = iommu_queue_command(iommu, &cmd);
+ if (ret)
+ return ret;
+
+ amd_iommu_completion_wait(iommu);
+ return ret;
+}
+
/*
* TLB invalidation function which is called from the mapping functions.
* It flushes range of PTEs of the domain.
@@ -2726,12 +2742,12 @@ static void amd_iommu_iotlb_sync(struct iommu_domain *domain,
iommu_put_pages_list(&gather->freelist);
}
-static const struct pt_iommu_driver_ops amd_hw_driver_ops_v1 = {
+const struct pt_iommu_driver_ops amd_hw_driver_ops_v1 = {
.get_top_lock = amd_iommu_get_top_lock,
.change_top = amd_iommu_change_top,
};
-static const struct iommu_domain_ops amdv1_ops = {
+const struct iommu_domain_ops amdv1_ops = {
IOMMU_PT_DOMAIN_OPS(amdv1),
.iotlb_sync_map = amd_iommu_iotlb_sync_map,
.flush_iotlb_all = amd_iommu_flush_iotlb_all,
@@ -2746,8 +2762,7 @@ static const struct iommu_dirty_ops amdv1_dirty_ops = {
.set_dirty_tracking = amd_iommu_set_dirty_tracking,
};
-static struct iommu_domain *amd_iommu_domain_alloc_paging_v1(struct device *dev,
- u32 flags)
+struct iommu_domain *amd_iommu_domain_alloc_paging_v1(struct device *dev, u32 flags)
{
struct pt_iommu_amdv1_cfg cfg = {};
struct protection_domain *domain;
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index 9e6eb2f977ec..c5c85e82c265 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -131,6 +131,167 @@ static int __init viommu_vf_vfcntl_init(struct amd_iommu *iommu)
return -ENOMEM;
}
+/*
+ * Allocate backing pages, mark UC, and map at @iova in viommu_pdom.
+ * *@out_va is NULL on any failure.
+ */
+static int viommu_priv_alloc_map_flush(struct amd_iommu *iommu, u64 iova, size_t size,
+ gfp_t gfp, void **out_va)
+{
+ int ret;
+ void *va;
+ int nid = iommu && iommu->dev ? dev_to_node(&iommu->dev->dev) : NUMA_NO_NODE;
+
+ *out_va = NULL;
+
+ if (!iommu || !iommu->viommu_pdom)
+ return -EINVAL;
+
+ va = iommu_alloc_pages_node_sz(nid, gfp, size);
+ if (!va)
+ return -ENOMEM;
+
+ /*
+ * IOMMU spec mentions that the vIOMMU backing storage memory
+ * should be marked as UC.
+ */
+ ret = set_memory_uc((unsigned long)va, size >> PAGE_SHIFT);
+ if (ret)
+ goto err_free_pages;
+
+ ret = iommu_map(&iommu->viommu_pdom->domain, iova, iommu_virt_to_phys(va), size,
+ IOMMU_PROT_IR | IOMMU_PROT_IW, GFP_KERNEL);
+ if (ret)
+ goto cleanup_mem_attr;
+
+ *out_va = va;
+ return 0;
+
+cleanup_mem_attr:
+ set_memory_wb((unsigned long)va, size >> PAGE_SHIFT);
+err_free_pages:
+ iommu_free_pages(va);
+ return ret;
+}
+
+/*
+ * Unmap @iova, flush the unmapped span on this IOMMU, WB, and free @cpu_va.
+ * Returns 0, or the flush error if amd_iommu_flush_private_vm_region() fails.
+ */
+static int viommu_priv_unmap_flush_free(struct amd_iommu *iommu, u64 iova, size_t size,
+ void *cpu_va)
+{
+ size_t unmapped;
+ int ret = 0;
+
+ if (!cpu_va)
+ return 0;
+ if (!iommu || !iommu->viommu_pdom)
+ return -EINVAL;
+
+ unmapped = iommu_unmap(&iommu->viommu_pdom->domain, iova, size);
+ if (unmapped != size)
+ pr_warn("%s: unmapped %#zx of %#lx at %#llx\n", __func__, unmapped, size, iova);
+
+ if (unmapped) {
+ ret = amd_iommu_flush_private_vm_region(iommu, iommu->viommu_pdom, iova,
+ unmapped);
+ if (ret)
+ pr_warn("%s: IOTLB flush failed (%d) for %#zx at %#llx\n",
+ __func__, ret, unmapped, iova);
+ }
+
+ set_memory_wb((unsigned long)cpu_va, size >> PAGE_SHIFT);
+ iommu_free_pages(cpu_va);
+ return ret;
+}
+
+static void *alloc_private_subregion(struct amd_iommu *iommu, u64 base, size_t size)
+{
+ void *region = NULL;
+ int ret;
+
+ ret = viommu_priv_alloc_map_flush(iommu, base, size, GFP_KERNEL | __GFP_ZERO, ®ion);
+ if (ret)
+ return NULL;
+
+ pr_debug("%s: base=%#llx, size=%#lx, subregion=%#llx(%#llx)\n",
+ __func__, base, size, (unsigned long long)region, iommu_virt_to_phys(region));
+
+ return region;
+}
+
+static void viommu_private_space_uninit(struct amd_iommu *iommu)
+{
+ int i, ret, first_err = 0;
+ u64 base;
+ struct iommu_domain *dom;
+
+ if (!iommu->viommu_pdom)
+ return;
+
+ for (i = 0; i < VIOMMU_PRIV_SUBREGION_CNT; i++) {
+ if (!iommu->viommu_priv_region[i])
+ continue;
+ base = VIOMMU_PRIV_REGION_BASE + (i * VIOMMU_PRIV_SUBREGION_SIZE);
+ ret = viommu_priv_unmap_flush_free(iommu, base, VIOMMU_PRIV_SUBREGION_SIZE,
+ iommu->viommu_priv_region[i]);
+ if (ret && !first_err)
+ first_err = ret;
+ iommu->viommu_priv_region[i] = NULL;
+ }
+
+ dom = &iommu->viommu_pdom->domain;
+ amd_iommu_domain_free(dom);
+ iommu->viommu_pdom = NULL;
+
+ if (first_err)
+ pr_err("%s: private subregion teardown failed (%d)\n", __func__, first_err);
+}
+static int viommu_private_space_init(struct amd_iommu *iommu)
+{
+ int i;
+ u64 base;
+ struct iommu_domain *dom;
+ struct protection_domain *pdom;
+ struct pt_iommu_amdv1_hw_info pt_info;
+
+ /*
+ * Setup page table root pointer, Guest MMIO and
+ * Cmdbuf Dirty Status regions.
+ */
+ dom = amd_iommu_domain_alloc_paging_v1(&iommu->dev->dev, 0);
+ if (!dom) {
+ pr_err("%s: Failed to initialize private space\n", __func__);
+ return -ENOMEM;
+ }
+
+ pdom = to_pdomain(dom);
+ iommu->viommu_pdom = pdom;
+
+ /*
+ * Each private region requires to 8MB of memory to be allocated
+ * and mapped. Split the region into 4 x 2MB-subregion.
+ */
+ for (i = 0; i < VIOMMU_PRIV_SUBREGION_CNT; i++) {
+ base = VIOMMU_PRIV_REGION_BASE + (i * VIOMMU_PRIV_SUBREGION_SIZE);
+ iommu->viommu_priv_region[i] = alloc_private_subregion(iommu, base,
+ VIOMMU_PRIV_SUBREGION_SIZE);
+ if (!iommu->viommu_priv_region[i]) {
+ pr_err("%s: Failed to allocate vIOMMU private subregion %d\n", __func__, i);
+ viommu_private_space_uninit(iommu);
+ return -ENOMEM;
+ }
+ }
+
+ pt_iommu_amdv1_hw_info(&pdom->amdv1, &pt_info);
+ pr_debug("%s: devid=%#x, pte_root=%#llx\n",
+ __func__, iommu->devid,
+ (unsigned long long)pt_info.host_pt_root);
+
+ return 0;
+}
+
/*
* Returns VF MMIO BAR offset for the give guest ID which will be
* mapped to guest vIOMMU 3rd 4K MMIO address
@@ -160,5 +321,9 @@ int __init amd_viommu_init(struct amd_iommu *iommu)
amd_viommu_gid_ida_init(iommu);
+ ret = viommu_private_space_init(iommu);
+ if (ret)
+ return ret;
+
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 09/22] iommu/amd: Introduce and map vIOMMU private IPA region
2026-06-29 15:35 ` [PATCH v3 09/22] iommu/amd: Introduce and map vIOMMU private IPA region Suravee Suthikulpanit
@ 2026-07-07 14:07 ` Jason Gunthorpe
0 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:07 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:22PM +0000, Suravee Suthikulpanit wrote:
> @@ -1808,6 +1808,22 @@ static int domain_flush_pages_v1(struct protection_domain *pdom,
> return ret;
> }
>
> +int amd_iommu_flush_private_vm_region(struct amd_iommu *iommu, struct protection_domain *pdom,
> + u64 address, size_t size)
> +{
> + int ret;
> + struct iommu_cmd cmd;
Why do we need this function? iommu_unmap() should generate this
flush automatically, shouldn't it?
> +/*
> + * Allocate backing pages, mark UC, and map at @iova in viommu_pdom.
> + * *@out_va is NULL on any failure.
> + */
> +static int viommu_priv_alloc_map_flush(struct amd_iommu *iommu, u64 iova, size_t size,
> + gfp_t gfp, void **out_va)
> +{
I think the flush is a bit unnecessary in the name
> + int ret;
> + void *va;
> + int nid = iommu && iommu->dev ? dev_to_node(&iommu->dev->dev) : NUMA_NO_NODE;
> +
> + *out_va = NULL;
> +
> + if (!iommu || !iommu->viommu_pdom)
> + return -EINVAL;
> +
> + va = iommu_alloc_pages_node_sz(nid, gfp, size);
> + if (!va)
> + return -ENOMEM;
> +
> + /*
> + * IOMMU spec mentions that the vIOMMU backing storage memory
> + * should be marked as UC.
> + */
> + ret = set_memory_uc((unsigned long)va, size >> PAGE_SHIFT);
> + if (ret)
> + goto err_free_pages;
> +
> + ret = iommu_map(&iommu->viommu_pdom->domain, iova, iommu_virt_to_phys(va), size,
> + IOMMU_PROT_IR | IOMMU_PROT_IW, GFP_KERNEL);
^^^^^^^^^^^^^^^^^^^^^^^
These are the wrong constants to pass to iommu_prot
> +/*
> + * Unmap @iova, flush the unmapped span on this IOMMU, WB, and free @cpu_va.
> + * Returns 0, or the flush error if amd_iommu_flush_private_vm_region() fails.
> + */
> +static int viommu_priv_unmap_flush_free(struct amd_iommu *iommu, u64 iova, size_t size,
> + void *cpu_va)
> +{
> + size_t unmapped;
> + int ret = 0;
> +
> + if (!cpu_va)
> + return 0;
> + if (!iommu || !iommu->viommu_pdom)
> + return -EINVAL;
> +
> + unmapped = iommu_unmap(&iommu->viommu_pdom->domain, iova, size);
> + if (unmapped != size)
> + pr_warn("%s: unmapped %#zx of %#lx at %#llx\n", __func__, unmapped, size, iova);
> +
> + if (unmapped) {
> + ret = amd_iommu_flush_private_vm_region(iommu, iommu->viommu_pdom, iova,
> + unmapped);
unmap calls flush through the domain, why do we need another flush?
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v3 10/22] iommu/amd: Pass iommu to device_flush_dte()
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (8 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 09/22] iommu/amd: Introduce and map vIOMMU private IPA region Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:18 ` Jason Gunthorpe
2026-06-29 15:35 ` [PATCH v3 11/22] iommu/amd: Export amd_iommu_alloc_dev_data() helper Suravee Suthikulpanit
` (12 subsequent siblings)
22 siblings, 1 reply; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Take struct amd_iommu explicitly so DTE invalidation is issued on the
same IOMMU instance used to program the DTE. Also, flush each device in
amd_iommu_change_top() immediately after updating its DTE.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/iommu.c | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 8b441f68bc47..3eb7e2ac3782 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -76,7 +76,7 @@ static void set_dte_entry(struct amd_iommu *iommu,
struct iommu_dev_data *dev_data,
phys_addr_t top_paddr, unsigned int top_level);
-static int device_flush_dte(struct iommu_dev_data *dev_data);
+static int device_flush_dte(struct amd_iommu *iommu, struct iommu_dev_data *dev_data);
static void amd_iommu_change_top(struct pt_iommu *iommu_table,
phys_addr_t top_paddr, unsigned int top_level);
@@ -213,7 +213,7 @@ void amd_iommu_update_dte(struct amd_iommu *iommu,
{
update_dte256(iommu, dev_data, new);
clone_aliases(iommu, dev_data->dev);
- device_flush_dte(dev_data);
+ device_flush_dte(iommu, dev_data);
amd_iommu_completion_wait(iommu);
}
@@ -1716,9 +1716,8 @@ static int device_flush_dte_alias(struct pci_dev *pdev, u16 alias, void *data)
/*
* Command send function for invalidating a device table entry
*/
-static int device_flush_dte(struct iommu_dev_data *dev_data)
+static int device_flush_dte(struct amd_iommu *iommu, struct iommu_dev_data *dev_data)
{
- struct amd_iommu *iommu = get_amd_iommu_from_dev_data(dev_data);
struct pci_dev *pdev = NULL;
struct amd_iommu_pci_seg *pci_seg;
u16 alias;
@@ -2691,11 +2690,9 @@ static void amd_iommu_change_top(struct pt_iommu *iommu_table,
/* Update the HW references with the new level and top ptr */
set_dte_entry(iommu, dev_data, top_paddr, top_level);
clone_aliases(iommu, dev_data->dev);
+ device_flush_dte(iommu, dev_data);
}
- list_for_each_entry(dev_data, &pdom->dev_list, list)
- device_flush_dte(dev_data);
-
domain_flush_complete(pdom);
}
@@ -3127,7 +3124,7 @@ static int amd_iommu_set_dirty_tracking(struct iommu_domain *domain,
spin_unlock(&dev_data->dte_lock);
/* Flush device DTE */
- device_flush_dte(dev_data);
+ device_flush_dte(iommu, dev_data);
domain_flush = true;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 10/22] iommu/amd: Pass iommu to device_flush_dte()
2026-06-29 15:35 ` [PATCH v3 10/22] iommu/amd: Pass iommu to device_flush_dte() Suravee Suthikulpanit
@ 2026-07-07 14:18 ` Jason Gunthorpe
0 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:18 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:23PM +0000, Suravee Suthikulpanit wrote:
> Take struct amd_iommu explicitly so DTE invalidation is issued on the
> same IOMMU instance used to program the DTE. Also, flush each device in
> amd_iommu_change_top() immediately after updating its DTE.
>
> Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
> ---
> drivers/iommu/amd/iommu.c | 13 +++++--------
> 1 file changed, 5 insertions(+), 8 deletions(-)
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v3 11/22] iommu/amd: Export amd_iommu_alloc_dev_data() helper
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (9 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 10/22] iommu/amd: Pass iommu to device_flush_dte() Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 12/22] iommu/amd: Pass iommu and devid to amd_iommu_make_clear_dte() Suravee Suthikulpanit
` (11 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
AMD vIOMMU requires programming the DTE of the IOMMU device ID to set up
IPA mapping, and current DTE helper functions (for setting / clearing)
require struct iommu_dev_data.
However, the IOMMU device does not go through the normal PCI device attach
path, which would allocate the struct iommu_dev_data. So, the driver needs
to manually allocate and initialize the struct iommu_dev_data for IOMMU
in order to reuse the helper functions.
Make alloc_dev_data() available outside iommu.c so callers can allocate
struct iommu_dev_data for a given IOMMU instance and device ID without
going through the PCI device attach path.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 2 ++
drivers/iommu/amd/iommu.c | 4 ++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 279f458becda..20000722451f 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -56,6 +56,8 @@ extern u8 amd_iommu_hpt_vasize;
extern unsigned long amd_iommu_pgsize_bitmap;
extern bool amd_iommu_hatdis;
+struct iommu_dev_data *amd_iommu_alloc_dev_data(struct amd_iommu *iommu, u16 devid);
+
/* Protection domain ops */
void amd_iommu_init_identity_domain(void);
struct protection_domain *protection_domain_alloc(void);
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 3eb7e2ac3782..55257cf12bac 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -388,7 +388,7 @@ static struct amd_iommu *rlookup_amd_iommu(struct device *dev)
return __rlookup_amd_iommu(seg, PCI_SBDF_TO_DEVID(devid));
}
-static struct iommu_dev_data *alloc_dev_data(struct amd_iommu *iommu, u16 devid)
+struct iommu_dev_data *amd_iommu_alloc_dev_data(struct amd_iommu *iommu, u16 devid)
{
struct iommu_dev_data *dev_data;
struct amd_iommu_pci_seg *pci_seg = iommu->pci_seg;
@@ -510,7 +510,7 @@ static struct iommu_dev_data *find_dev_data(struct amd_iommu *iommu, u16 devid)
dev_data = search_dev_data(iommu, devid);
if (dev_data == NULL) {
- dev_data = alloc_dev_data(iommu, devid);
+ dev_data = amd_iommu_alloc_dev_data(iommu, devid);
if (!dev_data)
return NULL;
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 12/22] iommu/amd: Pass iommu and devid to amd_iommu_make_clear_dte()
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (10 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 11/22] iommu/amd: Export amd_iommu_alloc_dev_data() helper Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:20 ` Jason Gunthorpe
2026-06-29 15:35 ` [PATCH v3 13/22] iommu/amd: Assign IOMMU Private Address domain to IOMMU Suravee Suthikulpanit
` (10 subsequent siblings)
22 siblings, 1 reply; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Take struct amd_iommu and device ID explicitly instead of deriving
the IOMMU from struct iommu_dev_data.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 6 +++---
drivers/iommu/amd/iommu.c | 4 ++--
drivers/iommu/amd/nested.c | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 20000722451f..269d0f448b9d 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -217,10 +217,10 @@ void amd_iommu_update_dte(struct amd_iommu *iommu,
int amd_iommu_completion_wait(struct amd_iommu *iommu);
static inline void
-amd_iommu_make_clear_dte(struct iommu_dev_data *dev_data, struct dev_table_entry *new)
+amd_iommu_make_clear_dte(struct amd_iommu *iommu, u16 devid,
+ struct dev_table_entry *new)
{
struct dev_table_entry *initial_dte;
- struct amd_iommu *iommu = get_amd_iommu_from_dev(dev_data->dev);
/* All existing DTE must have V bit set */
new->data128[0] = DTE_FLAG_V;
@@ -230,7 +230,7 @@ amd_iommu_make_clear_dte(struct iommu_dev_data *dev_data, struct dev_table_entry
* Restore cached persistent DTE bits, which can be set by information
* in IVRS table. See set_dev_entry_from_acpi().
*/
- initial_dte = amd_iommu_get_ivhd_dte_flags(iommu->pci_seg->id, dev_data->devid);
+ initial_dte = amd_iommu_get_ivhd_dte_flags(iommu->pci_seg->id, devid);
if (initial_dte) {
new->data128[0] |= initial_dte->data128[0];
new->data128[1] |= initial_dte->data128[1];
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 55257cf12bac..b9063a7d6a6e 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -2253,7 +2253,7 @@ static void set_dte_entry(struct amd_iommu *iommu,
struct gcr3_tbl_info *gcr3_info = &dev_data->gcr3_info;
struct dev_table_entry *dte = &get_dev_table(iommu)[dev_data->devid];
- amd_iommu_make_clear_dte(dev_data, &new);
+ amd_iommu_make_clear_dte(iommu, dev_data->devid, &new);
old_domid = READ_ONCE(dte->data[1]) & DTE_DOMID_MASK;
if (gcr3_info->gcr3_tbl)
@@ -2285,7 +2285,7 @@ static void clear_dte_entry(struct amd_iommu *iommu, struct iommu_dev_data *dev_
{
struct dev_table_entry new = {};
- amd_iommu_make_clear_dte(dev_data, &new);
+ amd_iommu_make_clear_dte(iommu, dev_data->devid, &new);
amd_iommu_update_dte(iommu, dev_data, &new);
}
diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c
index 5b902598e68a..81a5ae00225e 100644
--- a/drivers/iommu/amd/nested.c
+++ b/drivers/iommu/amd/nested.c
@@ -200,7 +200,7 @@ static void set_dte_nested(struct amd_iommu *iommu, struct iommu_domain *dom,
return;
parent = ndom->viommu->parent;
- amd_iommu_make_clear_dte(dev_data, new);
+ amd_iommu_make_clear_dte(iommu, dev_data->devid, new);
/* Retrieve the current pagetable info via the IOMMU PT API. */
pt_iommu_amdv1_hw_info(&parent->amdv1, &pt_info);
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 12/22] iommu/amd: Pass iommu and devid to amd_iommu_make_clear_dte()
2026-06-29 15:35 ` [PATCH v3 12/22] iommu/amd: Pass iommu and devid to amd_iommu_make_clear_dte() Suravee Suthikulpanit
@ 2026-07-07 14:20 ` Jason Gunthorpe
0 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:20 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:25PM +0000, Suravee Suthikulpanit wrote:
> Take struct amd_iommu and device ID explicitly instead of deriving
> the IOMMU from struct iommu_dev_data.
>
> Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
> ---
> drivers/iommu/amd/amd_iommu.h | 6 +++---
> drivers/iommu/amd/iommu.c | 4 ++--
> drivers/iommu/amd/nested.c | 2 +-
> 3 files changed, 6 insertions(+), 6 deletions(-)
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v3 13/22] iommu/amd: Assign IOMMU Private Address domain to IOMMU
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (11 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 12/22] iommu/amd: Pass iommu and devid to amd_iommu_make_clear_dte() Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 14/22] iommu/amd: Add per-VM private IPA alloc/map helpers Suravee Suthikulpanit
` (9 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
By setting the domain ID, pagetable mode, and IOMMU v1 page table in the
IOMMU Device Table Entry (DTE) indexed using the device ID of the
AMD IOMMU.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 2 ++
drivers/iommu/amd/amd_iommu_types.h | 1 +
drivers/iommu/amd/iommu.c | 23 +++++++++++++
drivers/iommu/amd/viommu.c | 51 +++++++++++++++++++++++++++--
4 files changed, 75 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 269d0f448b9d..aaa57840e904 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -57,6 +57,8 @@ extern unsigned long amd_iommu_pgsize_bitmap;
extern bool amd_iommu_hatdis;
struct iommu_dev_data *amd_iommu_alloc_dev_data(struct amd_iommu *iommu, u16 devid);
+void amd_iommu_free_dev_data(struct amd_iommu *iommu,
+ struct iommu_dev_data *dev_data);
/* Protection domain ops */
void amd_iommu_init_identity_domain(void);
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index a5e2f32590d1..340929ae776a 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -823,6 +823,7 @@ struct amd_iommu {
bool gid_ida_inited;
/* HW vIOMMU support */
+ struct iommu_dev_data *viommu_dev_data;
struct protection_domain *viommu_pdom;
void *viommu_priv_region[VIOMMU_PRIV_SUBREGION_CNT];
};
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index b9063a7d6a6e..e5dc34959c6e 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -406,6 +406,29 @@ struct iommu_dev_data *amd_iommu_alloc_dev_data(struct amd_iommu *iommu, u16 dev
return dev_data;
}
+void amd_iommu_free_dev_data(struct amd_iommu *iommu,
+ struct iommu_dev_data *dev_data)
+{
+ struct amd_iommu_pci_seg *pci_seg = iommu->pci_seg;
+ struct llist_node *prev = NULL, *node;
+
+ if (!dev_data)
+ return;
+
+ for (node = pci_seg->dev_data_list.first; node;
+ prev = node, node = node->next) {
+ if (node == &dev_data->dev_data_list) {
+ if (prev)
+ prev->next = node->next;
+ else
+ pci_seg->dev_data_list.first = node->next;
+ break;
+ }
+ }
+
+ kfree(dev_data);
+}
+
struct iommu_dev_data *search_dev_data(struct amd_iommu *iommu, u16 devid)
{
struct iommu_dev_data *dev_data;
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index c5c85e82c265..897355a143d6 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -71,8 +71,27 @@ static void __init amd_viommu_vf_vfcntl_unmap(struct amd_iommu *iommu)
release_mem_region(iommu->vf_base_phys, VIOMMU_VF_MMIO_MAP_SIZE);
}
+static void viommu_free_self_dev_data(struct amd_iommu *iommu, bool clear_dte)
+{
+ struct iommu_dev_data *dev_data = iommu->viommu_dev_data;
+
+ if (!dev_data)
+ return;
+
+ if (clear_dte) {
+ struct dev_table_entry new = {};
+
+ amd_iommu_make_clear_dte(iommu, dev_data->devid, &new);
+ amd_iommu_update_dte(iommu, dev_data, &new);
+ }
+
+ amd_iommu_free_dev_data(iommu, dev_data);
+ iommu->viommu_dev_data = NULL;
+}
+
void __init amd_viommu_uninit(struct amd_iommu *iommu)
{
+ viommu_free_self_dev_data(iommu, true);
amd_viommu_gid_ida_fini(iommu);
amd_viommu_vf_vfcntl_unmap(iommu);
}
@@ -303,21 +322,39 @@ u64 amd_viommu_get_vfmmio_addr(struct amd_iommu *iommu, u16 gid)
}
EXPORT_SYMBOL(amd_viommu_get_vfmmio_addr);
+static void set_dte_ipa(struct amd_iommu *iommu, struct dev_table_entry *new)
+{
+ struct pt_iommu_amdv1_hw_info pt_info;
+ struct protection_domain *pdom = iommu->viommu_pdom;
+
+ pt_iommu_amdv1_hw_info(&pdom->amdv1, &pt_info);
+ amd_iommu_set_dte_v1(iommu->viommu_dev_data, pdom, pdom->id, &pt_info, new);
+}
+
int __init amd_viommu_init(struct amd_iommu *iommu)
{
int ret;
+ bool dte_set = false;
+ struct dev_table_entry new = {};
if (!amd_iommu_viommu ||
!check_feature(FEATURE_VIOMMU))
return 0;
+ iommu->viommu_dev_data = amd_iommu_alloc_dev_data(iommu, iommu->devid);
+ if (!iommu->viommu_dev_data) {
+ pr_err("%s: Failed to allocate dev_data\n", __func__);
+ return -ENOMEM;
+ }
+ iommu->viommu_dev_data->dev = &iommu->dev->dev;
+
ret = viommu_init_pci_vsc(iommu);
if (ret)
- return ret;
+ goto err_dev_data;
ret = viommu_vf_vfcntl_init(iommu);
if (ret)
- return ret;
+ goto err_dev_data;
amd_viommu_gid_ida_init(iommu);
@@ -325,5 +362,15 @@ int __init amd_viommu_init(struct amd_iommu *iommu)
if (ret)
return ret;
+ /* Set DTE for IOMMU device */
+ amd_iommu_make_clear_dte(iommu, iommu->devid, &new);
+ set_dte_ipa(iommu, &new);
+ amd_iommu_update_dte(iommu, iommu->viommu_dev_data, &new);
+ dte_set = true;
+
return 0;
+
+err_dev_data:
+ viommu_free_self_dev_data(iommu, dte_set);
+ return ret;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 14/22] iommu/amd: Add per-VM private IPA alloc/map helpers
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (12 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 13/22] iommu/amd: Assign IOMMU Private Address domain to IOMMU Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 15/22] iommu/amd: Add helper functions to manage DevID / DomID mapping tables Suravee Suthikulpanit
` (8 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Guest device ID and guest domain ID tables use dedicated slots in
the vIOMMU private address (IPA) region, indexed by guest ID (GID).
Add alloc_private_vm_region() and free_private_vm_region() to
allocate backing pages, map them through viommu_pdom, and tear down
with an unmap flush on VM destroy.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/viommu.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index 897355a143d6..808f0b304006 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -374,3 +374,41 @@ int __init amd_viommu_init(struct amd_iommu *iommu)
viommu_free_self_dev_data(iommu, dte_set);
return ret;
}
+
+static int __maybe_unused alloc_private_vm_region(struct amd_iommu *iommu, u64 **entry,
+ u64 base, size_t size, u16 gid)
+{
+ int ret;
+ void *va = NULL;
+ u64 addr = base + (gid * size);
+
+ ret = viommu_priv_alloc_map_flush(iommu, addr, size, GFP_KERNEL | __GFP_ZERO, &va);
+ if (ret) {
+ *entry = NULL;
+ return ret;
+ }
+
+ *entry = (u64 *)va;
+
+ pr_debug("%s: entry=%#llx(%#llx), addr=%#llx, size=%#lx\n", __func__,
+ (unsigned long long)*entry, iommu_virt_to_phys(*entry), addr, size);
+
+ return 0;
+}
+
+static void __maybe_unused free_private_vm_region(struct amd_iommu *iommu, u64 **entry,
+ u64 base, size_t size, u16 gid)
+{
+ u64 addr = base + (gid * size);
+
+ if (!iommu || !iommu->viommu_pdom || !*entry)
+ return;
+
+ pr_debug("%s: entry=%#llx(%#llx), base=%#llx, addr=%#llx, size=%#lx\n",
+ __func__, (unsigned long long)*entry,
+ iommu_virt_to_phys(*entry), base, addr, size);
+
+ viommu_priv_unmap_flush_free(iommu, addr, size, *entry);
+
+ *entry = NULL;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 15/22] iommu/amd: Add helper functions to manage DevID / DomID mapping tables
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (13 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 14/22] iommu/amd: Add per-VM private IPA alloc/map helpers Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 16/22] iommu/amd: Introduce IOMMUFD vDevice support for AMD Suravee Suthikulpanit
` (7 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Introduce amd_viommu_init_one() and amd_viommu_uninit_one().
These functions are called during IOMMUFD vIOMMU initialize and destroy.
Currently, it manages the IPA mapping for Device ID and Domain ID mapping
tables.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu_types.h | 3 ++
drivers/iommu/amd/amd_viommu.h | 13 ++++++++
drivers/iommu/amd/iommu.c | 1 +
drivers/iommu/amd/iommufd.c | 5 +++
drivers/iommu/amd/viommu.c | 52 +++++++++++++++++++++++++++++
5 files changed, 74 insertions(+)
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 340929ae776a..03346258e2dc 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -549,6 +549,9 @@ struct amd_iommu_viommu {
*/
struct xarray gdomid_array;
+ u64 *devid_table;
+ u64 *domid_table;
+
/* Offset for mmap() of guest VF MMIO; set after iommufd_viommu_alloc_mmap(). */
unsigned long vfmmio_mmap_offset;
};
diff --git a/drivers/iommu/amd/amd_viommu.h b/drivers/iommu/amd/amd_viommu.h
index 447692b9101c..8b57717c22a6 100644
--- a/drivers/iommu/amd/amd_viommu.h
+++ b/drivers/iommu/amd/amd_viommu.h
@@ -14,6 +14,10 @@ void __init amd_viommu_uninit(struct amd_iommu *iommu);
u64 amd_viommu_get_vfmmio_addr(struct amd_iommu *iommu, u16 gid);
+int amd_viommu_init_one(struct amd_iommu *iommu, struct amd_iommu_viommu *viommu);
+
+void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *viommu);
+
#else
static inline int amd_viommu_init(struct amd_iommu *iommu)
@@ -30,6 +34,15 @@ static inline u64 amd_viommu_get_vfmmio_addr(struct amd_iommu *iommu, u16 gid)
return 0;
}
+static inline int amd_viommu_init_one(struct amd_iommu *iommu, struct amd_iommu_viommu *viommu)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *viommu)
+{
+}
+
#endif /* CONFIG_AMD_IOMMU_IOMMUFD */
#endif /* AMD_VIOMMU_H */
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index e5dc34959c6e..3b59938c2f86 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -43,6 +43,7 @@
#include <linux/generic_pt/iommu.h>
#include "amd_iommu.h"
+#include "amd_viommu.h"
#include "iommufd.h"
#include "../irq_remapping.h"
#include "../iommu-pages.h"
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index 81e0244348a2..bfc4b0ec22a9 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -83,6 +83,10 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
/* Reset vIOMMU MMIOs to initialize the vIOMMU */
iommu_reset_vmmio(iommu, aviommu->gid);
+ ret = amd_viommu_init_one(iommu, aviommu);
+ if (ret)
+ goto err_init;
+
ret = iommu_copy_struct_to_user(user_data, &data,
IOMMU_VIOMMU_TYPE_AMD,
out_vfmmio_mmap_offset);
@@ -118,6 +122,7 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)
spin_unlock_irqrestore(&pdom->lock, flags);
xa_destroy(&aviommu->gdomid_array);
iommufd_viommu_destroy_mmap(&aviommu->core, aviommu->vfmmio_mmap_offset);
+ amd_viommu_uninit_one(iommu, aviommu);
amd_iommu_gid_free(iommu, aviommu->gid);
}
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index 808f0b304006..27b17edb910e 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -26,6 +26,20 @@
#include "amd_viommu.h"
#include "../iommu-pages.h"
+/*
+ * Guest Device ID Mapping Table
+ */
+#define VIOMMU_MAX_GDEVID 0xFFFF
+#define VIOMMU_DEVID_MAPPING_BASE 0x1000000000ULL
+#define VIOMMU_DEVID_MAPPING_ENTRY_SIZE (1 << 20)
+
+/*
+ * Guest Domain ID Mapping Table
+ */
+#define VIOMMU_MAX_GDOMID 0xFFFF
+#define VIOMMU_DOMID_MAPPING_BASE 0x2000000000ULL
+#define VIOMMU_DOMID_MAPPING_ENTRY_SIZE (1 << 19)
+
LIST_HEAD(viommu_devid_map);
static int viommu_init_pci_vsc(struct amd_iommu *iommu)
@@ -412,3 +426,41 @@ static void __maybe_unused free_private_vm_region(struct amd_iommu *iommu, u64 *
*entry = NULL;
}
+
+void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *aviommu)
+{
+ pr_debug("%s: gid=%u\n", __func__, aviommu->gid);
+
+ free_private_vm_region(iommu, &aviommu->devid_table,
+ VIOMMU_DEVID_MAPPING_BASE,
+ VIOMMU_DEVID_MAPPING_ENTRY_SIZE,
+ aviommu->gid);
+ free_private_vm_region(iommu, &aviommu->domid_table,
+ VIOMMU_DOMID_MAPPING_BASE,
+ VIOMMU_DOMID_MAPPING_ENTRY_SIZE,
+ aviommu->gid);
+}
+
+int amd_viommu_init_one(struct amd_iommu *iommu, struct amd_iommu_viommu *viommu)
+{
+ int ret;
+
+ ret = alloc_private_vm_region(iommu, &viommu->devid_table,
+ VIOMMU_DEVID_MAPPING_BASE,
+ VIOMMU_DEVID_MAPPING_ENTRY_SIZE,
+ viommu->gid);
+ if (ret)
+ goto err_out;
+
+ ret = alloc_private_vm_region(iommu, &viommu->domid_table,
+ VIOMMU_DOMID_MAPPING_BASE,
+ VIOMMU_DOMID_MAPPING_ENTRY_SIZE,
+ viommu->gid);
+ if (ret)
+ goto err_out;
+
+ return 0;
+err_out:
+ amd_viommu_uninit_one(iommu, viommu);
+ return -ENOMEM;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 16/22] iommu/amd: Introduce IOMMUFD vDevice support for AMD
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (14 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 15/22] iommu/amd: Add helper functions to manage DevID / DomID mapping tables Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:31 ` Jason Gunthorpe
2026-06-29 15:35 ` [PATCH v3 17/22] iommu/amd: Introduce helper function for updating domain ID mapping table Suravee Suthikulpanit
` (6 subsequent siblings)
22 siblings, 1 reply; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Initialize vDevice for AMD vIOMMU by setting up the Device ID Mapping
table using the guest device ID.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu_types.h | 12 +++++++++++
drivers/iommu/amd/iommufd.c | 33 +++++++++++++++++++++++++++++
drivers/iommu/amd/nested.c | 12 +++++++++++
3 files changed, 57 insertions(+)
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 03346258e2dc..027d9cfa5533 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -388,6 +388,11 @@
#define DTE_GPT_LEVEL_SHIFT 54
#define DTE_GPT_LEVEL_MASK GENMASK_ULL(55, 54)
+/* vIOMMU bit fields */
+#define DTE_VIOMMU_EN_SHIFT 15
+#define DTE_VIOMMU_GDEVICEID_MASK GENMASK_ULL(31, 16)
+#define DTE_VIOMMU_GUESTID_MASK GENMASK_ULL(47, 32)
+
#define GCR3_VALID 0x01ULL
/* DTE[128:179] | DTE[184:191] */
@@ -895,6 +900,9 @@ struct iommu_dev_data {
bool defer_attach;
struct ratelimit_state rs; /* Ratelimit IOPF messages */
+
+ u16 gid; /* Guest ID */
+ u16 gDevId; /* Guest Device ID */
};
/* Map HPET and IOAPIC ids to the devid used by the IOMMU */
@@ -1122,6 +1130,10 @@ struct amd_irte_ops {
void (*clear_allocated)(struct irq_remap_table *, int);
};
+struct amd_iommu_vdevice {
+ struct iommufd_vdevice core;
+};
+
#ifdef CONFIG_IRQ_REMAP
extern struct amd_irte_ops irte_32_ops;
extern struct amd_irte_ops irte_128_ops;
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index bfc4b0ec22a9..1d159b78e37c 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -9,6 +9,7 @@
#include "amd_iommu.h"
#include "amd_viommu.h"
#include "amd_iommu_types.h"
+#include "../iommufd/iommufd_private.h"
static const struct iommufd_viommu_ops amd_viommu_ops;
@@ -126,6 +127,36 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)
amd_iommu_gid_free(iommu, aviommu->gid);
}
+/*
+ * Called from drivers/iommu/iommufd/viommu.c: iommufd_vdevice_alloc_ioctl()
+ */
+static int _amd_viommu_vdevice_init(struct iommufd_vdevice *vdev)
+{
+ struct iommu_dev_data *dev_data;
+ struct pci_dev *pdev = to_pci_dev(vdev->idev->dev);
+ struct iommufd_viommu *viommu = vdev->viommu;
+ struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
+
+ if (!pdev) {
+ pr_err("%s: not a PCI device\n", __func__);
+ return -EINVAL;
+ }
+
+ dev_data = dev_iommu_priv_get(&pdev->dev);
+ if (!dev_data) {
+ pr_err("%s: Device not found (devid=%#x)\n",
+ __func__, pci_dev_id(pdev));
+ return -EINVAL;
+ }
+
+ dev_data->gid = aviommu->gid;
+ dev_data->gDevId = vdev->virt_id;
+ pr_debug("%s: gid=%#x, hdev_id=%#x, gdev_id=%#x\n", __func__,
+ dev_data->gid, pci_dev_id(pdev), dev_data->gDevId);
+
+ return 0;
+}
+
/*
* See include/linux/iommufd.h
* struct iommufd_viommu_ops - vIOMMU specific operations
@@ -133,4 +164,6 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)
static const struct iommufd_viommu_ops amd_viommu_ops = {
.alloc_domain_nested = amd_iommu_alloc_domain_nested,
.destroy = amd_iommufd_viommu_destroy,
+ .vdevice_size = VDEVICE_STRUCT_SIZE(struct amd_iommu_vdevice, core),
+ .vdevice_init = _amd_viommu_vdevice_init,
};
diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c
index 81a5ae00225e..524362aed269 100644
--- a/drivers/iommu/amd/nested.c
+++ b/drivers/iommu/amd/nested.c
@@ -227,6 +227,18 @@ static void set_dte_nested(struct amd_iommu *iommu, struct iommu_domain *dom,
/* Guest paging mode */
new->data[2] |= gdte->dte[2] & DTE_GPT_LEVEL_MASK;
+
+ /* vImuEn */
+ new->data[3] |= 1ULL << DTE_VIOMMU_EN_SHIFT;
+
+ /* GDeviceID */
+ new->data[3] |= FIELD_PREP(DTE_VIOMMU_GDEVICEID_MASK,
+ dev_data->gDevId);
+
+ /* GuestID */
+ new->data[3] |= FIELD_PREP(DTE_VIOMMU_GUESTID_MASK,
+ dev_data->gid);
+
}
static int nested_attach_device(struct iommu_domain *dom, struct device *dev,
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 16/22] iommu/amd: Introduce IOMMUFD vDevice support for AMD
2026-06-29 15:35 ` [PATCH v3 16/22] iommu/amd: Introduce IOMMUFD vDevice support for AMD Suravee Suthikulpanit
@ 2026-07-07 14:31 ` Jason Gunthorpe
0 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:31 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:29PM +0000, Suravee Suthikulpanit wrote:
> @@ -388,6 +388,11 @@
> #define DTE_GPT_LEVEL_SHIFT 54
> #define DTE_GPT_LEVEL_MASK GENMASK_ULL(55, 54)
>
> +/* vIOMMU bit fields */
> +#define DTE_VIOMMU_EN_SHIFT 15
> +#define DTE_VIOMMU_GDEVICEID_MASK GENMASK_ULL(31, 16)
> +#define DTE_VIOMMU_GUESTID_MASK GENMASK_ULL(47, 32)
> +
> #define GCR3_VALID 0x01ULL
>
> /* DTE[128:179] | DTE[184:191] */
> @@ -895,6 +900,9 @@ struct iommu_dev_data {
> bool defer_attach;
>
> struct ratelimit_state rs; /* Ratelimit IOPF messages */
> +
> + u16 gid; /* Guest ID */
> + u16 gDevId; /* Guest Device ID */
> };
?
I don't expect this information to ever be stored in a dev_data? why
is it needed here? You should be able to reach the viommu from any
places that would need it.
> @@ -227,6 +227,18 @@ static void set_dte_nested(struct amd_iommu *iommu, struct iommu_domain *dom,
>
> /* Guest paging mode */
> new->data[2] |= gdte->dte[2] & DTE_GPT_LEVEL_MASK;
> +
> + /* vImuEn */
> + new->data[3] |= 1ULL << DTE_VIOMMU_EN_SHIFT;
> +
> + /* GDeviceID */
> + new->data[3] |= FIELD_PREP(DTE_VIOMMU_GDEVICEID_MASK,
> + dev_data->gDevId);
> +
> + /* GuestID */
> + new->data[3] |= FIELD_PREP(DTE_VIOMMU_GUESTID_MASK,
> + dev_data->gid);
> +
> }
Like here, the iommu_domain should have a path to the viommu if it is
in fact a viommu based nested domain, and only in that case should the
DTE gain the gid/gdevid.
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v3 17/22] iommu/amd: Introduce helper function for updating domain ID mapping table
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (15 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 16/22] iommu/amd: Introduce IOMMUFD vDevice support for AMD Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:32 ` Jason Gunthorpe
2026-06-29 15:35 ` [PATCH v3 18/22] iommu/amd: Introduce helper function for updating device " Suravee Suthikulpanit
` (5 subsequent siblings)
22 siblings, 1 reply; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
AMD vIOMMU hardware uses the Domain ID mapping table to map Guest Domain ID
(GDomID) to Host Domain ID when it virtualises guest IOMMU commands.
It uses GID and GDomID to index into the table to look up host domain ID.
Linux IOMMU driver programs the table entry using VFCntlMMIO Guest Domain
Map Control Register.
Introduce amd_viommu_domain_id_update(), which is used to set the entry
when attaching the nested device. Clearing the entry is done during VM
destroy.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_viommu.h | 2 ++
drivers/iommu/amd/nested.c | 5 ++++
drivers/iommu/amd/viommu.c | 46 ++++++++++++++++++++++++++++++++++
3 files changed, 53 insertions(+)
diff --git a/drivers/iommu/amd/amd_viommu.h b/drivers/iommu/amd/amd_viommu.h
index 8b57717c22a6..b6fd5ffc3b82 100644
--- a/drivers/iommu/amd/amd_viommu.h
+++ b/drivers/iommu/amd/amd_viommu.h
@@ -18,6 +18,8 @@ int amd_viommu_init_one(struct amd_iommu *iommu, struct amd_iommu_viommu *viommu
void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *viommu);
+int amd_viommu_domain_id_update(struct amd_iommu *iommu, u16 gid,
+ u16 hdom_id, u16 gdom_id);
#else
static inline int amd_viommu_init(struct amd_iommu *iommu)
diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c
index 524362aed269..6fff9e67bdd8 100644
--- a/drivers/iommu/amd/nested.c
+++ b/drivers/iommu/amd/nested.c
@@ -10,6 +10,7 @@
#include <uapi/linux/iommufd.h>
#include "amd_iommu.h"
+#include "amd_viommu.h"
static const struct iommu_domain_ops nested_domain_ops;
@@ -245,6 +246,7 @@ static int nested_attach_device(struct iommu_domain *dom, struct device *dev,
struct iommu_domain *old)
{
struct dev_table_entry new = {0};
+ struct nested_domain *ndom = to_ndomain(dom);
struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev);
struct amd_iommu *iommu = get_amd_iommu_from_dev_data(dev_data);
int ret = 0;
@@ -262,6 +264,9 @@ static int nested_attach_device(struct iommu_domain *dom, struct device *dev,
amd_iommu_update_dte(iommu, dev_data, &new);
+ ret = amd_viommu_domain_id_update(iommu, dev_data->gid,
+ ndom->gdom_info->hdom_id, ndom->gdom_id);
+
mutex_unlock(&dev_data->mutex);
return ret;
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index 27b17edb910e..9b471aadd5df 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -40,6 +40,8 @@
#define VIOMMU_DOMID_MAPPING_BASE 0x2000000000ULL
#define VIOMMU_DOMID_MAPPING_ENTRY_SIZE (1 << 19)
+#define VIOMMU_VFCTRL_GUEST_DID_MAP_CONTROL1_OFFSET 0x08
+
LIST_HEAD(viommu_devid_map);
static int viommu_init_pci_vsc(struct amd_iommu *iommu)
@@ -427,6 +429,22 @@ static void __maybe_unused free_private_vm_region(struct amd_iommu *iommu, u64 *
*entry = NULL;
}
+static void viommu_clear_mapping(struct amd_iommu *iommu,
+ struct amd_iommu_viommu *aviommu)
+{
+ int i;
+ u16 gid = aviommu->gid;
+
+ /*
+ * IOMMU hardware uses the domain ID mapping table to map gdom ID to hdom ID.
+ * If the mapping does not exist, the hardware would generate error in the event log.
+ * Therefore, initialize all gdom ID entries to map to parent domain ID to prevent
+ * unknown mapping scenario.
+ */
+ for (i = 0; i <= VIOMMU_MAX_GDOMID; i++)
+ amd_viommu_domain_id_update(iommu, gid, aviommu->parent->id, i);
+}
+
void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *aviommu)
{
pr_debug("%s: gid=%u\n", __func__, aviommu->gid);
@@ -439,6 +457,7 @@ void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *avi
VIOMMU_DOMID_MAPPING_BASE,
VIOMMU_DOMID_MAPPING_ENTRY_SIZE,
aviommu->gid);
+ viommu_clear_mapping(iommu, aviommu);
}
int amd_viommu_init_one(struct amd_iommu *iommu, struct amd_iommu_viommu *viommu)
@@ -459,8 +478,35 @@ int amd_viommu_init_one(struct amd_iommu *iommu, struct amd_iommu_viommu *viommu
if (ret)
goto err_out;
+ viommu_clear_mapping(iommu, viommu);
+
return 0;
err_out:
amd_viommu_uninit_one(iommu, viommu);
return -ENOMEM;
}
+
+/*
+ * Program the DomID via VFCTRL registers
+ * This function will be called during VM init via VFIO.
+ */
+
+ #define DOMID_ENTRY_GDOMID_MASK GENMASK_ULL(61, 46)
+ #define DOMID_ENTRY_HDOMID_MASK GENMASK_ULL(29, 14)
+ #define DOMID_ENTRY_VALID BIT_ULL(0)
+ #define DOMID_ENTRY_WRITE BIT_ULL(63)
+
+int amd_viommu_domain_id_update(struct amd_iommu *iommu, u16 gid,
+ u16 hdom_id, u16 gdom_id)
+{
+ u64 val;
+ u8 __iomem *vfctrl = VIOMMU_VFCTRL_MMIO_BASE(iommu, gid);
+
+ val = FIELD_PREP(DOMID_ENTRY_GDOMID_MASK, gdom_id) |
+ FIELD_PREP(DOMID_ENTRY_HDOMID_MASK, hdom_id) |
+ DOMID_ENTRY_WRITE | DOMID_ENTRY_VALID;
+
+ writeq(val, vfctrl + VIOMMU_VFCTRL_GUEST_DID_MAP_CONTROL1_OFFSET);
+ return 0;
+}
+EXPORT_SYMBOL(amd_viommu_domain_id_update);
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 17/22] iommu/amd: Introduce helper function for updating domain ID mapping table
2026-06-29 15:35 ` [PATCH v3 17/22] iommu/amd: Introduce helper function for updating domain ID mapping table Suravee Suthikulpanit
@ 2026-07-07 14:32 ` Jason Gunthorpe
0 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:32 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:30PM +0000, Suravee Suthikulpanit wrote:
> +int amd_viommu_domain_id_update(struct amd_iommu *iommu, u16 gid,
> + u16 hdom_id, u16 gdom_id)
> +{
> + u64 val;
> + u8 __iomem *vfctrl = VIOMMU_VFCTRL_MMIO_BASE(iommu, gid);
> +
> + val = FIELD_PREP(DOMID_ENTRY_GDOMID_MASK, gdom_id) |
> + FIELD_PREP(DOMID_ENTRY_HDOMID_MASK, hdom_id) |
> + DOMID_ENTRY_WRITE | DOMID_ENTRY_VALID;
> +
> + writeq(val, vfctrl + VIOMMU_VFCTRL_GUEST_DID_MAP_CONTROL1_OFFSET);
> + return 0;
> +}
> +EXPORT_SYMBOL(amd_viommu_domain_id_update);
Why is this exported?
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v3 18/22] iommu/amd: Introduce helper function for updating device ID mapping table
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (16 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 17/22] iommu/amd: Introduce helper function for updating domain ID mapping table Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-06-29 15:35 ` [PATCH v3 19/22] iommu/amd: Add per-segment translate device ID pool Suravee Suthikulpanit
` (4 subsequent siblings)
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
AMD vIOMMU hardware uses the Device ID mapping table to map Guest Device ID
(GDevID) to Host Device ID when it virtualises guest IOMMU commands.
It uses GID and GDevID to indexe into the table to look up host device ID.
Linux IOMMU driver programs the table entry using VFCntlMMIO Guest Device
Map Control Register.
Introduce amd_viommu_set/clear_device_mapping(), which are used to set
the entry when initialize the IOMMUFD vDevice. Clearing the entry is
done during VM destroy.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_viommu.h | 8 ++++++
drivers/iommu/amd/iommufd.c | 3 ++
drivers/iommu/amd/viommu.c | 52 ++++++++++++++++++++++++++++++++++
3 files changed, 63 insertions(+)
diff --git a/drivers/iommu/amd/amd_viommu.h b/drivers/iommu/amd/amd_viommu.h
index b6fd5ffc3b82..3a8f41baaab9 100644
--- a/drivers/iommu/amd/amd_viommu.h
+++ b/drivers/iommu/amd/amd_viommu.h
@@ -20,6 +20,9 @@ void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *vio
int amd_viommu_domain_id_update(struct amd_iommu *iommu, u16 gid,
u16 hdom_id, u16 gdom_id);
+
+void amd_viommu_set_device_mapping(struct amd_iommu *iommu, u16 hDevId,
+ u16 guestId, u16 gDevId);
#else
static inline int amd_viommu_init(struct amd_iommu *iommu)
@@ -45,6 +48,11 @@ static inline void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iom
{
}
+static inline void amd_viommu_set_device_mapping(struct amd_iommu *iommu, u16 hDevId,
+ u16 guestId, u16 gDevId)
+{
+}
+
#endif /* CONFIG_AMD_IOMMU_IOMMUFD */
#endif /* AMD_VIOMMU_H */
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index 1d159b78e37c..8b569b469a07 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -136,6 +136,7 @@ static int _amd_viommu_vdevice_init(struct iommufd_vdevice *vdev)
struct pci_dev *pdev = to_pci_dev(vdev->idev->dev);
struct iommufd_viommu *viommu = vdev->viommu;
struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
+ struct amd_iommu *iommu = container_of(viommu->iommu_dev, struct amd_iommu, iommu);
if (!pdev) {
pr_err("%s: not a PCI device\n", __func__);
@@ -154,6 +155,8 @@ static int _amd_viommu_vdevice_init(struct iommufd_vdevice *vdev)
pr_debug("%s: gid=%#x, hdev_id=%#x, gdev_id=%#x\n", __func__,
dev_data->gid, pci_dev_id(pdev), dev_data->gDevId);
+ amd_viommu_set_device_mapping(iommu, pci_dev_id(pdev), dev_data->gid, dev_data->gDevId);
+
return 0;
}
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index 9b471aadd5df..7b3127d829c5 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -40,6 +40,7 @@
#define VIOMMU_DOMID_MAPPING_BASE 0x2000000000ULL
#define VIOMMU_DOMID_MAPPING_ENTRY_SIZE (1 << 19)
+#define VIOMMU_VFCTRL_GUEST_DID_MAP_CONTROL0_OFFSET 0x00
#define VIOMMU_VFCTRL_GUEST_DID_MAP_CONTROL1_OFFSET 0x08
LIST_HEAD(viommu_devid_map);
@@ -429,6 +430,53 @@ static void __maybe_unused free_private_vm_region(struct amd_iommu *iommu, u64 *
*entry = NULL;
}
+#define DEVID_ENTRY_GDEVID_MASK GENMASK_ULL(61, 46)
+#define DEVID_ENTRY_HDEVID_MASK GENMASK_ULL(29, 14)
+#define DEVID_ENTRY_WRITE BIT_ULL(63)
+#define DEVID_ENTRY_VALID BIT_ULL(0)
+
+/*
+ * Program the DevID via VFCTRL registers
+ * This function will be called during VM init via VFIO.
+ */
+void amd_viommu_set_device_mapping(struct amd_iommu *iommu, u16 hDevId,
+ u16 guestId, u16 gDevId)
+{
+ u64 val;
+ u8 __iomem *vfctrl;
+
+ pr_debug("%s: iommu_devid=%#x, gid=%#x, hDevId=%#x, gDevId=%#x\n",
+ __func__, pci_dev_id(iommu->dev), guestId, hDevId, gDevId);
+
+ val = FIELD_PREP(DEVID_ENTRY_GDEVID_MASK, gDevId) |
+ FIELD_PREP(DEVID_ENTRY_HDEVID_MASK, hDevId) |
+ DEVID_ENTRY_WRITE | DEVID_ENTRY_VALID;
+
+ vfctrl = VIOMMU_VFCTRL_MMIO_BASE(iommu, guestId);
+
+ writeq(val, vfctrl + VIOMMU_VFCTRL_GUEST_DID_MAP_CONTROL0_OFFSET);
+}
+
+/*
+ * Clear the DevID via VFCTRL registers
+ * This function will be called during VM destroy via VFIO.
+ */
+static void clear_device_mapping(struct amd_iommu *iommu, u16 guestId, u16 gDevId)
+{
+ u64 val;
+ u8 __iomem *vfctrl;
+
+ /*
+ * Clear the DevID in VFCTRL registers
+ */
+ val = FIELD_PREP(DEVID_ENTRY_GDEVID_MASK, gDevId) |
+ FIELD_PREP(DEVID_ENTRY_HDEVID_MASK, 0) |
+ DEVID_ENTRY_WRITE | DEVID_ENTRY_VALID;
+
+ vfctrl = VIOMMU_VFCTRL_MMIO_BASE(iommu, guestId);
+ writeq(val, vfctrl + VIOMMU_VFCTRL_GUEST_DID_MAP_CONTROL0_OFFSET);
+}
+
static void viommu_clear_mapping(struct amd_iommu *iommu,
struct amd_iommu_viommu *aviommu)
{
@@ -443,6 +491,10 @@ static void viommu_clear_mapping(struct amd_iommu *iommu,
*/
for (i = 0; i <= VIOMMU_MAX_GDOMID; i++)
amd_viommu_domain_id_update(iommu, gid, aviommu->parent->id, i);
+
+ for (i = 0; i <= VIOMMU_MAX_GDEVID; i++)
+ clear_device_mapping(iommu, gid, i);
+
}
void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *aviommu)
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH v3 19/22] iommu/amd: Add per-segment translate device ID pool
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (17 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 18/22] iommu/amd: Introduce helper function for updating device " Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:36 ` Jason Gunthorpe
2026-06-29 15:35 ` [PATCH v3 20/22] iommu/amd: Reserve translate-device-id for PCI requestor aliases Suravee Suthikulpanit
` (3 subsequent siblings)
22 siblings, 1 reply; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Track translate-device-id slots per PCI segment so real PCI device IDs can
be reserved for normal DTE programming and excluded from dynamic allocation
for vIOMMU translation DTEs.
Add amd_iommu_pci_seg_trans_devid_init/fini() during segment setup and
teardown, amd_iommu_trans_devid_reserve() for attach-time reservation, and
trans_devid.c implementing the xarray-backed state machine.
Call the reserve hook from amd_iommu_attach_device() before programming
the DTE.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/Makefile | 2 +-
drivers/iommu/amd/amd_iommu.h | 19 +++++++
drivers/iommu/amd/amd_iommu_types.h | 17 ++++++
drivers/iommu/amd/init.c | 3 ++
drivers/iommu/amd/iommu.c | 12 +++++
drivers/iommu/amd/trans_devid.c | 83 +++++++++++++++++++++++++++++
6 files changed, 135 insertions(+), 1 deletion(-)
create mode 100644 drivers/iommu/amd/trans_devid.c
diff --git a/drivers/iommu/amd/Makefile b/drivers/iommu/amd/Makefile
index e1e824b9c7b0..12c3fe83e4ce 100644
--- a/drivers/iommu/amd/Makefile
+++ b/drivers/iommu/amd/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += iommu.o init.o quirks.o ppr.o pasid.o
-obj-$(CONFIG_AMD_IOMMU_IOMMUFD) += iommufd.o nested.o viommu.o
+obj-$(CONFIG_AMD_IOMMU_IOMMUFD) += iommufd.o nested.o viommu.o trans_devid.o
obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += debugfs.o
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index aaa57840e904..f99225be5d07 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -218,6 +218,25 @@ void amd_iommu_update_dte(struct amd_iommu *iommu,
struct dev_table_entry *new);
int amd_iommu_completion_wait(struct amd_iommu *iommu);
+/* Per-segment translate-device-id pool (CONFIG_AMD_IOMMU_IOMMUFD) */
+#ifdef CONFIG_AMD_IOMMU_IOMMUFD
+void amd_iommu_pci_seg_trans_devid_init(struct amd_iommu_pci_seg *pci_seg);
+void amd_iommu_pci_seg_trans_devid_fini(struct amd_iommu_pci_seg *pci_seg);
+int amd_iommu_trans_devid_reserve(struct amd_iommu_pci_seg *pci_seg, u16 id);
+#else
+static inline void
+amd_iommu_pci_seg_trans_devid_init(struct amd_iommu_pci_seg *pci_seg) { }
+static inline void
+amd_iommu_pci_seg_trans_devid_fini(struct amd_iommu_pci_seg *pci_seg) { }
+#endif
+
+int amd_iommu_set_translate_dte(struct amd_iommu *iommu,
+ struct protection_domain *pdom,
+ u16 gid, u32 trans_devid);
+void amd_iommu_clear_translate_dte(struct amd_iommu *iommu, u32 trans_devid);
+void amd_iommu_update_vfctrl_mmio_translate_devid(struct amd_iommu *iommu,
+ u16 gid, u32 trans_devid);
+
static inline void
amd_iommu_make_clear_dte(struct amd_iommu *iommu, u16 devid,
struct dev_table_entry *new)
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 027d9cfa5533..91cdb61b5254 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -612,6 +612,14 @@ PT_IOMMU_CHECK_DOMAIN(struct protection_domain, iommu, domain);
PT_IOMMU_CHECK_DOMAIN(struct protection_domain, amdv1.iommu, domain);
PT_IOMMU_CHECK_DOMAIN(struct protection_domain, amdv2.iommu, domain);
+#ifdef CONFIG_AMD_IOMMU_IOMMUFD
+enum trans_devid_state {
+ TRANS_DEVID_FREE = 0,
+ TRANS_DEVID_RESERVED,
+ TRANS_DEVID_ALLOCATED,
+};
+#endif
+
/*
* This structure contains information about one PCI segment in the system.
*/
@@ -673,6 +681,15 @@ struct amd_iommu_pci_seg {
* parsing time.
*/
struct list_head unity_map;
+
+#ifdef CONFIG_AMD_IOMMU_IOMMUFD
+ /*
+ * Per-segment translate-device-id allocation. The xarray is indexed by
+ * the translate-device-id. The value is the state (enum trans_devid_state).
+ */
+ struct mutex trans_devid_mutex;
+ struct xarray trans_devid_xa;
+#endif
};
/*
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index 6e69b3dd8b1e..622bc0337eda 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -1737,6 +1737,8 @@ static struct amd_iommu_pci_seg *__init alloc_pci_segment(u16 id,
if (alloc_rlookup_table(pci_seg))
goto err_free_alias_table;
+ amd_iommu_pci_seg_trans_devid_init(pci_seg);
+
return pci_seg;
err_free_alias_table:
@@ -1768,6 +1770,7 @@ static void __init free_pci_segments(void)
for_each_pci_segment_safe(pci_seg, next) {
list_del(&pci_seg->list);
+ amd_iommu_pci_seg_trans_devid_fini(pci_seg);
free_irq_lookup_table(pci_seg);
free_rlookup_table(pci_seg);
free_alias_table(pci_seg);
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 3b59938c2f86..01b755a2baed 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -3075,6 +3075,18 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, struct device *dev,
if (dom->dirty_ops && !amd_iommu_hd_support(iommu))
return -EINVAL;
+#if IS_ENABLED(CONFIG_AMD_IOMMU_IOMMUFD)
+ /* Translate-device-id reservation must be done before setting up
+ * the DTE for the device to make sure that the id has not been allocated
+ * yet. (See amd_iommu_trans_devid_alloc().)
+ */
+ ret = amd_iommu_trans_devid_reserve(iommu->pci_seg, dev_data->devid);
+ if (ret) {
+ pr_err("%s: Failed to reserve device id %#x\n", __func__, dev_data->devid);
+ return ret;
+ }
+#endif
+
if (dev_data->domain)
detach_device(dev);
diff --git a/drivers/iommu/amd/trans_devid.c b/drivers/iommu/amd/trans_devid.c
new file mode 100644
index 000000000000..e5cca409d134
--- /dev/null
+++ b/drivers/iommu/amd/trans_devid.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ *
+ * AMD vIOMMU translate-device-id management.
+ *
+ * The id must be allocated from unused range. It is used to program the vIOMMU VF Control
+ * register to specify the DTE used to contain the GPA->SPA mapping (v1 page table).
+ */
+
+#include <linux/kernel.h>
+#include <linux/xarray.h>
+
+#include "amd_iommu.h"
+
+static inline enum trans_devid_state trans_devid_xa_get_state(void *entry)
+{
+ if (!entry)
+ return TRANS_DEVID_FREE;
+ if (WARN_ON_ONCE(!xa_is_value(entry)))
+ return TRANS_DEVID_FREE;
+ return (enum trans_devid_state)xa_to_value(entry);
+}
+
+static inline void *trans_devid_xa_mk_state(enum trans_devid_state s)
+{
+ return xa_mk_value((unsigned long)s);
+}
+
+void amd_iommu_pci_seg_trans_devid_init(struct amd_iommu_pci_seg *pci_seg)
+{
+ mutex_init(&pci_seg->trans_devid_mutex);
+ xa_init(&pci_seg->trans_devid_xa);
+}
+
+void amd_iommu_pci_seg_trans_devid_fini(struct amd_iommu_pci_seg *pci_seg)
+{
+ xa_destroy(&pci_seg->trans_devid_xa);
+}
+
+/**
+ * amd_iommu_trans_devid_reserve - occupy @id so it is never returned by alloc
+ *
+ * Reservation is done when attaching device to a domain (see amd_iommu_attach_device()).
+ *
+ * Note: Since PCI hot-plug devices are enumerated during runtime, they could clash
+ * with the translate-device-id allocation. In such case, amd_iommu_trans_devid_reserve()
+ * could fail with %-EBUSY. This can be avoided by reserving the hot-plug id range if it
+ * is known in advance.
+ *
+ * Return: 0 on success, %-EBUSY if @id is already allocated. A second reserve of
+ * an already-reserved @id succeeds.
+ */
+int amd_iommu_trans_devid_reserve(struct amd_iommu_pci_seg *pci_seg, u16 id)
+{
+ void *entry, *old;
+ int ret = 0;
+
+ mutex_lock(&pci_seg->trans_devid_mutex);
+ entry = xa_load(&pci_seg->trans_devid_xa, id);
+ switch (trans_devid_xa_get_state(entry)) {
+ case TRANS_DEVID_ALLOCATED:
+ ret = -EBUSY;
+ break;
+ case TRANS_DEVID_RESERVED:
+ break;
+ case TRANS_DEVID_FREE:
+ old = xa_store(&pci_seg->trans_devid_xa, id,
+ trans_devid_xa_mk_state(TRANS_DEVID_RESERVED), GFP_KERNEL);
+ if (xa_is_err(old)) {
+ ret = xa_err(old);
+ break;
+ }
+ WARN_ON_ONCE(old);
+ break;
+ }
+ mutex_unlock(&pci_seg->trans_devid_mutex);
+
+ if (!ret)
+ pr_debug("%s: Reserved trans_devid %#x (seg %#x)\n", __func__, id,
+ pci_seg->id);
+ return ret;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 19/22] iommu/amd: Add per-segment translate device ID pool
2026-06-29 15:35 ` [PATCH v3 19/22] iommu/amd: Add per-segment translate device ID pool Suravee Suthikulpanit
@ 2026-07-07 14:36 ` Jason Gunthorpe
0 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:36 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:32PM +0000, Suravee Suthikulpanit wrote:
> Track translate-device-id slots per PCI segment so real PCI device IDs can
> be reserved for normal DTE programming and excluded from dynamic allocation
> for vIOMMU translation DTEs.
Why does it have to be per segment? The segment is encoded in the
devid isn't it?
This seems like a sketchy design to me, is it impossible to atomically
change the viommu used devids once they start being used? It seems
like this should be doable
If so the algorithm would be simpler, just maintain an xarray of all
dev ids, if the entry is marked as a VIOMMU and PCI needs it then move
the VIOMMU out of the way and use the entry for PCI.
No need to try to guess what ranges are usable or not.
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v3 20/22] iommu/amd: Reserve translate-device-id for PCI requestor aliases
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (18 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 19/22] iommu/amd: Add per-segment translate device ID pool Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:39 ` Jason Gunthorpe
2026-06-29 15:35 ` [PATCH v3 21/22] iommu/amd: Add translation DTE and VFctrl TransDevID helpers Suravee Suthikulpanit
` (2 subsequent siblings)
22 siblings, 1 reply; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
The per-segment translate-device-id (trans_devid) pool hands out numeric
device-table indices used by the AMD vIOMMU / iommufd path (e.g. mapping a
KVM file descriptor to a shared trans_devid). amd_iommu_attach_device()
already calls amd_iommu_trans_devid_reserve() for the struct device's own
PCI BDF so that id cannot later be returned by the allocator.
That is not sufficient on its own. The AMD IOMMU driver programs identical
DMA translation device-table entries (DTEs) for every requestor ID that can
issue DMA on behalf of the same PCI function: the IVRS alias from
alias_table[] when it is not covered by the PCI DMA-alias walk (different
bus than the device), and every alias visited by pci_for_each_dma_alias().
Those alternate BDFs are not separate struct device attach targets, so they
never received a trans_devid reservation and could in principle collide
with a dynamically allocated trans_devid.
Introduce amd_iommu_trans_devid_reserve_pci_aliases() in trans_devid.c and
invoke it from amd_iommu_attach_device() immediately after the primary
amd_iommu_trans_devid_reserve() succeeds. For PCI devices the helper
reserves the IVRS alias when it differs from the device BDF, then walks
pci_for_each_dma_alias() and reserves each alias BDF. Repeated attach and
overlap with the primary BDF in the PCI walk are handled by the existing
idempotency of amd_iommu_trans_devid_reserve() (a second reserve of an
already-reserved id succeeds).
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 2 ++
drivers/iommu/amd/iommu.c | 7 +++++
drivers/iommu/amd/trans_devid.c | 45 +++++++++++++++++++++++++++++++++
3 files changed, 54 insertions(+)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index f99225be5d07..7300f07e7a39 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -223,6 +223,8 @@ int amd_iommu_completion_wait(struct amd_iommu *iommu);
void amd_iommu_pci_seg_trans_devid_init(struct amd_iommu_pci_seg *pci_seg);
void amd_iommu_pci_seg_trans_devid_fini(struct amd_iommu_pci_seg *pci_seg);
int amd_iommu_trans_devid_reserve(struct amd_iommu_pci_seg *pci_seg, u16 id);
+int amd_iommu_trans_devid_reserve_pci_aliases(struct amd_iommu *iommu,
+ struct device *dev);
#else
static inline void
amd_iommu_pci_seg_trans_devid_init(struct amd_iommu_pci_seg *pci_seg) { }
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 01b755a2baed..fd25c4c361f3 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -3085,6 +3085,13 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, struct device *dev,
pr_err("%s: Failed to reserve device id %#x\n", __func__, dev_data->devid);
return ret;
}
+
+ ret = amd_iommu_trans_devid_reserve_pci_aliases(iommu, dev);
+ if (ret) {
+ pr_err("%s: Failed to reserve translate devid for alias of %#x (err %d)\n",
+ __func__, dev_data->devid, ret);
+ return ret;
+ }
#endif
if (dev_data->domain)
diff --git a/drivers/iommu/amd/trans_devid.c b/drivers/iommu/amd/trans_devid.c
index e5cca409d134..7c57087bfa10 100644
--- a/drivers/iommu/amd/trans_devid.c
+++ b/drivers/iommu/amd/trans_devid.c
@@ -9,6 +9,7 @@
*/
#include <linux/kernel.h>
+#include <linux/pci.h>
#include <linux/xarray.h>
#include "amd_iommu.h"
@@ -81,3 +82,47 @@ int amd_iommu_trans_devid_reserve(struct amd_iommu_pci_seg *pci_seg, u16 id)
pci_seg->id);
return ret;
}
+
+static int reserve_trans_devid_each_dma_alias(struct pci_dev *pdev, u16 alias,
+ void *data)
+{
+ struct amd_iommu_pci_seg *pci_seg = data;
+
+ (void)pdev;
+ return amd_iommu_trans_devid_reserve(pci_seg, alias);
+}
+
+/**
+ * amd_iommu_trans_devid_reserve_pci_aliases - reserve translate-device-ids for
+ * PCI DMA aliases and for the IVRS alias when it is not walked as a PCI DMA
+ * alias (different bus). Idempotent for repeated attach; see
+ * amd_iommu_trans_devid_reserve().
+ *
+ * Return: 0 on success or if @dev is not PCI; otherwise an errno from
+ * amd_iommu_trans_devid_reserve() or pci_for_each_dma_alias().
+ */
+int amd_iommu_trans_devid_reserve_pci_aliases(struct amd_iommu *iommu,
+ struct device *dev)
+{
+ struct pci_dev *pdev;
+ struct amd_iommu_pci_seg *pci_seg;
+ u16 devid, ivrs_alias;
+ int ret;
+
+ if (!dev_is_pci(dev))
+ return 0;
+
+ pdev = to_pci_dev(dev);
+ pci_seg = iommu->pci_seg;
+ devid = pci_dev_id(pdev);
+
+ ivrs_alias = pci_seg->alias_table[devid];
+ if (ivrs_alias != devid) {
+ ret = amd_iommu_trans_devid_reserve(pci_seg, ivrs_alias);
+ if (ret)
+ return ret;
+ }
+
+ return pci_for_each_dma_alias(pdev, reserve_trans_devid_each_dma_alias,
+ pci_seg);
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 20/22] iommu/amd: Reserve translate-device-id for PCI requestor aliases
2026-06-29 15:35 ` [PATCH v3 20/22] iommu/amd: Reserve translate-device-id for PCI requestor aliases Suravee Suthikulpanit
@ 2026-07-07 14:39 ` Jason Gunthorpe
0 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:39 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:33PM +0000, Suravee Suthikulpanit wrote:
> The per-segment translate-device-id (trans_devid) pool hands out numeric
> device-table indices used by the AMD vIOMMU / iommufd path (e.g. mapping a
> KVM file descriptor to a shared trans_devid).
Nothing to do with kvm
> Introduce amd_iommu_trans_devid_reserve_pci_aliases() in trans_devid.c and
> invoke it from amd_iommu_attach_device() immediately after the primary
> amd_iommu_trans_devid_reserve() succeeds. For PCI devices the helper
> reserves the IVRS alias when it differs from the device BDF, then walks
> pci_for_each_dma_alias() and reserves each alias BDF. Repeated attach and
> overlap with the primary BDF in the PCI walk are handled by the existing
> idempotency of amd_iommu_trans_devid_reserve() (a second reserve of an
> already-reserved id succeeds).
It would be so nice if the AMD driver could get fixed up to work more
like ARM with the alais devids.
ie on probe you allocate an array of all the alias dev ids, fill it in
with the information from PCI and then never touch the PCI again.
Then you'd just dump the whole array into the allocator in one clean
shot and this would all be simpler.
Instead we have these endless pci walks every where :\
But its fine like this I suppose
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v3 21/22] iommu/amd: Add translation DTE and VFctrl TransDevID helpers
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (19 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 20/22] iommu/amd: Reserve translate-device-id for PCI requestor aliases Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:43 ` Jason Gunthorpe
2026-06-29 15:35 ` [PATCH v3 22/22] iommu/amd: Assign per-vIOMMU translate device ID Suravee Suthikulpanit
2026-07-07 14:45 ` [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Jason Gunthorpe
22 siblings, 1 reply; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
The hardware vIOMMU uses a per-VM translate device ID (TransDevID) to
index the host device table when guest IOMMU traffic needs GPA->SPA
translation. The VF Control guest miscellaneous register tells the
IOMMU which TransDevID to use; the corresponding device table entry
(DTE) points at the nested IOMMU v1 page table that performs the walk
from guest physical to system physical addresses.
Add amd_iommu_set_translate_dte() and amd_iommu_clear_translate_dte()
to install or clear that DTE for a given TransDevID slot through
iommu_dev_data and the existing DTE update path, restoring IVRS
persistent bits via amd_iommu_make_clear_dte() on teardown. Add
amd_iommu_update_vfctrl_mmio_translate_devid() to publish the
TransDevID in VFctrl guest-misc MMIO, and
VIOMMU_VFCTRL_GUEST_MISC_CONTROL_OFFSET for the register offset.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu_types.h | 1 +
drivers/iommu/amd/iommu.c | 78 ++++++++++++++++++++++++++++-
2 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 91cdb61b5254..cd10e33c1317 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -495,6 +495,7 @@ extern bool amdr_ivrs_remap_support;
/* VIOMMU stuff */
#define VIOMMU_VF_MMIO_ENTRY_SIZE 4096
#define VIOMMU_VFCTRL_MMIO_ENTRY_SIZE 64
+#define VIOMMU_VFCTRL_GUEST_MISC_CONTROL_OFFSET 0x10
/* Host ioremap/request_mem_region sizes for VF / VF_CNTL BARs */
#define VIOMMU_VF_MMIO_MAP_SIZE 0x10000000UL
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index fd25c4c361f3..410c2f91064a 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -213,7 +213,12 @@ void amd_iommu_update_dte(struct amd_iommu *iommu,
struct dev_table_entry *new)
{
update_dte256(iommu, dev_data, new);
- clone_aliases(iommu, dev_data->dev);
+ /*
+ * The dev_data for trans_devid does not have struct dev.
+ * So clone_aliases is not supported for translate-device-id.
+ */
+ if (dev_data->dev)
+ clone_aliases(iommu, dev_data->dev);
device_flush_dte(iommu, dev_data);
amd_iommu_completion_wait(iommu);
}
@@ -1747,7 +1752,11 @@ static int device_flush_dte(struct amd_iommu *iommu, struct iommu_dev_data *dev_
u16 alias;
int ret;
- if (dev_is_pci(dev_data->dev))
+ /*
+ * The dev_data for trans_devid does not have struct dev.
+ * So, it is not considered as a PCI device.
+ */
+ if (dev_data->dev && dev_is_pci(dev_data->dev))
pdev = to_pci_dev(dev_data->dev);
if (pdev)
@@ -3281,6 +3290,71 @@ static bool amd_iommu_enforce_cache_coherency(struct iommu_domain *domain)
return true;
}
+#if IS_ENABLED(CONFIG_AMD_IOMMU_IOMMUFD)
+
+void amd_iommu_update_vfctrl_mmio_translate_devid(struct amd_iommu *iommu,
+ u16 gid, u32 devid)
+{
+ writeq((devid & 0xFFFFULL) << 16,
+ VIOMMU_VFCTRL_MMIO_BASE(iommu, gid) +
+ VIOMMU_VFCTRL_GUEST_MISC_CONTROL_OFFSET);
+}
+
+int amd_iommu_set_translate_dte(struct amd_iommu *iommu,
+ struct protection_domain *pdom,
+ u16 gid, u32 trans_devid)
+{
+ struct dev_table_entry new = {};
+ struct iommu_dev_data *trans_dev_data;
+ struct pt_iommu_amdv1_hw_info pt_info;
+
+ trans_dev_data = search_dev_data(iommu, trans_devid);
+ if (!trans_dev_data) {
+ trans_dev_data = amd_iommu_alloc_dev_data(iommu, trans_devid);
+ if (!trans_dev_data) {
+ pr_err("%s: Failed to allocate dev_data for translate-device-id %#x\n",
+ __func__, trans_devid);
+ return -ENOMEM;
+ }
+ }
+
+ trans_dev_data->dev = NULL;
+ trans_dev_data->devid = trans_devid;
+ trans_dev_data->domain = pdom;
+ trans_dev_data->gid = gid;
+
+ amd_iommu_make_clear_dte(iommu, trans_devid, &new);
+ /* Setup DTE for v1 page table at the offset specified by trans_devid */
+ pt_iommu_amdv1_hw_info(&pdom->amdv1, &pt_info);
+
+ pr_debug("%s: gid=%#x, iommu_devid=%#x, devid=%#x, host_pt_root=%#llx, mode=%#x\n",
+ __func__, gid, iommu->devid, trans_devid, pt_info.host_pt_root, pt_info.mode);
+
+ amd_iommu_set_dte_v1(trans_dev_data, pdom, pdom->id, &pt_info, &new);
+ amd_iommu_update_dte(iommu, trans_dev_data, &new);
+ return 0;
+}
+
+void amd_iommu_clear_translate_dte(struct amd_iommu *iommu, u32 trans_devid)
+{
+ struct dev_table_entry new = {};
+ struct iommu_dev_data *trans_dev_data;
+
+ pr_debug("%s: iommu_devid=%#x, trans_devid=%#x\n",
+ __func__, iommu->devid, trans_devid);
+
+ trans_dev_data = search_dev_data(iommu, trans_devid);
+ if (!trans_dev_data) {
+ WARN_ON_ONCE(1);
+ return;
+ }
+
+ amd_iommu_make_clear_dte(iommu, trans_devid, &new);
+ amd_iommu_update_dte(iommu, trans_dev_data, &new);
+ amd_iommu_free_dev_data(iommu, trans_dev_data);
+}
+#endif /* CONFIG_AMD_IOMMU_IOMMUFD */
+
const struct iommu_ops amd_iommu_ops = {
.capable = amd_iommu_capable,
.hw_info = amd_iommufd_hw_info,
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 21/22] iommu/amd: Add translation DTE and VFctrl TransDevID helpers
2026-06-29 15:35 ` [PATCH v3 21/22] iommu/amd: Add translation DTE and VFctrl TransDevID helpers Suravee Suthikulpanit
@ 2026-07-07 14:43 ` Jason Gunthorpe
0 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:43 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:34PM +0000, Suravee Suthikulpanit wrote:
> The hardware vIOMMU uses a per-VM translate device ID (TransDevID)
> to
per-viommu, the iommu drivers don't work with the concept of VM.
> index the host device table when guest IOMMU traffic needs GPA->SPA
> translation. The VF Control guest miscellaneous register tells the
> IOMMU which TransDevID to use; the corresponding device table entry
> (DTE) points at the nested IOMMU v1 page table that performs the walk
> from guest physical to system physical addresses.
A v1 page table is a "parent" not a nested
> @@ -213,7 +213,12 @@ void amd_iommu_update_dte(struct amd_iommu *iommu,
> struct dev_table_entry *new)
> {
> update_dte256(iommu, dev_data, new);
> - clone_aliases(iommu, dev_data->dev);
> + /*
> + * The dev_data for trans_devid does not have struct dev.
> + * So clone_aliases is not supported for translate-device-id.
> + */
> + if (dev_data->dev)
> + clone_aliases(iommu, dev_data->dev);
More junk from the ugly alias scheme :\ Maybe you should fix it
> +int amd_iommu_set_translate_dte(struct amd_iommu *iommu,
> + struct protection_domain *pdom,
> + u16 gid, u32 trans_devid)
> +{
This should probably accept a viommu struct instead of all these arguments.
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v3 22/22] iommu/amd: Assign per-vIOMMU translate device ID
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (20 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 21/22] iommu/amd: Add translation DTE and VFctrl TransDevID helpers Suravee Suthikulpanit
@ 2026-06-29 15:35 ` Suravee Suthikulpanit
2026-07-07 14:45 ` [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Jason Gunthorpe
22 siblings, 0 replies; 33+ messages in thread
From: Suravee Suthikulpanit @ 2026-06-29 15:35 UTC (permalink / raw)
To: linux-kernel, iommu, joro, jgg
Cc: yi.l.liu, kevin.tian, nicolinc, vasant.hegde, jon.grimm,
santosh.shukla, sairaj.arunkodilkar, jay.chen, wvw, wnliu,
dantuluris, chriscli, kpsingh, Suravee Suthikulpanit
Allocate one translate-device-id per IOMMUFD vIOMMU instance from
the per-segment pool. Program translation DTE and VFctrl TransDevID
on init; clear both on error and destroy.
Each vIOMMU owns its trans_devid independently.
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 2 +
drivers/iommu/amd/amd_iommu_types.h | 1 +
drivers/iommu/amd/iommufd.c | 25 +++++++++++++
drivers/iommu/amd/trans_devid.c | 58 +++++++++++++++++++++++++++++
drivers/iommu/amd/viommu.c | 2 +
5 files changed, 88 insertions(+)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 7300f07e7a39..f0293d636718 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -225,6 +225,8 @@ void amd_iommu_pci_seg_trans_devid_fini(struct amd_iommu_pci_seg *pci_seg);
int amd_iommu_trans_devid_reserve(struct amd_iommu_pci_seg *pci_seg, u16 id);
int amd_iommu_trans_devid_reserve_pci_aliases(struct amd_iommu *iommu,
struct device *dev);
+int amd_iommu_trans_devid_alloc(struct amd_iommu_pci_seg *pci_seg);
+void amd_iommu_trans_devid_free(struct amd_iommu_pci_seg *pci_seg, u16 id);
#else
static inline void
amd_iommu_pci_seg_trans_devid_init(struct amd_iommu_pci_seg *pci_seg) { }
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index cd10e33c1317..1090d7796ce5 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -557,6 +557,7 @@ struct amd_iommu_viommu {
u64 *devid_table;
u64 *domid_table;
+ u16 trans_devid;
/* Offset for mmap() of guest VF MMIO; set after iommufd_viommu_alloc_mmap(). */
unsigned long vfmmio_mmap_offset;
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index 8b569b469a07..0d80eaeee662 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -48,6 +48,9 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
int ret;
phys_addr_t page_base;
unsigned long flags;
+ u16 trans_devid;
+ bool trans_devid_allocated = false;
+ bool trans_dte_set = false;
struct iommu_viommu_amd data = {};
struct protection_domain *pdom = to_pdomain(parent);
struct amd_iommu *iommu = container_of(viommu->iommu_dev, struct amd_iommu, iommu);
@@ -81,9 +84,22 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
data.out_vfmmio_mmap_offset = aviommu->vfmmio_mmap_offset;
+ ret = amd_iommu_trans_devid_alloc(iommu->pci_seg);
+ if (ret < 0)
+ goto err_trans_devid;
+ trans_devid = ret;
+ trans_devid_allocated = true;
+ aviommu->trans_devid = trans_devid;
+
/* Reset vIOMMU MMIOs to initialize the vIOMMU */
iommu_reset_vmmio(iommu, aviommu->gid);
+ ret = amd_iommu_set_translate_dte(iommu, pdom, aviommu->gid, trans_devid);
+ if (ret)
+ goto err_init;
+ trans_dte_set = true;
+ amd_iommu_update_vfctrl_mmio_translate_devid(iommu, aviommu->gid, trans_devid);
+
ret = amd_viommu_init_one(iommu, aviommu);
if (ret)
goto err_init;
@@ -102,6 +118,13 @@ int amd_iommufd_viommu_init(struct iommufd_viommu *viommu, struct iommu_domain *
return 0;
err_init:
+ if (trans_dte_set) {
+ amd_iommu_update_vfctrl_mmio_translate_devid(iommu, aviommu->gid, 0);
+ amd_iommu_clear_translate_dte(iommu, trans_devid);
+ }
+ if (trans_devid_allocated)
+ amd_iommu_trans_devid_free(iommu->pci_seg, trans_devid);
+err_trans_devid:
iommufd_viommu_destroy_mmap(&aviommu->core, aviommu->vfmmio_mmap_offset);
err_mmap:
amd_iommu_gid_free(iommu, aviommu->gid);
@@ -124,6 +147,8 @@ static void amd_iommufd_viommu_destroy(struct iommufd_viommu *viommu)
xa_destroy(&aviommu->gdomid_array);
iommufd_viommu_destroy_mmap(&aviommu->core, aviommu->vfmmio_mmap_offset);
amd_viommu_uninit_one(iommu, aviommu);
+ amd_iommu_clear_translate_dte(iommu, aviommu->trans_devid);
+ amd_iommu_trans_devid_free(iommu->pci_seg, aviommu->trans_devid);
amd_iommu_gid_free(iommu, aviommu->gid);
}
diff --git a/drivers/iommu/amd/trans_devid.c b/drivers/iommu/amd/trans_devid.c
index 7c57087bfa10..6dd291d63607 100644
--- a/drivers/iommu/amd/trans_devid.c
+++ b/drivers/iommu/amd/trans_devid.c
@@ -126,3 +126,61 @@ int amd_iommu_trans_devid_reserve_pci_aliases(struct amd_iommu *iommu,
return pci_for_each_dma_alias(pdev, reserve_trans_devid_each_dma_alias,
pci_seg);
}
+
+/**
+ * amd_iommu_trans_devid_alloc - allocate a translate-device-id for @pci_seg
+ *
+ * The trans_devid is allocated from the highest id to the lowest id.
+ * Generally, the PCI devices enumerated from the beginning of the bus range.
+ * Therefore, ids in the high range are likely to not be used.
+ *
+ * Each vIOMMU receives its own translate-device-id from the per-segment pool.
+ *
+ * Return: allocated id on success, negative errno on failure.
+ */
+int amd_iommu_trans_devid_alloc(struct amd_iommu_pci_seg *pci_seg)
+{
+ int id;
+
+ mutex_lock(&pci_seg->trans_devid_mutex);
+ for (id = U16_MAX; id >= 0; id--) {
+ void *entry, *old;
+
+ entry = xa_load(&pci_seg->trans_devid_xa, id);
+ if (entry)
+ continue;
+
+ old = xa_store(&pci_seg->trans_devid_xa, id,
+ trans_devid_xa_mk_state(TRANS_DEVID_ALLOCATED), GFP_KERNEL);
+ if (xa_is_err(old)) {
+ int err = xa_err(old);
+
+ mutex_unlock(&pci_seg->trans_devid_mutex);
+ return err;
+ }
+ WARN_ON_ONCE(old);
+ mutex_unlock(&pci_seg->trans_devid_mutex);
+ pr_debug("%s: Allocated trans_devid %#x (seg %#x)\n", __func__, id,
+ pci_seg->id);
+ return id;
+ }
+ pr_err("%s: No free trans_devid found (seg %#x)\n", __func__, pci_seg->id);
+ mutex_unlock(&pci_seg->trans_devid_mutex);
+ return -ENOSPC;
+}
+
+/**
+ * amd_iommu_trans_devid_free - return @id to the per-segment pool
+ */
+void amd_iommu_trans_devid_free(struct amd_iommu_pci_seg *pci_seg, u16 id)
+{
+ void *old;
+
+ mutex_lock(&pci_seg->trans_devid_mutex);
+ old = xa_erase(&pci_seg->trans_devid_xa, id);
+ if (WARN_ON_ONCE(!old || trans_devid_xa_get_state(old) == TRANS_DEVID_FREE))
+ goto out;
+ pr_debug("%s: Freed trans_devid %#x (seg %#x)\n", __func__, id, pci_seg->id);
+out:
+ mutex_unlock(&pci_seg->trans_devid_mutex);
+}
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index 7b3127d829c5..e38d919dcec3 100644
--- a/drivers/iommu/amd/viommu.c
+++ b/drivers/iommu/amd/viommu.c
@@ -509,6 +509,8 @@ void amd_viommu_uninit_one(struct amd_iommu *iommu, struct amd_iommu_viommu *avi
VIOMMU_DOMID_MAPPING_BASE,
VIOMMU_DOMID_MAPPING_ENTRY_SIZE,
aviommu->gid);
+
+ amd_iommu_update_vfctrl_mmio_translate_devid(iommu, aviommu->gid, 0);
viommu_clear_mapping(iommu, aviommu);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support
2026-06-29 15:35 [PATCH v3 00/22] iommu/amd: Introduce AMD Hardware-accelerated Virtualized IOMMU (vIOMMU) Support Suravee Suthikulpanit
` (21 preceding siblings ...)
2026-06-29 15:35 ` [PATCH v3 22/22] iommu/amd: Assign per-vIOMMU translate device ID Suravee Suthikulpanit
@ 2026-07-07 14:45 ` Jason Gunthorpe
22 siblings, 0 replies; 33+ messages in thread
From: Jason Gunthorpe @ 2026-07-07 14:45 UTC (permalink / raw)
To: Suravee Suthikulpanit
Cc: linux-kernel, iommu, joro, yi.l.liu, kevin.tian, nicolinc,
vasant.hegde, jon.grimm, santosh.shukla, sairaj.arunkodilkar,
jay.chen, wvw, wnliu, dantuluris, chriscli, kpsingh
On Mon, Jun 29, 2026 at 03:35:13PM +0000, Suravee Suthikulpanit wrote:
> The series is organized into the following subsets:
>
> Patch 1-3 : Preparatory patches
> Patch 4-8 : Introduce IOMMUFD vIOMMU support and VF MMIO setup
> Patch 9-14 : Introduce and map vIOMMU Private Address (IPA) region
> Patch 15-18 : Introduce IOMMUFD vDevice support for AMD
> Patch 19-22 : Translate-device-ID pool and per-vIOMMU translation DTE
I thought this is pretty good looking now, it uses the APIs as I would
expect other than a few small notes
Jason
^ permalink raw reply [flat|nested] 33+ messages in thread