@samitouri / QOSamiQemu / commits / f95eeea5c8

tests/qtest: Add Intel IOMMU bare-metal test

Add a qtest suite for the Intel IOMMU (VT-d) device on the Q35 machine. The test exercises both Legacy and Scalable translation modes using iommu-testdev and the qos-intel-iommu helpers, without requiring any guest kernel or firmware. The test validates: - Legacy-mode Root Entry Table and Context Entry Table configuration - Scalable-mode Context Entry, PASID Directory, and PASID Table setup - Legacy-mode 4-level page table walks for 48-bit address translation - Scalable-mode second-level and first-level 4-level page table walks - Pass-through mode in both Legacy and Scalable modes - DMA transaction execution with memory content verification Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Signed-off-by: Fengyuan Yu <15fengyuan@gmail.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Tao Tang <tangtao1634@phytium.com.cn> Link: https://lore.kernel.org/qemu-devel/ce3c44f3b07734a4f0ee43f55b21c856034af1b1.1774421649.git.15fengyuan@gmail.com Signed-off-by: Fabiano Rosas <farosas@suse.de>

Fengyuan Yu committed Mar 25, 2026 at 15:09 UTC f95eeea5c85bd197077e1ce3f5df61f95ea8ebe5
3 files changed +219
MAINTAINERS
+1
@@ -4010,6 +4010,7 @@ F: hw/i386/intel_iommu_accel.*
4010 F: include/hw/i386/intel_iommu.h
4011 F: tests/functional/x86_64/test_intel_iommu.py
4012 F: tests/qtest/intel-iommu-test.c
4013 +F: tests/qtest/iommu-intel-test.c
4014
4015 AMD-Vi Emulation
4016 M: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
tests/qtest/iommu-intel-test.c new
+216
@@ -0,0 +1,216 @@
1 +/*
2 + * QTest for Intel IOMMU (VT-d) with iommu-testdev
3 + *
4 + * This QTest file is used to test the Intel IOMMU with iommu-testdev so that
5 + * we can test VT-d without any guest kernel or firmware.
6 + *
7 + * Copyright (c) 2026 Fengyuan Yu <15fengyuan@gmail.com>
8 + *
9 + * SPDX-License-Identifier: GPL-2.0-or-later
10 + */
11 +
12 +#include "qemu/osdep.h"
13 +#include "libqtest.h"
14 +#include "libqos/pci.h"
15 +#include "libqos/pci-pc.h"
16 +#include "hw/i386/intel_iommu_internal.h"
17 +#include "hw/misc/iommu-testdev.h"
18 +#include "libqos/qos-intel-iommu.h"
19 +
20 +#define DMA_LEN 4
21 +
22 +static uint64_t intel_iommu_expected_gpa(uint64_t iova)
23 +{
24 + return (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (iova & 0xfff);
25 +}
26 +
27 +static void save_fn(QPCIDevice *dev, int devfn, void *data)
28 +{
29 + QPCIDevice **pdev = (QPCIDevice **) data;
30 +
31 + *pdev = dev;
32 +}
33 +
34 +static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus,
35 + QPCIBar *bar)
36 +{
37 + QPCIDevice *dev = NULL;
38 +
39 + *pcibus = qpci_new_pc(qts, NULL);
40 + g_assert(*pcibus != NULL);
41 +
42 + qpci_device_foreach(*pcibus, IOMMU_TESTDEV_VENDOR_ID,
43 + IOMMU_TESTDEV_DEVICE_ID, save_fn, &dev);
44 +
45 + g_assert(dev);
46 + qpci_device_enable(dev);
47 + *bar = qpci_iomap(dev, 0, NULL);
48 + g_assert_false(bar->is_io);
49 +
50 + return dev;
51 +}
52 +
53 +static const char *qvtd_iommu_args(QVTDTransMode mode)
54 +{
55 + switch (mode) {
56 + case QVTD_TM_SCALABLE_FLT:
57 + return "-device intel-iommu,x-scalable-mode=on,x-flts=on ";
58 + case QVTD_TM_SCALABLE_PT:
59 + case QVTD_TM_SCALABLE_SLT:
60 + return "-device intel-iommu,x-scalable-mode=on ";
61 + default:
62 + return "-device intel-iommu ";
63 + }
64 +}
65 +
66 +static bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode)
67 +{
68 + uint64_t ecap = qtest_readq(qts,
69 + Q35_HOST_BRIDGE_IOMMU_ADDR + DMAR_ECAP_REG);
70 +
71 + /* All scalable modes require SMTS */
72 + if (qvtd_is_scalable(mode) && !(ecap & VTD_ECAP_SMTS)) {
73 + g_test_skip("ECAP.SMTS not supported");
74 + return false;
75 + }
76 +
77 + switch (mode) {
78 + case QVTD_TM_SCALABLE_PT:
79 + if (!(ecap & VTD_ECAP_PT)) {
80 + g_test_skip("ECAP.PT not supported");
81 + return false;
82 + }
83 + break;
84 + case QVTD_TM_SCALABLE_SLT:
85 + if (!(ecap & VTD_ECAP_SSTS)) {
86 + g_test_skip("ECAP.SSTS not supported");
87 + return false;
88 + }
89 + break;
90 + case QVTD_TM_SCALABLE_FLT:
91 + if (!(ecap & VTD_ECAP_FSTS)) {
92 + g_test_skip("ECAP.FSTS not supported");
93 + return false;
94 + }
95 + break;
96 + default:
97 + break;
98 + }
99 +
100 + return true;
101 +}
102 +
103 +static void run_intel_iommu_translation(const QVTDTestConfig *cfg)
104 +{
105 + QTestState *qts;
106 + QPCIBus *pcibus;
107 + QPCIDevice *dev;
108 + QPCIBar bar;
109 +
110 + if (!qtest_has_machine("q35")) {
111 + g_test_skip("q35 machine not available");
112 + return;
113 + }
114 +
115 + /* Initialize QEMU environment for Intel IOMMU testing */
116 + qts = qtest_initf("-machine q35 -smp 1 -m 512 -net none "
117 + "%s -device iommu-testdev",
118 + qvtd_iommu_args(cfg->trans_mode));
119 +
120 + /* Check CAP/ECAP capabilities for required translation mode */
121 + if (!qvtd_check_caps(qts, cfg->trans_mode)) {
122 + qtest_quit(qts);
123 + return;
124 + }
125 +
126 + /* Setup and configure IOMMU-testdev PCI device */
127 + dev = setup_qtest_pci_device(qts, &pcibus, &bar);
128 + g_assert(dev);
129 +
130 + g_test_message("### Intel IOMMU translation mode=%d ###", cfg->trans_mode);
131 + qvtd_run_translation_case(qts, dev, bar, Q35_HOST_BRIDGE_IOMMU_ADDR, cfg);
132 + g_free(dev);
133 + qpci_free_pc(pcibus);
134 + qtest_quit(qts);
135 +}
136 +
137 +static void test_intel_iommu_legacy_pt(void)
138 +{
139 + QVTDTestConfig cfg = {
140 + .trans_mode = QVTD_TM_LEGACY_PT,
141 + .dma_gpa = QVTD_IOVA, /* pass-through: GPA == IOVA */
142 + .dma_len = DMA_LEN,
143 + .expected_result = 0,
144 + };
145 +
146 + run_intel_iommu_translation(&cfg);
147 +}
148 +
149 +static void test_intel_iommu_legacy_trans(void)
150 +{
151 + QVTDTestConfig cfg = {
152 + .trans_mode = QVTD_TM_LEGACY_TRANS,
153 + .dma_gpa = intel_iommu_expected_gpa(QVTD_IOVA),
154 + .dma_len = DMA_LEN,
155 + .expected_result = 0,
156 + };
157 +
158 + run_intel_iommu_translation(&cfg);
159 +}
160 +
161 +static void test_intel_iommu_scalable_pt(void)
162 +{
163 + QVTDTestConfig cfg = {
164 + .trans_mode = QVTD_TM_SCALABLE_PT,
165 + .dma_gpa = QVTD_IOVA, /* pass-through: GPA == IOVA */
166 + .dma_len = DMA_LEN,
167 + .expected_result = 0,
168 + };
169 +
170 + run_intel_iommu_translation(&cfg);
171 +}
172 +
173 +static void test_intel_iommu_scalable_slt(void)
174 +{
175 + QVTDTestConfig cfg = {
176 + .trans_mode = QVTD_TM_SCALABLE_SLT,
177 + .dma_gpa = intel_iommu_expected_gpa(QVTD_IOVA),
178 + .dma_len = DMA_LEN,
179 + .expected_result = 0,
180 + };
181 +
182 + run_intel_iommu_translation(&cfg);
183 +}
184 +
185 +static void test_intel_iommu_scalable_flt(void)
186 +{
187 + QVTDTestConfig cfg = {
188 + .trans_mode = QVTD_TM_SCALABLE_FLT,
189 + .dma_gpa = intel_iommu_expected_gpa(QVTD_IOVA),
190 + .dma_len = DMA_LEN,
191 + .expected_result = 0,
192 + };
193 +
194 + run_intel_iommu_translation(&cfg);
195 +}
196 +
197 +int main(int argc, char **argv)
198 +{
199 + g_test_init(&argc, &argv, NULL);
200 +
201 + /* Legacy mode tests */
202 + qtest_add_func("/iommu-testdev/intel/legacy-pt",
203 + test_intel_iommu_legacy_pt);
204 + qtest_add_func("/iommu-testdev/intel/legacy-trans",
205 + test_intel_iommu_legacy_trans);
206 +
207 + /* Scalable mode tests */
208 + qtest_add_func("/iommu-testdev/intel/scalable-pt",
209 + test_intel_iommu_scalable_pt);
210 + qtest_add_func("/iommu-testdev/intel/scalable-slt",
211 + test_intel_iommu_scalable_slt);
212 + qtest_add_func("/iommu-testdev/intel/scalable-flt",
213 + test_intel_iommu_scalable_flt);
214 +
215 + return g_test_run();
216 +}
tests/qtest/meson.build
+2
@@ -96,6 +96,8 @@ qtests_i386 = \
96 (config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \
97 (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \
98 (config_all_devices.has_key('CONFIG_VTD') ? ['intel-iommu-test'] : []) + \
99 + (config_all_devices.has_key('CONFIG_VTD') and
100 + config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-intel-test'] : []) + \
101 (host_os != 'windows' and \
102 config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \
103 (config_all_devices.has_key('CONFIG_PCIE_PORT') and \