[PATCH 06/22] iommu/amd: Map vIOMMU VF MMIO and VF Control MMIO regions

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

To enable the vIOMMU feature, the AMD IOMMU driver needs to map
vIOMMU VF MMIO and VF Control MMIO regions using information in
the PCI vendor-specific capability (VSC).

The VF Control MMIO region represents the 1st 4K of guest IOMMU mmio
region, which contains IOMMU control registers, and will be trapped
and handled by QEMU.

The VF MMIO region represents the 3rd 4K of guest IOMMU mmio region,
which will be virtualized by the IOMMU hardware.

Signed-off-by: Vasant Hegde <vasant.hegde@amd.com>
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/amd_iommu.h | 1 +
drivers/iommu/amd/amd_iommu_types.h | 29 ++++++++++
drivers/iommu/amd/init.c | 2 +-
drivers/iommu/amd/viommu.c | 83 +++++++++++++++++++++++++++++
4 files changed, 114 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 51a7c3b3329f..470e5d98c52b 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -26,6 +26,7 @@ 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);

#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 0b4d713d3337..f0e18a7dd7f2 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -38,6 +38,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
@@ -472,6 +478,16 @@ 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
+
+#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;
@@ -685,6 +701,19 @@ 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;
+
+ /* 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/init.c b/drivers/iommu/amd/init.c
index a1cc6aa6f7dc..0018ae804ab4 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -478,7 +478,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",
diff --git a/drivers/iommu/amd/viommu.c b/drivers/iommu/amd/viommu.c
index f4b5f96d4785..887a9eb8122d 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,89 @@
#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 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, 0x10000000);
+ if (!iommu->vf_base) {
+ pr_err("Can't reserve vf_base\n");
+ return -ENOMEM;
+ }
+
+ vf_cntl_phys &= ~1ULL;
+ iommu->vfctrl_base = iommu_map_mmio_space(vf_cntl_phys, 0x400000);
+
+ if (!iommu->vfctrl_base) {
+ pr_err("Can't reserve vfctrl_base\n");
+ return -ENOMEM;
+ }
+
+ /* Track VF MMIO base addess */
+ iommu->vf_base_phys = vf_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;
+}

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;
+
return 0;
}
--
2.34.1