[PATCH v2 18/26] iommu/amd: Add translation DTE and VFctrl TransDevID helpers
From: Suravee Suthikulpanit
Date: Thu May 28 2026 - 01:22:17 EST
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, using the
AMDv1 page table root and mode from the protection domain. 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@xxxxxxx>
---
drivers/iommu/amd/amd_iommu.h | 7 ++++
drivers/iommu/amd/amd_iommu_types.h | 1 +
drivers/iommu/amd/iommu.c | 51 +++++++++++++++++++++++++++++
3 files changed, 59 insertions(+)
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 1d8727c16840..d1640181b292 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -216,6 +216,13 @@ void amd_iommu_update_dte(struct amd_iommu *iommu,
struct dev_table_entry *new);
int amd_iommu_completion_wait(struct amd_iommu *iommu);
+void amd_iommu_set_translate_dte(struct amd_iommu *iommu, u16 gid,
+ struct protection_domain *pdom,
+ u32 devid);
+void amd_iommu_clear_translate_dte(struct amd_iommu *iommu, u16 gid, u32 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 iommu_dev_data *dev_data, struct dev_table_entry *new)
{
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index e9b49f0b9051..2d7bc791dbd9 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 44d20e598d85..6c4c4f62ddde 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -3242,6 +3242,57 @@ 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);
+}
+
+void amd_iommu_set_translate_dte(struct amd_iommu *iommu, u16 gid,
+ struct protection_domain *pdom,
+ u32 devid)
+{
+ u64 tmp0 = 0ULL, tmp1 = 0ULL;
+ struct pt_iommu_amdv1_hw_info pt_info;
+ struct dev_table_entry *dev_table = get_dev_table(iommu);
+
+ 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, devid, pt_info.host_pt_root, pt_info.mode);
+
+ /* Setup DTE for v1 page table at the offset specified by devid */
+ tmp0 |= FIELD_PREP(DTE_HOST_TRP, pt_info.host_pt_root >> 12);
+ tmp0 |= FIELD_PREP(DTE_MODE_MASK, pt_info.mode);
+ tmp0 |= (DTE_FLAG_IR | DTE_FLAG_IW | DTE_FLAG_TV | DTE_FLAG_V);
+ tmp1 |= FIELD_PREP(DTE_DOMID_MASK, pdom->id);
+
+ dev_table[devid].data[0] = tmp0;
+ dev_table[devid].data[1] = tmp1;
+
+ iommu_flush_dte(iommu, devid);
+ amd_iommu_completion_wait(iommu);
+}
+
+void amd_iommu_clear_translate_dte(struct amd_iommu *iommu, u16 gid, u32 devid)
+{
+ struct dev_table_entry *dev_table = get_dev_table(iommu);
+
+ pr_debug("%s: gid=%#x, iommu_devid=%#x, devid=%#x\n",
+ __func__, gid, iommu->devid, devid);
+
+ dev_table[devid].data[0] = 0ULL;
+ dev_table[devid].data[1] = 0ULL;
+
+ iommu_flush_dte(iommu, devid);
+ amd_iommu_completion_wait(iommu);
+}
+#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