[PATCH v2 15/26] iommu/amd: Introduce helper function for updating domain ID mapping table
From: Suravee Suthikulpanit
Date: Thu May 28 2026 - 01:21:13 EST
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@xxxxxxx>
---
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 08f74ebb3523..ce16c8404c28 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 3636093732ce..3f7c17d400d0 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)
@@ -367,6 +369,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);
@@ -379,6 +397,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)
@@ -399,8 +418,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