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
+}