summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCostas Argyris <costas.argyris@amd.com>2026-05-30 19:34:51 +0100
committerMichael Tokarev <mjt@tls.msk.ru>2026-06-16 17:45:53 +0300
commit36d13850196bb88fa936c4e562e6224cca4fe51b (patch)
tree8e2a0d9196fc7522c16b3c1bd121751918d5284a
parent7d07b86a62e3bd61b3add23b9d24296b0e266598 (diff)
downloadqemu-36d13850196bb88fa936c4e562e6224cca4fe51b.tar.gz
qemu-36d13850196bb88fa936c4e562e6224cca4fe51b.zip
tests/qtest: Add amd-iommu command buffer head wrap test
Add a qtest for AMD IOMMU command buffer head pointer wraparound. The test programs a command buffer, fills it with COMPLETION_WAIT commands, advances the tail to consume all but the final entry, then wraps the tail to zero to force the final command to advance CmdHeadPtr past the end of the buffer. The guest-visible CmdHeadPtr register must then wrap back to zero. This covers the case fixed by an earlier CmdHeadPtr wraparound patch. The Linux kernel AMD IOMMU driver is not affected by this bug because it uses COMPLETION_WAIT with a memory store doorbell to detect command progress. Signed-off-by: Costas Argyris <costas.argyris@amd.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-Id: <20260530183458.7778-1-costas.argyris@amd.com> (cherry picked from commit 8a1c09cbd3fe776a467398700c610cd126ee8a59) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
-rw-r--r--MAINTAINERS1
-rw-r--r--tests/qtest/amd-iommu-test.c76
-rw-r--r--tests/qtest/meson.build1
3 files changed, 78 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 8cc89d4feb..296a9c7697 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4026,6 +4026,7 @@ M: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
R: Sairaj Kodilkar <sarunkod@amd.com>
S: Supported
F: hw/i386/amd_iommu*
+F: tests/qtest/amd-iommu-test.c
OpenSBI Firmware
L: qemu-riscv@nongnu.org
diff --git a/tests/qtest/amd-iommu-test.c b/tests/qtest/amd-iommu-test.c
new file mode 100644
index 0000000000..fb28511588
--- /dev/null
+++ b/tests/qtest/amd-iommu-test.c
@@ -0,0 +1,76 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "hw/i386/amd_iommu.h"
+
+#define CMDBUF_ADDR 0x200000
+#define CMDBUF_LEN_FIELD 8
+#define CMDBUF_ENTRIES (1U << CMDBUF_LEN_FIELD)
+
+static inline uint64_t amdvi_reg_readq(QTestState *s, uint64_t offset)
+{
+ return qtest_readq(s, AMDVI_BASE_ADDR + offset);
+}
+
+static inline void amdvi_reg_writeq(QTestState *s, uint64_t offset,
+ uint64_t val)
+{
+ qtest_writeq(s, AMDVI_BASE_ADDR + offset, val);
+}
+
+static void test_cmdbuf_head_wrap(void)
+{
+ QTestState *s;
+ uint64_t head;
+ int i;
+ /* 16 bytes per command */
+ struct {
+ uint64_t qw0;
+ uint64_t qw1;
+ } cmdbuf[CMDBUF_ENTRIES];
+
+ if (!qtest_has_machine("q35")) {
+ g_test_skip("q35 machine not available");
+ return;
+ }
+
+ s = qtest_init("-M q35 -device amd-iommu");
+
+ /* fill the command buffer with COMPLETION_WAIT (no-op) commands */
+ for (i = 0; i < CMDBUF_ENTRIES; i++) {
+ cmdbuf[i].qw0 = (uint64_t)AMDVI_CMD_COMPLETION_WAIT << 60;
+ cmdbuf[i].qw1 = 0;
+ }
+ qtest_memwrite(s, CMDBUF_ADDR, cmdbuf, sizeof(cmdbuf));
+
+ /* point the IOMMU at the command buffer and set its length */
+ amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_BASE,
+ CMDBUF_ADDR | ((uint64_t)CMDBUF_LEN_FIELD << 56));
+
+ /* enable the IOMMU and its command buffer processor */
+ amdvi_reg_writeq(s, AMDVI_MMIO_CONTROL,
+ AMDVI_MMIO_CONTROL_AMDVIEN | AMDVI_MMIO_CONTROL_CMDBUFLEN);
+
+ /* advance tail to the last entry, consuming all but the final entry */
+ amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_TAIL,
+ (CMDBUF_ENTRIES - 1) * AMDVI_COMMAND_SIZE);
+
+ /* wrap tail to 0, consuming the final entry and completing the buffer */
+ amdvi_reg_writeq(s, AMDVI_MMIO_COMMAND_TAIL, 0);
+
+ /* after consuming all entries the IOMMU must wrap CmdHeadPtr to 0 */
+ head = amdvi_reg_readq(s, AMDVI_MMIO_COMMAND_HEAD);
+ g_assert((head & AMDVI_MMIO_CMDBUF_HEAD_MASK) == 0);
+
+ qtest_quit(s);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_func("/q35/amd-iommu/cmdbuf-head-wrap", test_cmdbuf_head_wrap);
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index be4fa627b5..12ad4c0ad6 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -95,6 +95,7 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \
(config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \
(config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_AMD_IOMMU') ? ['amd-iommu-test'] : []) + \
(config_all_devices.has_key('CONFIG_VTD') ? ['intel-iommu-test'] : []) + \
(host_os != 'windows' and \
config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \