Add a qtest suite that validates ARM SMMUv3 translation without guest
firmware or OS. The tests leverage iommu-testdev to trigger DMA
operations and the qos-smmuv3 library to configure IOMMU translation
structures.

Test coverage includes:
- Stage 1 only translation (VA -> PA via CD page tables)
- Stage 2 only translation (IPA -> PA via STE S2 tables)
- Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)
- Could be easily extended to support multiple security spaces
  (Non-Secure, Secure, Root, Realm)

Each test:
1. Initializes SMMUv3 with appropriate command/event queues
2. Builds translation tables (STE/CD/PTE) for the target scenario
3. Configures iommu-testdev with IOVA and DMA attributes
4. Triggers DMA and validates successful translation
5. Verifies data integrity through the write-read pattern

This bare-metal approach provides deterministic IOMMU testing with
minimal dependencies, making failures directly attributable to the SMMU
translation path. The test infrastructure can be easily extended to
cover additional scenarios like fault handling, permission checks, and
invalidation commands.

Signed-off-by: Tao Tang <[email protected]>
---
 tests/qtest/iommu-smmuv3-test.c | 116 ++++++++++++++++++++++++++++++++
 tests/qtest/meson.build         |   1 +
 2 files changed, 117 insertions(+)
 create mode 100644 tests/qtest/iommu-smmuv3-test.c

diff --git a/tests/qtest/iommu-smmuv3-test.c b/tests/qtest/iommu-smmuv3-test.c
new file mode 100644
index 0000000000..100fbdfe05
--- /dev/null
+++ b/tests/qtest/iommu-smmuv3-test.c
@@ -0,0 +1,116 @@
+/*
+ * QTest for SMMUv3 with iommu-testdev
+ *
+ * This QTest file is used to test the SMMUv3 with iommu-testdev so that we can
+ * test SMMUv3 without any guest kernel or firmware.
+ *
+ * Copyright (c) 2025 Phytium Technology
+ *
+ * Author:
+ *  Tao Tang <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/generic-pcihost.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/misc/iommu-testdev.h"
+#include "libqos/qos-smmuv3.h"
+
+#define DMA_LEN           4
+
+/* Test configurations for different SMMU modes and spaces */
+static const QSMMUTestConfig base_test_configs[] = {
+    {
+        .trans_mode = QSMMU_TM_S1_ONLY,
+        .sec_sid = QSMMU_SEC_SID_NONSECURE,
+        .dma_iova = QSMMU_IOVA_OR_IPA,
+        .dma_len = DMA_LEN,
+        .expected_result = 0
+    },
+    {
+        .trans_mode = QSMMU_TM_S2_ONLY,
+        .sec_sid = QSMMU_SEC_SID_NONSECURE,
+        .dma_iova = QSMMU_IOVA_OR_IPA,
+        .dma_len = DMA_LEN,
+        .expected_result = 0
+    },
+    {
+        .trans_mode = QSMMU_TM_NESTED,
+        .sec_sid = QSMMU_SEC_SID_NONSECURE,
+        .dma_iova = QSMMU_IOVA_OR_IPA,
+        .dma_len = DMA_LEN,
+        .expected_result = 0
+    }
+};
+
+static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QGenericPCIBus 
*gbus,
+                                          QPCIBar *bar)
+{
+    int slot, fn;
+    uint16_t vid, did;
+    QPCIDevice *dev = NULL;
+
+    qpci_init_generic(gbus, qts, NULL, false);
+
+    /* Find device by vendor/device ID to avoid slot surprises. */
+    for (slot = 0; slot < 32 && !dev; slot++) {
+        for (fn = 0; fn < 8 && !dev; fn++) {
+            QPCIDevice *cand = qpci_device_find(&gbus->bus,
+                                                QPCI_DEVFN(slot, fn));
+            if (!cand) {
+                continue;
+            }
+            vid = qpci_config_readw(cand, PCI_VENDOR_ID);
+            did = qpci_config_readw(cand, PCI_DEVICE_ID);
+            if (vid == IOMMU_TESTDEV_VENDOR_ID &&
+                did == IOMMU_TESTDEV_DEVICE_ID) {
+                dev = cand;
+                g_test_message("Found iommu-testdev! devfn: 0x%x", 
cand->devfn);
+            } else {
+                g_free(cand);
+            }
+        }
+    }
+    g_assert(dev);
+
+    qpci_device_enable(dev);
+    *bar = qpci_iomap(dev, 0, NULL);
+    g_assert_false(bar->is_io);
+
+    return dev;
+}
+
+static void test_smmuv3_translation(void)
+{
+    QTestState *qts;
+    QGenericPCIBus gbus;
+    QPCIDevice *dev;
+    QPCIBar bar;
+
+    /* Initialize QEMU environment for SMMU testing */
+    qts = qtest_init("-machine virt,acpi=off,gic-version=3,iommu=smmuv3 "
+                     "-smp 1 -m 512 -cpu max -net none "
+                     "-device iommu-testdev");
+
+    /* Setup and configure PCI device */
+    dev = setup_qtest_pci_device(qts, &gbus, &bar);
+    g_assert(dev);
+
+    /* Run the enhanced translation tests */
+    g_test_message("### Starting SMMUv3 translation tests...###");
+    qsmmu_translation_batch(base_test_configs, ARRAY_SIZE(base_test_configs),
+                            qts, dev, bar, VIRT_SMMU_BASE);
+    g_test_message("### SMMUv3 translation tests completed successfully! ###");
+    qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+    qtest_add_func("/iommu-testdev/translation", test_smmuv3_translation);
+    return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 669d07c06b..e2d2e68092 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -263,6 +263,7 @@ qtests_aarch64 = \
    config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : 
[]) + \
   (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \
   (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \
+  (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-smmuv3-test'] 
: []) + \
   qtests_cxl +                                                                 
                 \
   ['arm-cpu-features',
    'numa-test',
-- 
2.34.1


Reply via email to