[PATCH 21/22] iommu/amd: Handle set/get option for AMD vIOMMU hw_queue

Suravee Suthikulpanit
2026-03-30T08:44:33Z

Guest kernel programs guest Command Buffer, Event Log, and PPR Log
(a.k.a hardware queue) settings via guest control MMIO register
(guest MMIO offset 0x18). Accesses to the register is trapped by VMM (QEMU)
and information is passed as IOMMU_VIOMMU_OPTION to host IOMMU driver via
struct iommufd_viommu_ops set_option() and get_option().

Provides AMD IOMMU driver hooks to handle set/get operations for the
guest control MMIO register, which uses key parameter as AMD IOMMU MMIO
offset. The value parameter contains the value of the corresponding guest
MMIO register, which is converted to the format of AMD vIOMMU VF Control
MMIO registers then programed onto the hardware.

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/amd/Makefile | 2 +-
drivers/iommu/amd/amd_iommu_types.h | 5 +
drivers/iommu/amd/amd_viommu.h | 15 +++
drivers/iommu/amd/iommufd.c | 2 +
drivers/iommu/amd/vfctrl_mmio.c | 145 ++++++++++++++++++++++++++++
5 files changed, 168 insertions(+), 1 deletion(-)
create mode 100644 drivers/iommu/amd/vfctrl_mmio.c

diff --git a/drivers/iommu/amd/Makefile b/drivers/iommu/amd/Makefile
index e1e824b9c7b0..a5049fd7f789 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 vfctrl_mmio.o
obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += debugfs.o
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index 1cd50a4008fb..ff72023fa512 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -194,10 +194,15 @@
#define CONTROL_GAM_EN 25
#define CONTROL_GALOG_EN 28
#define CONTROL_GAINT_EN 29
+#define CONTROL_DUALPPRLOG_EN 30
+#define CONTROL_DUALEVTLOG_EN 32
+#define CONTROL_PPR_AUTO_RSP_EN 39
+#define CONTROL_BLKSTOPMRK_EN 41
#define CONTROL_NUM_INT_REMAP_MODE 43
#define CONTROL_NUM_INT_REMAP_MODE_MASK 0x03
#define CONTROL_NUM_INT_REMAP_MODE_2K 0x01
#define CONTROL_EPH_EN 45
+#define CONTROL_PPR_AUTO_RSP_AON 48
#define CONTROL_XT_EN 50
#define CONTROL_INTCAPXT_EN 51
#define CONTROL_GCR3TRPMODE 58
diff --git a/drivers/iommu/amd/amd_viommu.h b/drivers/iommu/amd/amd_viommu.h
index 93e44f8b6012..bd4e7481ee0c 100644
--- a/drivers/iommu/amd/amd_viommu.h
+++ b/drivers/iommu/amd/amd_viommu.h
@@ -21,6 +21,11 @@ int amd_viommu_domain_id_update(struct amd_iommu *iommu, u16 gid,

void amd_viommu_set_device_mapping(struct amd_iommu *iommu, u16 hDevId,
u16 guestId, u16 gDevId);
+
+int amd_viommu_guest_mmio_write(struct iommufd_viommu *viommu, u16 offset, u64 value);
+
+int amd_viommu_guest_mmio_read(struct iommufd_viommu *viommu, u16 offset, u64 *value);
+
#else

static inline int amd_viommu_init(struct amd_iommu *iommu)
@@ -47,6 +52,16 @@ static inline void amd_viommu_set_device_mapping(struct amd_iommu *iommu, u16 hD
{
}

+static inline int amd_viommu_guest_mmio_write(struct iommufd_viommu *viommu, u16 offset, u64 value)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int amd_viommu_guest_mmio_read(struct iommufd_viommu *viommu, u16 offset, u64 *value)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* CONFIG_AMD_IOMMU_IOMMUFD */

#endif /* AMD_VIOMMU_H */
diff --git a/drivers/iommu/amd/iommufd.c b/drivers/iommu/amd/iommufd.c
index 6f766254a390..b10cbff02288 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -242,4 +242,6 @@ static const struct iommufd_viommu_ops amd_viommu_ops = {
.vdevice_init = _amd_viommu_vdevice_init,
.get_hw_queue_size = _amd_viommu_get_hw_queue_size,
.hw_queue_init = _amd_viommu_hw_queue_init,
+ .set_option = amd_viommu_guest_mmio_write,
+ .get_option = amd_viommu_guest_mmio_read,
};
diff --git a/drivers/iommu/amd/vfctrl_mmio.c b/drivers/iommu/amd/vfctrl_mmio.c
new file mode 100644
index 000000000000..1dcccc24e847
--- /dev/null
+++ b/drivers/iommu/amd/vfctrl_mmio.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Advanced Micro Devices, Inc.
+ * Author: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
+ */
+
+#define pr_fmt(fmt) "AMD-Vi: " fmt
+#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 <uapi/linux/iommufd.h>
+#include <linux/mem_encrypt.h>
+
+#include <asm/iommu.h>
+#include <asm/set_memory.h>
+
+#include "amd_iommu.h"
+#include "amd_iommu_types.h"
+#include "amd_viommu.h"
+#include "../iommu-pages.h"
+
+#define GET_CTRL_BITS(reg, bit, msk) (((reg) >> (bit)) & (ULL(msk)))
+#define SET_CTRL_BITS(reg, bit1, bit2, msk) \
+ ((((reg) >> (bit1)) & (ULL(msk))) << (bit2))
+
+int amd_viommu_guest_mmio_read(struct iommufd_viommu *viommu, u16 offset, u64 *value)
+{
+ u8 __iomem *vfctrl, *vf;
+ u64 val, tmp = 0;
+ 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);
+ int gid = aviommu->gid;
+
+ vf = VIOMMU_VF_MMIO_BASE(iommu, gid);
+ vfctrl = VIOMMU_VFCTRL_MMIO_BASE(iommu, gid);
+
+ switch (offset) {
+ case MMIO_CONTROL_OFFSET:
+ {
+ /* VFCTRL offset 20h */
+ val = readq(vfctrl + 0x20);
+ tmp |= SET_CTRL_BITS(val, 8, CONTROL_CMDBUF_EN, 1); // [12]
+ tmp |= SET_CTRL_BITS(val, 9, CONTROL_COMWAIT_EN, 1); // [4]
+
+ /* VFCTRL offset 28h */
+ val = readq(vfctrl + 0x28);
+ tmp |= SET_CTRL_BITS(val, 8, CONTROL_EVT_LOG_EN, 1); // [2]
+ tmp |= SET_CTRL_BITS(val, 9, CONTROL_EVT_INT_EN, 1); // [3]
+ tmp |= SET_CTRL_BITS(val, 10, CONTROL_DUALEVTLOG_EN, 3); // [33:32]
+
+ /* VFCTRL offset 30h */
+ val = readq(vfctrl + 0x30);
+ tmp |= SET_CTRL_BITS(val, 8, CONTROL_PPRLOG_EN, 1); // [13]
+ tmp |= SET_CTRL_BITS(val, 9, CONTROL_PPRINT_EN, 1); // [14]
+ tmp |= SET_CTRL_BITS(val, 10, CONTROL_PPR_EN, 1); // [15]
+ tmp |= SET_CTRL_BITS(val, 11, CONTROL_DUALPPRLOG_EN, 3); // [31:30]
+ tmp |= SET_CTRL_BITS(val, 13, CONTROL_PPR_AUTO_RSP_EN, 1); // [39]
+ tmp |= SET_CTRL_BITS(val, 14, CONTROL_BLKSTOPMRK_EN, 1); // [41]
+ tmp |= SET_CTRL_BITS(val, 15, CONTROL_PPR_AUTO_RSP_AON, 1); // [42]
+
+ *value = tmp;
+ break;
+ }
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ pr_debug("%s: iommu_devid=%#x, gid=%u, offset=%#x, value=%#llx\n",
+ __func__, iommu->devid, gid, offset, *value);
+ return 0;
+}
+EXPORT_SYMBOL(amd_viommu_guest_mmio_read);
+
+int amd_viommu_guest_mmio_write(struct iommufd_viommu *viommu, u16 offset, u64 value)
+{
+ u8 __iomem *vfctrl, *vf;
+ u64 val, tmp, ctrl = value;
+ 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);
+ int gid = aviommu->gid;
+
+ pr_debug("%s: iommu_devid=%#x, gid=%u, offset=%#x, value=%#llx\n",
+ __func__, iommu->devid, gid, offset, value);
+
+ vf = VIOMMU_VF_MMIO_BASE(iommu, gid);
+ vfctrl = VIOMMU_VFCTRL_MMIO_BASE(iommu, gid);
+
+ switch (offset) {
+ case MMIO_CONTROL_OFFSET:
+ {
+ /* VFCTRL offset 20h */
+ val = readq(vfctrl + 0x20);
+ val &= ~(0x3ULL << 8);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_CMDBUF_EN, 1); // [12]
+ val |= (tmp << 8);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_COMWAIT_EN, 1); // [4]
+ val |= (tmp << 9);
+ writeq(val, vfctrl + 0x20);
+
+ /* VFCTRL offset 28h */
+ val = readq(vfctrl + 0x28);
+ val &= ~(0xFULL << 8);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_EVT_LOG_EN, 1); // [2]
+ val |= (tmp << 8);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_EVT_INT_EN, 1); // [3]
+ val |= (tmp << 9);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_DUALEVTLOG_EN, 3); // [33:32]
+ val |= (tmp << 10);
+ writeq(val, vfctrl + 0x28);
+
+ /* VFCTRL offset 30h */
+ val = readq(vfctrl + 0x30);
+ val &= ~(0xFFULL << 8);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_PPRLOG_EN, 1); // [13]
+ val |= (tmp << 8);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_PPRINT_EN, 1); // [14]
+ val |= (tmp << 9);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_PPR_EN, 1); // [15]
+ val |= (tmp << 10);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_DUALPPRLOG_EN, 3); // [31:30]
+ val |= (tmp << 11);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_PPR_AUTO_RSP_EN, 1); // [39]
+ val |= (tmp << 13);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_BLKSTOPMRK_EN, 1); // [41]
+ val |= (tmp << 14);
+ tmp = GET_CTRL_BITS(ctrl, CONTROL_PPR_AUTO_RSP_AON, 1); // [42]
+ val |= (tmp << 15);
+ writeq(val, vfctrl + 0x30);
+ break;
+ }
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(amd_viommu_guest_mmio_write);
--
2.34.1