[PATCH v2 25/26] iommu/amd: Handle set/get command for AMD vIOMMU

From: Suravee Suthikulpanit

Date: Thu May 28 2026 - 01:25:08 EST


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_command() and get_command().

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@xxxxxxx>
---
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 | 146 ++++++++++++++++++++++++++++
5 files changed, 169 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 12c3fe83e4ce..afbefda87f57 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 trans_devid.o
+obj-$(CONFIG_AMD_IOMMU_IOMMUFD) += iommufd.o nested.o viommu.o trans_devid.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 70d060a85e66..44a185bfa39e 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -191,10 +191,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 3a8f41baaab9..c4ef374b68ec 100644
--- a/drivers/iommu/amd/amd_viommu.h
+++ b/drivers/iommu/amd/amd_viommu.h
@@ -23,6 +23,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)
@@ -53,6 +58,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 7e6381e5a06b..53dda68c5a0a 100644
--- a/drivers/iommu/amd/iommufd.c
+++ b/drivers/iommu/amd/iommufd.c
@@ -317,4 +317,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_command = amd_viommu_guest_mmio_write,
+ .get_command = 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..ece94f2212ec
--- /dev/null
+++ b/drivers/iommu/amd/vfctrl_mmio.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Advanced Micro Devices, Inc.
+ * Author: Suravee Suthikulpanit <suravee.suthikulpanit@xxxxxxx>
+ */
+
+#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:
+ pr_err("%s: invalid offset=%#x\n", __func__, offset);
+ 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