[PATCH 15/22] iommu/amd: Introduce helper function for updating domain ID mapping table

Suravee Suthikulpanit
2026-03-30T08:43:57Z

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 | 42 ++++++++++++++++++++++++++++++++++
3 files changed, 49 insertions(+)

diff --git a/drivers/iommu/amd/amd_viommu.h b/drivers/iommu/amd/amd_viommu.h
index ccdd1cbcec36..e1387d9ea97d 100644
--- a/drivers/iommu/amd/amd_viommu.h
+++ b/drivers/iommu/amd/amd_viommu.h
@@ -16,6 +16,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 70af39da9360..f38813394a25 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 53cf6ab2db04..80873bd62c52 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)
@@ -350,6 +352,22 @@ static void free_private_vm_region(struct amd_iommu *iommu, u64 **entry,
*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);
@@ -362,6 +380,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)
@@ -382,8 +401,31 @@ 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.
+ */
+int amd_viommu_domain_id_update(struct amd_iommu *iommu, u16 gid,
+ u16 hdom_id, u16 gdom_id)
+{
+ u64 val, tmp1, tmp2;
+ u8 __iomem *vfctrl = VIOMMU_VFCTRL_MMIO_BASE(iommu, gid);
+
+ tmp1 = gdom_id;
+ tmp1 = ((tmp1 & 0xFFFFULL) << 46);
+ tmp2 = hdom_id;
+ tmp2 = ((tmp2 & 0xFFFFULL) << 14);
+ val = tmp1 | tmp2 | 0x8000000000000001UL;
+ writeq(val, vfctrl + VIOMMU_VFCTRL_GUEST_DID_MAP_CONTROL1_OFFSET);
+
+ return 0;
+}
+EXPORT_SYMBOL(amd_viommu_domain_id_update);
--
2.34.1