* [PATCH v3 0/2] tests/qtest: Add Intel IOMMU bare-metal test using iommu-testdev @ 2026-03-25 7:09 Fengyuan Yu 2026-03-25 7:09 ` [PATCH v3 1/2] tests/qtest/libqos: Add Intel IOMMU helper library Fengyuan Yu 2026-03-25 7:09 ` [PATCH v3 2/2] tests/qtest: Add Intel IOMMU bare-metal test Fengyuan Yu 0 siblings, 2 replies; 7+ messages in thread From: Fengyuan Yu @ 2026-03-25 7:09 UTC (permalink / raw) To: Michael S. Tsirkin, Jason Wang, Yi Liu, Clément Mathieu--Drif, Fabiano Rosas, Laurent Vivier, Paolo Bonzini, Tao Tang Cc: qemu-devel, Chao Liu, Fengyuan Yu Hi, This patch series adds a bare-metal qtest for the Intel IOMMU (VT-d) using the iommu-testdev framework. The test exercises address translation paths without requiring a full guest OS boot. Motivation ---------- The Intel IOMMU implementation in QEMU supports various translation modes. Currently, comprehensive testing of these translation paths requires booting a full guest OS with appropriate drivers, which is time-consuming and makes regression testing difficult. This new test fills that gap by using iommu-testdev to trigger DMA transactions and validate the IOMMU's translation logic directly. Test Coverage ------------- The new test provides: - Legacy pass-through mode (identity mapping) - Legacy translated mode with 4-level second-level page table walks - Scalable pass-through mode (PGTT=100b) - Scalable second-level translation mode (PGTT=010b) - Scalable first-level translation mode (PGTT=001b) - Root/Context Entry, PASID Directory/Table Entry configuration - Complete 48-bit address space translation - End-to-end DMA verification with memory validation Testing ------- QTEST_QEMU_BINARY=./build/qemu-system-x86_64 \ ./build/tests/qtest/iommu-intel-test --tap -k Changes v1 -> v2 ---------------- - Rebase onto latest upstream to resolve compilation errors - Add scalable translation mode tests (SLT, FLT, PassThrough) - Add spec-aligned register field comments for improved readability Changes v2 -> v3 ---------------- - Fix MAINTAINERS: move qos-intel-iommu* to "QTest IOMMU helpers" section, move iommu-intel-test.c entry to patch 2/2, remove duplicate entries from x86 general section - Remove duplicate Q35_IOMMU_BASE macro, use existing Q35_HOST_BRIDGE_IOMMU_ADDR instead - Drop unused QVTD_TM_SCALABLE_NESTED enum value - Fix parameter alignment in function declarations - Clarify qvtd_build_dma_attrs() comment: PASID=0 is reached via VT-d's no-PASID fallback, not via DMA attributes Thanks, Fengyuan Fengyuan Yu (2): tests/qtest/libqos: Add Intel IOMMU helper library tests/qtest: Add Intel IOMMU bare-metal test MAINTAINERS | 2 + tests/qtest/iommu-intel-test.c | 216 +++++++++++++ tests/qtest/libqos/meson.build | 3 + tests/qtest/libqos/qos-intel-iommu.c | 454 +++++++++++++++++++++++++++ tests/qtest/libqos/qos-intel-iommu.h | 185 +++++++++++ tests/qtest/meson.build | 2 + 6 files changed, 862 insertions(+) create mode 100644 tests/qtest/iommu-intel-test.c create mode 100644 tests/qtest/libqos/qos-intel-iommu.c create mode 100644 tests/qtest/libqos/qos-intel-iommu.h -- 2.39.5 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 1/2] tests/qtest/libqos: Add Intel IOMMU helper library 2026-03-25 7:09 [PATCH v3 0/2] tests/qtest: Add Intel IOMMU bare-metal test using iommu-testdev Fengyuan Yu @ 2026-03-25 7:09 ` Fengyuan Yu 2026-03-27 15:39 ` Fabiano Rosas 2026-03-30 9:09 ` Tao Tang 2026-03-25 7:09 ` [PATCH v3 2/2] tests/qtest: Add Intel IOMMU bare-metal test Fengyuan Yu 1 sibling, 2 replies; 7+ messages in thread From: Fengyuan Yu @ 2026-03-25 7:09 UTC (permalink / raw) To: Michael S. Tsirkin, Jason Wang, Yi Liu, Clément Mathieu--Drif, Fabiano Rosas, Laurent Vivier, Paolo Bonzini, Tao Tang Cc: qemu-devel, Chao Liu, Fengyuan Yu Introduce a libqos helper module for Intel IOMMU (VT-d) bare-metal testing via iommu-testdev. The helper provides routines to: - Build Legacy-mode structures: Root Entry Tables, Context Entry Tables, and 4-level page tables for 48-bit address translation - Build Scalable-mode structures: Scalable Context Entries, PASID Directory Entries, PASID Table Entries, and 4-level page tables for both second-level and first-level translation - Program VT-d registers (Root Table Address, Invalidation Queue, Fault Event MSI, Global Command) following the VT-d specification, with GSTS read-back verification for each step - Execute DMA translations through iommu-testdev and verify results by reading back guest memory The module supports all major VT-d translation modes through the QVTDTransMode enum: - Legacy pass-through - Legacy translated with 4-level paging - Scalable pass-through - Scalable Second-Level Translation - Scalable First-Level Translation Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> Signed-off-by: Fengyuan Yu <15fengyuan@gmail.com> --- MAINTAINERS | 1 + tests/qtest/libqos/meson.build | 3 + tests/qtest/libqos/qos-intel-iommu.c | 454 +++++++++++++++++++++++++++ tests/qtest/libqos/qos-intel-iommu.h | 185 +++++++++++ 4 files changed, 643 insertions(+) create mode 100644 tests/qtest/libqos/qos-intel-iommu.c create mode 100644 tests/qtest/libqos/qos-intel-iommu.h diff --git a/MAINTAINERS b/MAINTAINERS index cd8ba14450..ba0901bf4f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3606,6 +3606,7 @@ S: Maintained F: tests/qtest/libqos/qos-iommu* F: tests/qtest/libqos/qos-smmuv3* F: tests/qtest/libqos/qos-riscv-iommu* +F: tests/qtest/libqos/qos-intel-iommu* Device Fuzzing M: Alexander Bulekov <alxndr@bu.edu> diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 4a69acad0d..96f2fc48b4 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -73,6 +73,9 @@ endif if config_all_devices.has_key('CONFIG_RISCV_IOMMU') libqos_srcs += files('riscv-iommu.c', 'qos-riscv-iommu.c') endif +if config_all_devices.has_key('CONFIG_VTD') + libqos_srcs += files('qos-intel-iommu.c') +endif if config_all_devices.has_key('CONFIG_TPCI200') libqos_srcs += files('tpci200.c') endif diff --git a/tests/qtest/libqos/qos-intel-iommu.c b/tests/qtest/libqos/qos-intel-iommu.c new file mode 100644 index 0000000000..f8ca4c871b --- /dev/null +++ b/tests/qtest/libqos/qos-intel-iommu.c @@ -0,0 +1,454 @@ +/* + * QOS Intel IOMMU (VT-d) Module Implementation + * + * This module provides Intel IOMMU-specific helper functions for libqos tests. + * + * Copyright (c) 2026 Fengyuan Yu <15fengyuan@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i386/intel_iommu_internal.h" +#include "tests/qtest/libqos/pci.h" +#include "qos-iommu-testdev.h" +#include "qos-intel-iommu.h" + +#define QVTD_AW_48BIT_ENCODING 2 + +uint32_t qvtd_expected_dma_result(QVTDTestContext *ctx) +{ + return ctx->config.expected_result; +} + +uint32_t qvtd_build_dma_attrs(void) +{ + /* + * VT-d obtains the Requester ID (Source ID) from PCI bus/devfn routing + * via pci_device_iommu_address_space(), not from DMA attributes. + * + * For scalable mode, iommu-testdev does not set MemTxAttrs.pid, + * so the device's VTDAddressSpace has pasid=PCI_NO_PASID. + * vtd_do_iommu_translate() remaps PCI_NO_PASID to PASID_0 + * when root_scalable is set, which matches the PASID=0 entry + * we configure in qvtd_build_pasid_table_entry(). + */ + return 0; +} + +static void qvtd_build_root_entry(QTestState *qts, uint8_t bus, + uint64_t context_table_ptr, + QVTDTransMode mode) +{ + uint64_t root_entry_addr = QVTD_ROOT_TABLE_BASE + + (bus * sizeof(VTDRootEntry)); + uint64_t lo, hi; + + if (qvtd_is_scalable(mode)) { + /* + * Scalable-mode Root Entry (Section 9.2): + * lo = Lower Context Table Pointer + LP (Lower Present) + * hi = Upper Context Table Pointer + UP (Upper Present) + * + * Lower table covers devfn 0-127, Upper covers devfn 128-255. + * Only lower half is needed for test device (devfn < 128). + */ + lo = (context_table_ptr & VTD_ROOT_ENTRY_CTP) | VTD_ROOT_ENTRY_P; + hi = 0; /* UP=0: upper context table not present */ + } else { + /* + * Legacy Root Entry (Section 9.1): + * lo = Context Table Pointer + Present + * hi = Reserved + */ + lo = (context_table_ptr & VTD_ROOT_ENTRY_CTP) | VTD_ROOT_ENTRY_P; + hi = 0; + } + + qtest_writeq(qts, root_entry_addr, lo); + qtest_writeq(qts, root_entry_addr + 8, hi); +} + +static void qvtd_build_context_entry(QTestState *qts, uint16_t sid, + QVTDTransMode mode, uint64_t ssptptr) +{ + uint8_t devfn = sid & 0xff; + uint64_t context_entry_addr = QVTD_CONTEXT_TABLE_BASE + + (devfn * VTD_CTX_ENTRY_LEGACY_SIZE); + uint64_t lo, hi; + + if (mode == QVTD_TM_LEGACY_PT) { + /* + * Pass-through mode (Section 9.3): + * lo: P + FPD(=0, fault enabled) + TT(=Pass-through) + * hi: DID + AW + */ + lo = VTD_CONTEXT_ENTRY_P | VTD_CONTEXT_TT_PASS_THROUGH; + hi = ((uint64_t)QVTD_DOMAIN_ID << 8) | QVTD_AW_48BIT_ENCODING; + } else { + /* + * Translated mode (Section 9.3): + * lo: P + FPD(=0, fault enabled) + TT(=Multi-level) + SSPTPTR + * hi: DID + AW(=48-bit, 4-level) + */ + lo = VTD_CONTEXT_ENTRY_P | VTD_CONTEXT_TT_MULTI_LEVEL | + (ssptptr & VTD_CONTEXT_ENTRY_SSPTPTR); + hi = ((uint64_t)QVTD_DOMAIN_ID << 8) | QVTD_AW_48BIT_ENCODING; + } + + qtest_writeq(qts, context_entry_addr, lo); + qtest_writeq(qts, context_entry_addr + 8, hi); +} + +static void qvtd_build_scalable_context_entry(QTestState *qts, uint16_t sid) +{ + uint8_t devfn = sid & 0xff; + uint64_t ce_addr = QVTD_CONTEXT_TABLE_BASE + + (devfn * VTD_CTX_ENTRY_SCALABLE_SIZE); + + /* + * Scalable-Mode Context Entry (Section 9.4), 32 bytes = 4 qwords: + * + * val[0]: P + FPD(=0) + DTE(=0) + PASIDE(=0) + PRE(=0) + HPTE(=0) + * + EPTR(=0) + PDTS(=0) + PASIDDIRPTR + * val[1]: RID_PASID(=0) + PDTTE(=0) + PRE(=0) + RID_CG(=0) + * val[2]: Reserved (must be 0) + * val[3]: Reserved (must be 0) + */ + qtest_writeq(qts, ce_addr, + (QVTD_PASID_DIR_BASE & VTD_PASID_DIR_BASE_ADDR_MASK) | + VTD_CONTEXT_ENTRY_P); + qtest_writeq(qts, ce_addr + 8, 0); + qtest_writeq(qts, ce_addr + 16, 0); + qtest_writeq(qts, ce_addr + 24, 0); +} + +static void qvtd_build_pasid_dir_entry(QTestState *qts) +{ + uint64_t addr = QVTD_PASID_DIR_BASE + + VTD_PASID_DIR_INDEX(0) * VTD_PASID_DIR_ENTRY_SIZE; + + /* + * PASID Directory Entry (Section 9.5): + * P + FPD(=0, fault enabled) + SMPTBLPTR + */ + qtest_writeq(qts, addr, + (QVTD_PASID_TABLE_BASE & VTD_PASID_TABLE_BASE_ADDR_MASK) | + VTD_PASID_ENTRY_P); +} + +static void qvtd_build_pasid_table_entry(QTestState *qts, QVTDTransMode mode, + uint64_t ptptr) +{ + uint64_t addr = QVTD_PASID_TABLE_BASE + + VTD_PASID_TABLE_INDEX(0) * VTD_PASID_ENTRY_SIZE; + uint64_t val0, val1, val2; + + /* + * Scalable-Mode PASID Table Entry (Section 9.6), 64 bytes = 8 qwords: + * + * val[0]: P + FPD(=0) + AW + PGTT + SSADE(=0) + SSPTPTR + * val[1]: DID + PWSNP(=0) + PGSNP(=0) + * + CD(=0) + EMTE(=0) + PAT(=0): Memory Type, + * all Reserved(0) since QEMU ECAP.MTS=0 + * val[2]: SRE(=0) + FSPM(=0, 4-level) + WPE(=0) + IGN + EAFE(=0) + FSPTPTR + * val[3]: Reserved (must be 0) + * val[4]: HPT fields, Reserved(0) since QEMU ECAP.HPTS=0 + * val[5]: HPT fields, Reserved(0) since QEMU ECAP.HPTS=0 + * val[6]: Reserved (must be 0) + * val[7]: Reserved (must be 0) + */ + switch (mode) { + case QVTD_TM_SCALABLE_PT: + val0 = VTD_PASID_ENTRY_P | + ((uint64_t)VTD_SM_PASID_ENTRY_PT << 6); + val1 = (uint64_t)QVTD_DOMAIN_ID; + val2 = 0; + break; + case QVTD_TM_SCALABLE_SLT: + val0 = VTD_PASID_ENTRY_P | + ((uint64_t)VTD_SM_PASID_ENTRY_SST << 6) | + ((uint64_t)QVTD_AW_48BIT_ENCODING << 2) | + (ptptr & VTD_SM_PASID_ENTRY_SSPTPTR); + val1 = (uint64_t)QVTD_DOMAIN_ID; + val2 = 0; + break; + case QVTD_TM_SCALABLE_FLT: + /* + * val[2] fields for FLT (Section 9.6): + * SRE(=0, user-level DMA only) + FSPM(=0, 4-level) + + * WPE(=0, no supervisor write-protect) + IGN + EAFE(=0) + FSPTPTR + */ + val0 = VTD_PASID_ENTRY_P | + ((uint64_t)VTD_SM_PASID_ENTRY_FST << 6); + val1 = (uint64_t)QVTD_DOMAIN_ID; + val2 = ptptr & QVTD_SM_PASID_ENTRY_FSPTPTR; + break; + default: + g_assert_not_reached(); + } + + qtest_writeq(qts, addr, val0); + qtest_writeq(qts, addr + 8, val1); + qtest_writeq(qts, addr + 16, val2); + qtest_writeq(qts, addr + 24, 0); + qtest_writeq(qts, addr + 32, 0); + qtest_writeq(qts, addr + 40, 0); + qtest_writeq(qts, addr + 48, 0); + qtest_writeq(qts, addr + 56, 0); +} + +/* + * VT-d second-level paging helpers. + * 4-level, 48-bit address space, 9 bits per level index. + */ +static uint32_t qvtd_get_table_index(uint64_t iova, int level) +{ + int shift = VTD_PAGE_SHIFT + VTD_LEVEL_BITS * (level - 1); + + return (iova >> shift) & ((1u << VTD_LEVEL_BITS) - 1); +} + +static uint64_t qvtd_get_table_addr(uint64_t base, int level, uint64_t iova) +{ + return base + (qvtd_get_table_index(iova, level) * QVTD_PTE_SIZE); +} + +static uint64_t qvtd_get_pte_attrs(void) +{ + /* Second-level: R/W in every paging entry (Section 3.7.1) */ + return VTD_SS_R | VTD_SS_W; +} + +static uint64_t qvtd_get_fl_pte_attrs(bool is_leaf) +{ + /* First-level: x86 page table format (VT-d spec Section 9.9) */ + uint64_t attrs = VTD_FS_P | VTD_FS_RW | VTD_FS_US | VTD_FS_A; + + if (is_leaf) { + attrs |= VTD_FS_D; + } + return attrs; +} + +void qvtd_setup_translation_tables(QTestState *qts, uint64_t iova, + QVTDTransMode mode) +{ + bool is_fl = (mode == QVTD_TM_SCALABLE_FLT); + uint64_t non_leaf_attrs, leaf_attrs; + + if (is_fl) { + non_leaf_attrs = qvtd_get_fl_pte_attrs(false); + leaf_attrs = qvtd_get_fl_pte_attrs(true); + } else { + /* Second-level: all levels use identical R/W attrs (spec 3.7.1) */ + non_leaf_attrs = qvtd_get_pte_attrs(); + leaf_attrs = non_leaf_attrs; + } + + g_test_message("Page table setup: IOVA=0x%" PRIx64 + " PA=0x%" PRIx64 " %s", + (uint64_t)iova, (uint64_t)QVTD_PT_VAL, + is_fl ? "first-level" : "second-level"); + + /* PML4 (L4) -> PDPT (L3) -> PD (L2) -> PT (L1) -> PA */ + qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L4_BASE, 4, iova), + QVTD_PT_L3_BASE | non_leaf_attrs); + qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L3_BASE, 3, iova), + QVTD_PT_L2_BASE | non_leaf_attrs); + qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L2_BASE, 2, iova), + QVTD_PT_L1_BASE | non_leaf_attrs); + qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L1_BASE, 1, iova), + (QVTD_PT_VAL & VTD_PAGE_MASK_4K) | leaf_attrs); +} + +void qvtd_program_regs(QTestState *qts, uint64_t iommu_base, + QVTDTransMode mode) +{ + uint32_t gcmd = 0; + uint64_t rtaddr = QVTD_ROOT_TABLE_BASE; + + /* Set SMT bit for scalable mode (VT-d spec Section 9.1) */ + if (qvtd_is_scalable(mode)) { + rtaddr |= VTD_RTADDR_SMT; + } + + /* Set Root Table Address */ + qtest_writeq(qts, iommu_base + DMAR_RTADDR_REG, rtaddr); + + /* Set Root Table Pointer and verify */ + gcmd |= VTD_GCMD_SRTP; + qtest_writel(qts, iommu_base + DMAR_GCMD_REG, gcmd); + g_assert(qtest_readl(qts, iommu_base + DMAR_GSTS_REG) & VTD_GSTS_RTPS); + + /* Setup Invalidation Queue */ + qtest_writeq(qts, iommu_base + DMAR_IQA_REG, + QVTD_IQ_BASE | QVTD_IQ_QS); + qtest_writeq(qts, iommu_base + DMAR_IQH_REG, 0); + qtest_writeq(qts, iommu_base + DMAR_IQT_REG, 0); + + /* Enable Queued Invalidation and verify */ + gcmd |= VTD_GCMD_QIE; + qtest_writel(qts, iommu_base + DMAR_GCMD_REG, gcmd); + g_assert(qtest_readl(qts, iommu_base + DMAR_GSTS_REG) & VTD_GSTS_QIES); + + /* Setup Fault Event MSI */ + qtest_writel(qts, iommu_base + DMAR_FECTL_REG, 0x0); + qtest_writel(qts, iommu_base + DMAR_FEDATA_REG, QVTD_FAULT_IRQ_DATA); + qtest_writel(qts, iommu_base + DMAR_FEADDR_REG, QVTD_FAULT_IRQ_ADDR); + + /* Enable translation and verify */ + gcmd |= VTD_GCMD_TE; + qtest_writel(qts, iommu_base + DMAR_GCMD_REG, gcmd); + g_assert(qtest_readl(qts, iommu_base + DMAR_GSTS_REG) & VTD_GSTS_TES); +} + +uint32_t qvtd_build_translation(QTestState *qts, QVTDTransMode mode, + uint16_t sid) +{ + uint8_t bus = (sid >> 8) & 0xff; + + g_test_message("Build translation: IOVA=0x%" PRIx64 " PA=0x%" PRIx64 + " mode=%d", + (uint64_t)QVTD_IOVA, (uint64_t)QVTD_PT_VAL, mode); + + /* Clear IOMMU structure regions to avoid stale entries */ + qtest_memset(qts, QVTD_ROOT_TABLE_BASE, 0, 0x1000); + qtest_memset(qts, QVTD_PT_L4_BASE, 0, 0x4000); + + if (qvtd_is_scalable(mode)) { + /* Scalable: 32B context entries need 8KB */ + qtest_memset(qts, QVTD_CONTEXT_TABLE_BASE, 0, 0x2000); + qtest_memset(qts, QVTD_PASID_DIR_BASE, 0, 0x1000); + qtest_memset(qts, QVTD_PASID_TABLE_BASE, 0, 0x1000); + } else { + qtest_memset(qts, QVTD_CONTEXT_TABLE_BASE, 0, 0x1000); + } + + qvtd_build_root_entry(qts, bus, QVTD_CONTEXT_TABLE_BASE, mode); + + if (qvtd_is_scalable(mode)) { + /* Scalable path: context -> PASID dir -> PASID entry -> page tables */ + qvtd_build_scalable_context_entry(qts, sid); + qvtd_build_pasid_dir_entry(qts); + + if (mode == QVTD_TM_SCALABLE_PT) { + qvtd_build_pasid_table_entry(qts, mode, 0); + } else { + qvtd_setup_translation_tables(qts, QVTD_IOVA, mode); + qvtd_build_pasid_table_entry(qts, mode, QVTD_PT_L4_BASE); + } + } else { + /* Legacy path */ + if (mode == QVTD_TM_LEGACY_PT) { + qvtd_build_context_entry(qts, sid, mode, 0); + } else { + qvtd_setup_translation_tables(qts, QVTD_IOVA, mode); + qvtd_build_context_entry(qts, sid, mode, QVTD_PT_L4_BASE); + } + } + + return 0; +} + +uint32_t qvtd_setup_and_enable_translation(QVTDTestContext *ctx) +{ + uint32_t build_result; + + /* Build translation structures first */ + build_result = qvtd_build_translation(ctx->qts, ctx->config.trans_mode, + ctx->sid); + if (build_result != 0) { + g_test_message("Build failed: mode=%u sid=%u status=0x%x", + ctx->config.trans_mode, ctx->sid, build_result); + ctx->trans_status = build_result; + return ctx->trans_status; + } + + /* Program IOMMU registers (sets root table pointer, enables translation) */ + qvtd_program_regs(ctx->qts, ctx->iommu_base, ctx->config.trans_mode); + + ctx->trans_status = 0; + return ctx->trans_status; +} + +static bool qvtd_validate_test_result(QVTDTestContext *ctx) +{ + uint32_t expected = qvtd_expected_dma_result(ctx); + + g_test_message("-> Validating result: expected=0x%x actual=0x%x", + expected, ctx->dma_result); + return (ctx->dma_result == expected); +} + +static uint32_t qvtd_single_translation_setup(void *opaque) +{ + return qvtd_setup_and_enable_translation(opaque); +} + +static uint32_t qvtd_single_translation_attrs(void *opaque) +{ + return qvtd_build_dma_attrs(); +} + +static bool qvtd_single_translation_validate(void *opaque) +{ + return qvtd_validate_test_result(opaque); +} + +static void qvtd_single_translation_report(void *opaque, uint32_t dma_result) +{ + QVTDTestContext *ctx = opaque; + + if (dma_result != 0) { + g_test_message("DMA failed: mode=%u result=0x%x", + ctx->config.trans_mode, dma_result); + } else { + g_test_message("-> DMA succeeded: mode=%u", + ctx->config.trans_mode); + } +} + +void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev, + QPCIBar bar, uint64_t iommu_base, + const QVTDTestConfig *cfg) +{ + QVTDTestContext ctx = { + .qts = qts, + .dev = dev, + .bar = bar, + .iommu_base = iommu_base, + .config = *cfg, + .sid = dev->devfn, + }; + + QOSIOMMUTestdevDmaCfg dma = { + .dev = dev, + .bar = bar, + .iova = QVTD_IOVA, + .gpa = cfg->dma_gpa, + .len = cfg->dma_len, + }; + + qtest_memset(qts, cfg->dma_gpa, 0x00, cfg->dma_len); + qos_iommu_testdev_single_translation(&dma, &ctx, + qvtd_single_translation_setup, + qvtd_single_translation_attrs, + qvtd_single_translation_validate, + qvtd_single_translation_report, + &ctx.dma_result); + + if (ctx.dma_result == 0 && ctx.config.expected_result == 0) { + g_autofree uint8_t *buf = NULL; + + buf = g_malloc(ctx.config.dma_len); + qtest_memread(ctx.qts, ctx.config.dma_gpa, buf, ctx.config.dma_len); + + for (int i = 0; i < ctx.config.dma_len; i++) { + uint8_t expected; + + expected = (ITD_DMA_WRITE_VAL >> ((i % 4) * 8)) & 0xff; + g_assert_cmpuint(buf[i], ==, expected); + } + } +} diff --git a/tests/qtest/libqos/qos-intel-iommu.h b/tests/qtest/libqos/qos-intel-iommu.h new file mode 100644 index 0000000000..c6cacc5c3f --- /dev/null +++ b/tests/qtest/libqos/qos-intel-iommu.h @@ -0,0 +1,185 @@ +/* + * QOS Intel IOMMU (VT-d) Module + * + * This module provides Intel IOMMU-specific helper functions for libqos tests, + * encapsulating VT-d setup, assertion, and cleanup operations. + * + * Copyright (c) 2026 Fengyuan Yu <15fengyuan@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QTEST_LIBQOS_INTEL_IOMMU_H +#define QTEST_LIBQOS_INTEL_IOMMU_H + +#include "hw/misc/iommu-testdev.h" +#include "hw/i386/intel_iommu_internal.h" + +/* + * Guest memory layout for IOMMU structures. + * All structures are placed in guest physical memory inside the 512MB RAM. + * Using 256MB mark (0x10000000) as base to ensure all structures fit in RAM. + */ +#define QVTD_MEM_BASE 0x10000000ULL + +/* Root Entry Table: 256 entries * 16 bytes = 4KB */ +#define QVTD_ROOT_TABLE_BASE (QVTD_MEM_BASE + 0x00000000) + +/* Context Entry Table: 256 entries, 16B (legacy) or 32B (scalable) per entry */ +#define QVTD_CONTEXT_TABLE_BASE (QVTD_MEM_BASE + 0x00001000) + +/* Page Tables: 4-level hierarchy for 48-bit address translation */ +#define QVTD_PT_L4_BASE (QVTD_MEM_BASE + 0x00010000) /* PML4 */ +#define QVTD_PT_L3_BASE (QVTD_MEM_BASE + 0x00011000) /* PDPT */ +#define QVTD_PT_L2_BASE (QVTD_MEM_BASE + 0x00012000) /* PD */ +#define QVTD_PT_L1_BASE (QVTD_MEM_BASE + 0x00013000) /* PT */ + +/* + * Invalidation Queue. + * IQA_REG bits[2:0] = QS, entries = 1 << (QS + 8), each entry 16 bytes. + */ +#define QVTD_IQ_BASE (QVTD_MEM_BASE + 0x00020000) +#define QVTD_IQ_QS 0 /* QS=0 → 256 entries */ + +/* + * Fault Event MSI configuration. + */ +#define QVTD_FAULT_IRQ_ADDR 0xfee00000 /* APIC base */ +#define QVTD_FAULT_IRQ_DATA 0x0 + +/* Scalable mode PASID structures */ +#define QVTD_PASID_DIR_BASE (QVTD_MEM_BASE + 0x00030000) +#define QVTD_PASID_TABLE_BASE (QVTD_MEM_BASE + 0x00031000) + +/* Page table entry size (8 bytes per PTE) */ +#define QVTD_PTE_SIZE sizeof(uint64_t) + +/* FSPTPTR mask: same as VTD_SM_PASID_ENTRY_SSPTPTR, bits[63:12] */ +#define QVTD_SM_PASID_ENTRY_FSPTPTR VTD_SM_PASID_ENTRY_SSPTPTR + +/* Default Domain ID for single-domain tests */ +#define QVTD_DOMAIN_ID 0 + +/* Test IOVA and target physical address */ +#define QVTD_IOVA 0x0000000010200567ull +#define QVTD_PT_VAL (QVTD_MEM_BASE + 0x00100000) + +/* + * Translation modes supported by Intel IOMMU + */ +typedef enum QVTDTransMode { + QVTD_TM_LEGACY_PT, /* Legacy pass-through mode */ + QVTD_TM_LEGACY_TRANS, /* Legacy translated mode (4-level paging) */ + QVTD_TM_SCALABLE_PT, /* Scalable pass-through mode */ + QVTD_TM_SCALABLE_SLT, /* Scalable Second Level Translation */ + QVTD_TM_SCALABLE_FLT, /* Scalable First Level Translation */ +} QVTDTransMode; + +static inline bool qvtd_is_scalable(QVTDTransMode mode) +{ + return mode == QVTD_TM_SCALABLE_PT || + mode == QVTD_TM_SCALABLE_SLT || + mode == QVTD_TM_SCALABLE_FLT; +} + +typedef struct QVTDTestConfig { + QVTDTransMode trans_mode; /* Translation mode */ + uint64_t dma_gpa; /* GPA for readback validation */ + uint32_t dma_len; /* DMA length for testing */ + uint32_t expected_result; /* Expected DMA result */ +} QVTDTestConfig; + +typedef struct QVTDTestContext { + QTestState *qts; /* QTest state handle */ + QPCIDevice *dev; /* PCI device handle */ + QPCIBar bar; /* PCI BAR for MMIO access */ + QVTDTestConfig config; /* Test configuration */ + uint64_t iommu_base; /* Intel IOMMU base address */ + uint32_t trans_status; /* Translation configuration status */ + uint32_t dma_result; /* DMA operation result */ + uint16_t sid; /* Source ID (bus:devfn) */ +} QVTDTestContext; + +/* + * qvtd_setup_and_enable_translation - Complete translation setup and enable + * + * @ctx: Test context containing configuration and device handles + * + * Returns: Translation status (0 = success, non-zero = error) + * + * This function performs the complete translation setup sequence: + * 1. Builds VT-d structures (root/context entry, page tables) + * 2. Programs IOMMU registers and enables translation + * 3. Returns configuration status + */ +uint32_t qvtd_setup_and_enable_translation(QVTDTestContext *ctx); + +/* + * qvtd_build_translation - Build Intel IOMMU translation structures + * + * @qts: QTest state handle + * @mode: Translation mode (pass-through or translated) + * @sid: Source ID (bus:devfn) + * + * Returns: Build status (0 = success, non-zero = error) + * + * Constructs all necessary VT-d translation structures in guest memory: + * - Root Entry for the device's bus + * - Context Entry for the device + * - Complete 4-level page table hierarchy (if translated mode) + */ +uint32_t qvtd_build_translation(QTestState *qts, QVTDTransMode mode, + uint16_t sid); + +/* + * qvtd_program_regs - Program Intel IOMMU registers and enable translation + * + * @qts: QTest state handle + * @iommu_base: IOMMU base address + * @mode: Translation mode (scalable modes set RTADDR SMT bit) + * + * Programs IOMMU registers with the following sequence: + * 1. Set root table pointer (SRTP), with SMT bit for scalable mode + * 2. Setup invalidation queue (QIE) + * 3. Configure fault event MSI + * 4. Enable translation (TE) + * + * Each step verifies completion via GSTS register read-back. + */ +void qvtd_program_regs(QTestState *qts, uint64_t iommu_base, + QVTDTransMode mode); + +/* + * qvtd_setup_translation_tables - Setup complete VT-d page table hierarchy + * + * @qts: QTest state handle + * @iova: Input Virtual Address to translate + * @mode: Translation mode + * + * This builds the 4-level page table structure for translating + * the given IOVA to PA through Intel VT-d. The structure is: + * - PML4 (Level 4): IOVA bits [47:39] + * - PDPT (Level 3): IOVA bits [38:30] + * - PD (Level 2): IOVA bits [29:21] + * - PT (Level 1): IOVA bits [20:12] + * - Page offset: IOVA bits [11:0] + * + * The function writes all necessary Page Table Entries (PTEs) to guest + * memory using qtest_writeq(), setting up the complete translation path + * that the VT-d hardware will traverse during DMA operations. + */ +void qvtd_setup_translation_tables(QTestState *qts, uint64_t iova, + QVTDTransMode mode); + +/* Calculate expected DMA result */ +uint32_t qvtd_expected_dma_result(QVTDTestContext *ctx); + +/* Build DMA attributes for Intel VT-d */ +uint32_t qvtd_build_dma_attrs(void); + +/* High-level test execution helpers */ +void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev, + QPCIBar bar, uint64_t iommu_base, + const QVTDTestConfig *cfg); + +#endif /* QTEST_LIBQOS_INTEL_IOMMU_H */ -- 2.39.5 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] tests/qtest/libqos: Add Intel IOMMU helper library 2026-03-25 7:09 ` [PATCH v3 1/2] tests/qtest/libqos: Add Intel IOMMU helper library Fengyuan Yu @ 2026-03-27 15:39 ` Fabiano Rosas 2026-03-30 9:09 ` Tao Tang 1 sibling, 0 replies; 7+ messages in thread From: Fabiano Rosas @ 2026-03-27 15:39 UTC (permalink / raw) To: Fengyuan Yu, Michael S. Tsirkin, Jason Wang, Yi Liu, Clément Mathieu--Drif, Laurent Vivier, Paolo Bonzini, Tao Tang Cc: qemu-devel, Chao Liu, Fengyuan Yu Fengyuan Yu <15fengyuan@gmail.com> writes: > Introduce a libqos helper module for Intel IOMMU (VT-d) bare-metal > testing via iommu-testdev. The helper provides routines to: > > - Build Legacy-mode structures: Root Entry Tables, Context Entry Tables, > and 4-level page tables for 48-bit address translation > - Build Scalable-mode structures: Scalable Context Entries, PASID > Directory Entries, PASID Table Entries, and 4-level page tables for > both second-level and first-level translation > - Program VT-d registers (Root Table Address, Invalidation Queue, > Fault Event MSI, Global Command) following the VT-d specification, > with GSTS read-back verification for each step > - Execute DMA translations through iommu-testdev and verify results > by reading back guest memory > > The module supports all major VT-d translation modes through the > QVTDTransMode enum: > - Legacy pass-through > - Legacy translated with 4-level paging > - Scalable pass-through > - Scalable Second-Level Translation > - Scalable First-Level Translation > > Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> > Signed-off-by: Fengyuan Yu <15fengyuan@gmail.com> > --- > MAINTAINERS | 1 + > tests/qtest/libqos/meson.build | 3 + > tests/qtest/libqos/qos-intel-iommu.c | 454 +++++++++++++++++++++++++++ > tests/qtest/libqos/qos-intel-iommu.h | 185 +++++++++++ > 4 files changed, 643 insertions(+) > create mode 100644 tests/qtest/libqos/qos-intel-iommu.c > create mode 100644 tests/qtest/libqos/qos-intel-iommu.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index cd8ba14450..ba0901bf4f 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -3606,6 +3606,7 @@ S: Maintained > F: tests/qtest/libqos/qos-iommu* > F: tests/qtest/libqos/qos-smmuv3* > F: tests/qtest/libqos/qos-riscv-iommu* > +F: tests/qtest/libqos/qos-intel-iommu* > > Device Fuzzing > M: Alexander Bulekov <alxndr@bu.edu> > diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build > index 4a69acad0d..96f2fc48b4 100644 > --- a/tests/qtest/libqos/meson.build > +++ b/tests/qtest/libqos/meson.build > @@ -73,6 +73,9 @@ endif > if config_all_devices.has_key('CONFIG_RISCV_IOMMU') > libqos_srcs += files('riscv-iommu.c', 'qos-riscv-iommu.c') > endif > +if config_all_devices.has_key('CONFIG_VTD') > + libqos_srcs += files('qos-intel-iommu.c') > +endif > if config_all_devices.has_key('CONFIG_TPCI200') > libqos_srcs += files('tpci200.c') > endif > diff --git a/tests/qtest/libqos/qos-intel-iommu.c b/tests/qtest/libqos/qos-intel-iommu.c > new file mode 100644 > index 0000000000..f8ca4c871b > --- /dev/null > +++ b/tests/qtest/libqos/qos-intel-iommu.c > @@ -0,0 +1,454 @@ > +/* > + * QOS Intel IOMMU (VT-d) Module Implementation > + * > + * This module provides Intel IOMMU-specific helper functions for libqos tests. > + * > + * Copyright (c) 2026 Fengyuan Yu <15fengyuan@gmail.com> > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#include "qemu/osdep.h" > +#include "hw/i386/intel_iommu_internal.h" > +#include "tests/qtest/libqos/pci.h" > +#include "qos-iommu-testdev.h" > +#include "qos-intel-iommu.h" > + > +#define QVTD_AW_48BIT_ENCODING 2 > + > +uint32_t qvtd_expected_dma_result(QVTDTestContext *ctx) > +{ > + return ctx->config.expected_result; > +} > + > +uint32_t qvtd_build_dma_attrs(void) > +{ > + /* > + * VT-d obtains the Requester ID (Source ID) from PCI bus/devfn routing > + * via pci_device_iommu_address_space(), not from DMA attributes. > + * > + * For scalable mode, iommu-testdev does not set MemTxAttrs.pid, > + * so the device's VTDAddressSpace has pasid=PCI_NO_PASID. > + * vtd_do_iommu_translate() remaps PCI_NO_PASID to PASID_0 > + * when root_scalable is set, which matches the PASID=0 entry > + * we configure in qvtd_build_pasid_table_entry(). > + */ > + return 0; > +} > + > +static void qvtd_build_root_entry(QTestState *qts, uint8_t bus, > + uint64_t context_table_ptr, > + QVTDTransMode mode) > +{ > + uint64_t root_entry_addr = QVTD_ROOT_TABLE_BASE + > + (bus * sizeof(VTDRootEntry)); > + uint64_t lo, hi; > + > + if (qvtd_is_scalable(mode)) { > + /* > + * Scalable-mode Root Entry (Section 9.2): > + * lo = Lower Context Table Pointer + LP (Lower Present) > + * hi = Upper Context Table Pointer + UP (Upper Present) > + * > + * Lower table covers devfn 0-127, Upper covers devfn 128-255. > + * Only lower half is needed for test device (devfn < 128). > + */ > + lo = (context_table_ptr & VTD_ROOT_ENTRY_CTP) | VTD_ROOT_ENTRY_P; > + hi = 0; /* UP=0: upper context table not present */ > + } else { > + /* > + * Legacy Root Entry (Section 9.1): > + * lo = Context Table Pointer + Present > + * hi = Reserved > + */ > + lo = (context_table_ptr & VTD_ROOT_ENTRY_CTP) | VTD_ROOT_ENTRY_P; > + hi = 0; > + } > + > + qtest_writeq(qts, root_entry_addr, lo); > + qtest_writeq(qts, root_entry_addr + 8, hi); > +} > + > +static void qvtd_build_context_entry(QTestState *qts, uint16_t sid, > + QVTDTransMode mode, uint64_t ssptptr) > +{ > + uint8_t devfn = sid & 0xff; > + uint64_t context_entry_addr = QVTD_CONTEXT_TABLE_BASE + > + (devfn * VTD_CTX_ENTRY_LEGACY_SIZE); > + uint64_t lo, hi; > + > + if (mode == QVTD_TM_LEGACY_PT) { > + /* > + * Pass-through mode (Section 9.3): > + * lo: P + FPD(=0, fault enabled) + TT(=Pass-through) > + * hi: DID + AW > + */ > + lo = VTD_CONTEXT_ENTRY_P | VTD_CONTEXT_TT_PASS_THROUGH; > + hi = ((uint64_t)QVTD_DOMAIN_ID << 8) | QVTD_AW_48BIT_ENCODING; > + } else { > + /* > + * Translated mode (Section 9.3): > + * lo: P + FPD(=0, fault enabled) + TT(=Multi-level) + SSPTPTR > + * hi: DID + AW(=48-bit, 4-level) > + */ > + lo = VTD_CONTEXT_ENTRY_P | VTD_CONTEXT_TT_MULTI_LEVEL | > + (ssptptr & VTD_CONTEXT_ENTRY_SSPTPTR); > + hi = ((uint64_t)QVTD_DOMAIN_ID << 8) | QVTD_AW_48BIT_ENCODING; > + } > + > + qtest_writeq(qts, context_entry_addr, lo); > + qtest_writeq(qts, context_entry_addr + 8, hi); > +} > + > +static void qvtd_build_scalable_context_entry(QTestState *qts, uint16_t sid) > +{ > + uint8_t devfn = sid & 0xff; > + uint64_t ce_addr = QVTD_CONTEXT_TABLE_BASE + > + (devfn * VTD_CTX_ENTRY_SCALABLE_SIZE); > + > + /* > + * Scalable-Mode Context Entry (Section 9.4), 32 bytes = 4 qwords: > + * > + * val[0]: P + FPD(=0) + DTE(=0) + PASIDE(=0) + PRE(=0) + HPTE(=0) > + * + EPTR(=0) + PDTS(=0) + PASIDDIRPTR > + * val[1]: RID_PASID(=0) + PDTTE(=0) + PRE(=0) + RID_CG(=0) > + * val[2]: Reserved (must be 0) > + * val[3]: Reserved (must be 0) > + */ > + qtest_writeq(qts, ce_addr, > + (QVTD_PASID_DIR_BASE & VTD_PASID_DIR_BASE_ADDR_MASK) | > + VTD_CONTEXT_ENTRY_P); > + qtest_writeq(qts, ce_addr + 8, 0); > + qtest_writeq(qts, ce_addr + 16, 0); > + qtest_writeq(qts, ce_addr + 24, 0); > +} > + > +static void qvtd_build_pasid_dir_entry(QTestState *qts) > +{ > + uint64_t addr = QVTD_PASID_DIR_BASE + > + VTD_PASID_DIR_INDEX(0) * VTD_PASID_DIR_ENTRY_SIZE; > + > + /* > + * PASID Directory Entry (Section 9.5): > + * P + FPD(=0, fault enabled) + SMPTBLPTR > + */ > + qtest_writeq(qts, addr, > + (QVTD_PASID_TABLE_BASE & VTD_PASID_TABLE_BASE_ADDR_MASK) | > + VTD_PASID_ENTRY_P); > +} > + > +static void qvtd_build_pasid_table_entry(QTestState *qts, QVTDTransMode mode, > + uint64_t ptptr) > +{ > + uint64_t addr = QVTD_PASID_TABLE_BASE + > + VTD_PASID_TABLE_INDEX(0) * VTD_PASID_ENTRY_SIZE; > + uint64_t val0, val1, val2; > + > + /* > + * Scalable-Mode PASID Table Entry (Section 9.6), 64 bytes = 8 qwords: > + * > + * val[0]: P + FPD(=0) + AW + PGTT + SSADE(=0) + SSPTPTR > + * val[1]: DID + PWSNP(=0) + PGSNP(=0) > + * + CD(=0) + EMTE(=0) + PAT(=0): Memory Type, > + * all Reserved(0) since QEMU ECAP.MTS=0 > + * val[2]: SRE(=0) + FSPM(=0, 4-level) + WPE(=0) + IGN + EAFE(=0) + FSPTPTR > + * val[3]: Reserved (must be 0) > + * val[4]: HPT fields, Reserved(0) since QEMU ECAP.HPTS=0 > + * val[5]: HPT fields, Reserved(0) since QEMU ECAP.HPTS=0 > + * val[6]: Reserved (must be 0) > + * val[7]: Reserved (must be 0) > + */ > + switch (mode) { > + case QVTD_TM_SCALABLE_PT: > + val0 = VTD_PASID_ENTRY_P | > + ((uint64_t)VTD_SM_PASID_ENTRY_PT << 6); > + val1 = (uint64_t)QVTD_DOMAIN_ID; > + val2 = 0; > + break; > + case QVTD_TM_SCALABLE_SLT: > + val0 = VTD_PASID_ENTRY_P | > + ((uint64_t)VTD_SM_PASID_ENTRY_SST << 6) | > + ((uint64_t)QVTD_AW_48BIT_ENCODING << 2) | > + (ptptr & VTD_SM_PASID_ENTRY_SSPTPTR); > + val1 = (uint64_t)QVTD_DOMAIN_ID; > + val2 = 0; > + break; > + case QVTD_TM_SCALABLE_FLT: > + /* > + * val[2] fields for FLT (Section 9.6): > + * SRE(=0, user-level DMA only) + FSPM(=0, 4-level) + > + * WPE(=0, no supervisor write-protect) + IGN + EAFE(=0) + FSPTPTR > + */ > + val0 = VTD_PASID_ENTRY_P | > + ((uint64_t)VTD_SM_PASID_ENTRY_FST << 6); > + val1 = (uint64_t)QVTD_DOMAIN_ID; > + val2 = ptptr & QVTD_SM_PASID_ENTRY_FSPTPTR; > + break; > + default: > + g_assert_not_reached(); > + } > + > + qtest_writeq(qts, addr, val0); > + qtest_writeq(qts, addr + 8, val1); > + qtest_writeq(qts, addr + 16, val2); > + qtest_writeq(qts, addr + 24, 0); > + qtest_writeq(qts, addr + 32, 0); > + qtest_writeq(qts, addr + 40, 0); > + qtest_writeq(qts, addr + 48, 0); > + qtest_writeq(qts, addr + 56, 0); > +} > + > +/* > + * VT-d second-level paging helpers. > + * 4-level, 48-bit address space, 9 bits per level index. > + */ > +static uint32_t qvtd_get_table_index(uint64_t iova, int level) > +{ > + int shift = VTD_PAGE_SHIFT + VTD_LEVEL_BITS * (level - 1); > + > + return (iova >> shift) & ((1u << VTD_LEVEL_BITS) - 1); > +} > + > +static uint64_t qvtd_get_table_addr(uint64_t base, int level, uint64_t iova) > +{ > + return base + (qvtd_get_table_index(iova, level) * QVTD_PTE_SIZE); > +} > + > +static uint64_t qvtd_get_pte_attrs(void) > +{ > + /* Second-level: R/W in every paging entry (Section 3.7.1) */ > + return VTD_SS_R | VTD_SS_W; > +} > + > +static uint64_t qvtd_get_fl_pte_attrs(bool is_leaf) > +{ > + /* First-level: x86 page table format (VT-d spec Section 9.9) */ > + uint64_t attrs = VTD_FS_P | VTD_FS_RW | VTD_FS_US | VTD_FS_A; > + > + if (is_leaf) { > + attrs |= VTD_FS_D; > + } > + return attrs; > +} > + > +void qvtd_setup_translation_tables(QTestState *qts, uint64_t iova, > + QVTDTransMode mode) > +{ > + bool is_fl = (mode == QVTD_TM_SCALABLE_FLT); > + uint64_t non_leaf_attrs, leaf_attrs; > + > + if (is_fl) { > + non_leaf_attrs = qvtd_get_fl_pte_attrs(false); > + leaf_attrs = qvtd_get_fl_pte_attrs(true); > + } else { > + /* Second-level: all levels use identical R/W attrs (spec 3.7.1) */ > + non_leaf_attrs = qvtd_get_pte_attrs(); > + leaf_attrs = non_leaf_attrs; > + } > + > + g_test_message("Page table setup: IOVA=0x%" PRIx64 > + " PA=0x%" PRIx64 " %s", > + (uint64_t)iova, (uint64_t)QVTD_PT_VAL, > + is_fl ? "first-level" : "second-level"); > + > + /* PML4 (L4) -> PDPT (L3) -> PD (L2) -> PT (L1) -> PA */ > + qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L4_BASE, 4, iova), > + QVTD_PT_L3_BASE | non_leaf_attrs); > + qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L3_BASE, 3, iova), > + QVTD_PT_L2_BASE | non_leaf_attrs); > + qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L2_BASE, 2, iova), > + QVTD_PT_L1_BASE | non_leaf_attrs); > + qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L1_BASE, 1, iova), > + (QVTD_PT_VAL & VTD_PAGE_MASK_4K) | leaf_attrs); > +} > + > +void qvtd_program_regs(QTestState *qts, uint64_t iommu_base, > + QVTDTransMode mode) > +{ > + uint32_t gcmd = 0; > + uint64_t rtaddr = QVTD_ROOT_TABLE_BASE; > + > + /* Set SMT bit for scalable mode (VT-d spec Section 9.1) */ > + if (qvtd_is_scalable(mode)) { > + rtaddr |= VTD_RTADDR_SMT; > + } > + > + /* Set Root Table Address */ > + qtest_writeq(qts, iommu_base + DMAR_RTADDR_REG, rtaddr); > + > + /* Set Root Table Pointer and verify */ > + gcmd |= VTD_GCMD_SRTP; > + qtest_writel(qts, iommu_base + DMAR_GCMD_REG, gcmd); > + g_assert(qtest_readl(qts, iommu_base + DMAR_GSTS_REG) & VTD_GSTS_RTPS); > + > + /* Setup Invalidation Queue */ > + qtest_writeq(qts, iommu_base + DMAR_IQA_REG, > + QVTD_IQ_BASE | QVTD_IQ_QS); > + qtest_writeq(qts, iommu_base + DMAR_IQH_REG, 0); > + qtest_writeq(qts, iommu_base + DMAR_IQT_REG, 0); > + > + /* Enable Queued Invalidation and verify */ > + gcmd |= VTD_GCMD_QIE; > + qtest_writel(qts, iommu_base + DMAR_GCMD_REG, gcmd); > + g_assert(qtest_readl(qts, iommu_base + DMAR_GSTS_REG) & VTD_GSTS_QIES); > + > + /* Setup Fault Event MSI */ > + qtest_writel(qts, iommu_base + DMAR_FECTL_REG, 0x0); > + qtest_writel(qts, iommu_base + DMAR_FEDATA_REG, QVTD_FAULT_IRQ_DATA); > + qtest_writel(qts, iommu_base + DMAR_FEADDR_REG, QVTD_FAULT_IRQ_ADDR); > + > + /* Enable translation and verify */ > + gcmd |= VTD_GCMD_TE; > + qtest_writel(qts, iommu_base + DMAR_GCMD_REG, gcmd); > + g_assert(qtest_readl(qts, iommu_base + DMAR_GSTS_REG) & VTD_GSTS_TES); > +} > + > +uint32_t qvtd_build_translation(QTestState *qts, QVTDTransMode mode, > + uint16_t sid) > +{ > + uint8_t bus = (sid >> 8) & 0xff; > + > + g_test_message("Build translation: IOVA=0x%" PRIx64 " PA=0x%" PRIx64 > + " mode=%d", > + (uint64_t)QVTD_IOVA, (uint64_t)QVTD_PT_VAL, mode); > + > + /* Clear IOMMU structure regions to avoid stale entries */ > + qtest_memset(qts, QVTD_ROOT_TABLE_BASE, 0, 0x1000); > + qtest_memset(qts, QVTD_PT_L4_BASE, 0, 0x4000); > + > + if (qvtd_is_scalable(mode)) { > + /* Scalable: 32B context entries need 8KB */ > + qtest_memset(qts, QVTD_CONTEXT_TABLE_BASE, 0, 0x2000); > + qtest_memset(qts, QVTD_PASID_DIR_BASE, 0, 0x1000); > + qtest_memset(qts, QVTD_PASID_TABLE_BASE, 0, 0x1000); > + } else { > + qtest_memset(qts, QVTD_CONTEXT_TABLE_BASE, 0, 0x1000); > + } > + > + qvtd_build_root_entry(qts, bus, QVTD_CONTEXT_TABLE_BASE, mode); > + > + if (qvtd_is_scalable(mode)) { > + /* Scalable path: context -> PASID dir -> PASID entry -> page tables */ > + qvtd_build_scalable_context_entry(qts, sid); > + qvtd_build_pasid_dir_entry(qts); > + > + if (mode == QVTD_TM_SCALABLE_PT) { > + qvtd_build_pasid_table_entry(qts, mode, 0); > + } else { > + qvtd_setup_translation_tables(qts, QVTD_IOVA, mode); > + qvtd_build_pasid_table_entry(qts, mode, QVTD_PT_L4_BASE); > + } > + } else { > + /* Legacy path */ > + if (mode == QVTD_TM_LEGACY_PT) { > + qvtd_build_context_entry(qts, sid, mode, 0); > + } else { > + qvtd_setup_translation_tables(qts, QVTD_IOVA, mode); > + qvtd_build_context_entry(qts, sid, mode, QVTD_PT_L4_BASE); > + } > + } > + > + return 0; > +} > + > +uint32_t qvtd_setup_and_enable_translation(QVTDTestContext *ctx) > +{ > + uint32_t build_result; > + > + /* Build translation structures first */ > + build_result = qvtd_build_translation(ctx->qts, ctx->config.trans_mode, > + ctx->sid); > + if (build_result != 0) { > + g_test_message("Build failed: mode=%u sid=%u status=0x%x", > + ctx->config.trans_mode, ctx->sid, build_result); > + ctx->trans_status = build_result; > + return ctx->trans_status; > + } > + > + /* Program IOMMU registers (sets root table pointer, enables translation) */ > + qvtd_program_regs(ctx->qts, ctx->iommu_base, ctx->config.trans_mode); > + > + ctx->trans_status = 0; > + return ctx->trans_status; > +} > + > +static bool qvtd_validate_test_result(QVTDTestContext *ctx) > +{ > + uint32_t expected = qvtd_expected_dma_result(ctx); > + > + g_test_message("-> Validating result: expected=0x%x actual=0x%x", > + expected, ctx->dma_result); > + return (ctx->dma_result == expected); > +} > + > +static uint32_t qvtd_single_translation_setup(void *opaque) > +{ > + return qvtd_setup_and_enable_translation(opaque); > +} > + > +static uint32_t qvtd_single_translation_attrs(void *opaque) > +{ > + return qvtd_build_dma_attrs(); > +} > + > +static bool qvtd_single_translation_validate(void *opaque) > +{ > + return qvtd_validate_test_result(opaque); > +} > + > +static void qvtd_single_translation_report(void *opaque, uint32_t dma_result) > +{ > + QVTDTestContext *ctx = opaque; > + > + if (dma_result != 0) { > + g_test_message("DMA failed: mode=%u result=0x%x", > + ctx->config.trans_mode, dma_result); > + } else { > + g_test_message("-> DMA succeeded: mode=%u", > + ctx->config.trans_mode); > + } > +} > + > +void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev, > + QPCIBar bar, uint64_t iommu_base, > + const QVTDTestConfig *cfg) > +{ > + QVTDTestContext ctx = { > + .qts = qts, > + .dev = dev, > + .bar = bar, > + .iommu_base = iommu_base, > + .config = *cfg, > + .sid = dev->devfn, > + }; > + > + QOSIOMMUTestdevDmaCfg dma = { > + .dev = dev, > + .bar = bar, > + .iova = QVTD_IOVA, > + .gpa = cfg->dma_gpa, > + .len = cfg->dma_len, > + }; > + > + qtest_memset(qts, cfg->dma_gpa, 0x00, cfg->dma_len); > + qos_iommu_testdev_single_translation(&dma, &ctx, > + qvtd_single_translation_setup, > + qvtd_single_translation_attrs, > + qvtd_single_translation_validate, > + qvtd_single_translation_report, > + &ctx.dma_result); > + > + if (ctx.dma_result == 0 && ctx.config.expected_result == 0) { > + g_autofree uint8_t *buf = NULL; > + > + buf = g_malloc(ctx.config.dma_len); > + qtest_memread(ctx.qts, ctx.config.dma_gpa, buf, ctx.config.dma_len); > + > + for (int i = 0; i < ctx.config.dma_len; i++) { > + uint8_t expected; > + > + expected = (ITD_DMA_WRITE_VAL >> ((i % 4) * 8)) & 0xff; > + g_assert_cmpuint(buf[i], ==, expected); > + } > + } > +} > diff --git a/tests/qtest/libqos/qos-intel-iommu.h b/tests/qtest/libqos/qos-intel-iommu.h > new file mode 100644 > index 0000000000..c6cacc5c3f > --- /dev/null > +++ b/tests/qtest/libqos/qos-intel-iommu.h > @@ -0,0 +1,185 @@ > +/* > + * QOS Intel IOMMU (VT-d) Module > + * > + * This module provides Intel IOMMU-specific helper functions for libqos tests, > + * encapsulating VT-d setup, assertion, and cleanup operations. > + * > + * Copyright (c) 2026 Fengyuan Yu <15fengyuan@gmail.com> > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#ifndef QTEST_LIBQOS_INTEL_IOMMU_H > +#define QTEST_LIBQOS_INTEL_IOMMU_H > + > +#include "hw/misc/iommu-testdev.h" > +#include "hw/i386/intel_iommu_internal.h" > + > +/* > + * Guest memory layout for IOMMU structures. > + * All structures are placed in guest physical memory inside the 512MB RAM. > + * Using 256MB mark (0x10000000) as base to ensure all structures fit in RAM. > + */ > +#define QVTD_MEM_BASE 0x10000000ULL > + > +/* Root Entry Table: 256 entries * 16 bytes = 4KB */ > +#define QVTD_ROOT_TABLE_BASE (QVTD_MEM_BASE + 0x00000000) > + > +/* Context Entry Table: 256 entries, 16B (legacy) or 32B (scalable) per entry */ > +#define QVTD_CONTEXT_TABLE_BASE (QVTD_MEM_BASE + 0x00001000) > + > +/* Page Tables: 4-level hierarchy for 48-bit address translation */ > +#define QVTD_PT_L4_BASE (QVTD_MEM_BASE + 0x00010000) /* PML4 */ > +#define QVTD_PT_L3_BASE (QVTD_MEM_BASE + 0x00011000) /* PDPT */ > +#define QVTD_PT_L2_BASE (QVTD_MEM_BASE + 0x00012000) /* PD */ > +#define QVTD_PT_L1_BASE (QVTD_MEM_BASE + 0x00013000) /* PT */ > + > +/* > + * Invalidation Queue. > + * IQA_REG bits[2:0] = QS, entries = 1 << (QS + 8), each entry 16 bytes. > + */ > +#define QVTD_IQ_BASE (QVTD_MEM_BASE + 0x00020000) > +#define QVTD_IQ_QS 0 /* QS=0 → 256 entries */ > + > +/* > + * Fault Event MSI configuration. > + */ > +#define QVTD_FAULT_IRQ_ADDR 0xfee00000 /* APIC base */ > +#define QVTD_FAULT_IRQ_DATA 0x0 > + > +/* Scalable mode PASID structures */ > +#define QVTD_PASID_DIR_BASE (QVTD_MEM_BASE + 0x00030000) > +#define QVTD_PASID_TABLE_BASE (QVTD_MEM_BASE + 0x00031000) > + > +/* Page table entry size (8 bytes per PTE) */ > +#define QVTD_PTE_SIZE sizeof(uint64_t) > + > +/* FSPTPTR mask: same as VTD_SM_PASID_ENTRY_SSPTPTR, bits[63:12] */ > +#define QVTD_SM_PASID_ENTRY_FSPTPTR VTD_SM_PASID_ENTRY_SSPTPTR > + > +/* Default Domain ID for single-domain tests */ > +#define QVTD_DOMAIN_ID 0 > + > +/* Test IOVA and target physical address */ > +#define QVTD_IOVA 0x0000000010200567ull > +#define QVTD_PT_VAL (QVTD_MEM_BASE + 0x00100000) > + > +/* > + * Translation modes supported by Intel IOMMU > + */ > +typedef enum QVTDTransMode { > + QVTD_TM_LEGACY_PT, /* Legacy pass-through mode */ > + QVTD_TM_LEGACY_TRANS, /* Legacy translated mode (4-level paging) */ > + QVTD_TM_SCALABLE_PT, /* Scalable pass-through mode */ > + QVTD_TM_SCALABLE_SLT, /* Scalable Second Level Translation */ > + QVTD_TM_SCALABLE_FLT, /* Scalable First Level Translation */ > +} QVTDTransMode; > + > +static inline bool qvtd_is_scalable(QVTDTransMode mode) > +{ > + return mode == QVTD_TM_SCALABLE_PT || > + mode == QVTD_TM_SCALABLE_SLT || > + mode == QVTD_TM_SCALABLE_FLT; > +} > + > +typedef struct QVTDTestConfig { > + QVTDTransMode trans_mode; /* Translation mode */ > + uint64_t dma_gpa; /* GPA for readback validation */ > + uint32_t dma_len; /* DMA length for testing */ > + uint32_t expected_result; /* Expected DMA result */ > +} QVTDTestConfig; > + > +typedef struct QVTDTestContext { > + QTestState *qts; /* QTest state handle */ > + QPCIDevice *dev; /* PCI device handle */ > + QPCIBar bar; /* PCI BAR for MMIO access */ > + QVTDTestConfig config; /* Test configuration */ > + uint64_t iommu_base; /* Intel IOMMU base address */ > + uint32_t trans_status; /* Translation configuration status */ > + uint32_t dma_result; /* DMA operation result */ > + uint16_t sid; /* Source ID (bus:devfn) */ > +} QVTDTestContext; > + > +/* > + * qvtd_setup_and_enable_translation - Complete translation setup and enable > + * > + * @ctx: Test context containing configuration and device handles > + * > + * Returns: Translation status (0 = success, non-zero = error) > + * > + * This function performs the complete translation setup sequence: > + * 1. Builds VT-d structures (root/context entry, page tables) > + * 2. Programs IOMMU registers and enables translation > + * 3. Returns configuration status > + */ > +uint32_t qvtd_setup_and_enable_translation(QVTDTestContext *ctx); > + > +/* > + * qvtd_build_translation - Build Intel IOMMU translation structures > + * > + * @qts: QTest state handle > + * @mode: Translation mode (pass-through or translated) > + * @sid: Source ID (bus:devfn) > + * > + * Returns: Build status (0 = success, non-zero = error) > + * > + * Constructs all necessary VT-d translation structures in guest memory: > + * - Root Entry for the device's bus > + * - Context Entry for the device > + * - Complete 4-level page table hierarchy (if translated mode) > + */ > +uint32_t qvtd_build_translation(QTestState *qts, QVTDTransMode mode, > + uint16_t sid); > + > +/* > + * qvtd_program_regs - Program Intel IOMMU registers and enable translation > + * > + * @qts: QTest state handle > + * @iommu_base: IOMMU base address > + * @mode: Translation mode (scalable modes set RTADDR SMT bit) > + * > + * Programs IOMMU registers with the following sequence: > + * 1. Set root table pointer (SRTP), with SMT bit for scalable mode > + * 2. Setup invalidation queue (QIE) > + * 3. Configure fault event MSI > + * 4. Enable translation (TE) > + * > + * Each step verifies completion via GSTS register read-back. > + */ > +void qvtd_program_regs(QTestState *qts, uint64_t iommu_base, > + QVTDTransMode mode); > + > +/* > + * qvtd_setup_translation_tables - Setup complete VT-d page table hierarchy > + * > + * @qts: QTest state handle > + * @iova: Input Virtual Address to translate > + * @mode: Translation mode > + * > + * This builds the 4-level page table structure for translating > + * the given IOVA to PA through Intel VT-d. The structure is: > + * - PML4 (Level 4): IOVA bits [47:39] > + * - PDPT (Level 3): IOVA bits [38:30] > + * - PD (Level 2): IOVA bits [29:21] > + * - PT (Level 1): IOVA bits [20:12] > + * - Page offset: IOVA bits [11:0] > + * > + * The function writes all necessary Page Table Entries (PTEs) to guest > + * memory using qtest_writeq(), setting up the complete translation path > + * that the VT-d hardware will traverse during DMA operations. > + */ > +void qvtd_setup_translation_tables(QTestState *qts, uint64_t iova, > + QVTDTransMode mode); > + > +/* Calculate expected DMA result */ > +uint32_t qvtd_expected_dma_result(QVTDTestContext *ctx); > + > +/* Build DMA attributes for Intel VT-d */ > +uint32_t qvtd_build_dma_attrs(void); > + > +/* High-level test execution helpers */ > +void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev, > + QPCIBar bar, uint64_t iommu_base, > + const QVTDTestConfig *cfg); > + > +#endif /* QTEST_LIBQOS_INTEL_IOMMU_H */ Reviewed-by: Fabiano Rosas <farosas@suse.de> ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] tests/qtest/libqos: Add Intel IOMMU helper library 2026-03-25 7:09 ` [PATCH v3 1/2] tests/qtest/libqos: Add Intel IOMMU helper library Fengyuan Yu 2026-03-27 15:39 ` Fabiano Rosas @ 2026-03-30 9:09 ` Tao Tang 1 sibling, 0 replies; 7+ messages in thread From: Tao Tang @ 2026-03-30 9:09 UTC (permalink / raw) To: Fengyuan Yu, Michael S. Tsirkin, Jason Wang, Yi Liu, Clément Mathieu--Drif, Fabiano Rosas, Laurent Vivier, Paolo Bonzini Cc: qemu-devel, Chao Liu Hi Fengyuan, On 2026/3/25 15:09, Fengyuan Yu wrote: > Introduce a libqos helper module for Intel IOMMU (VT-d) bare-metal > testing via iommu-testdev. The helper provides routines to: > > - Build Legacy-mode structures: Root Entry Tables, Context Entry Tables, > and 4-level page tables for 48-bit address translation > - Build Scalable-mode structures: Scalable Context Entries, PASID > Directory Entries, PASID Table Entries, and 4-level page tables for > both second-level and first-level translation > - Program VT-d registers (Root Table Address, Invalidation Queue, > Fault Event MSI, Global Command) following the VT-d specification, > with GSTS read-back verification for each step > - Execute DMA translations through iommu-testdev and verify results > by reading back guest memory > > The module supports all major VT-d translation modes through the > QVTDTransMode enum: > - Legacy pass-through > - Legacy translated with 4-level paging > - Scalable pass-through > - Scalable Second-Level Translation > - Scalable First-Level Translation > > Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com> > Signed-off-by: Fengyuan Yu <15fengyuan@gmail.com> Reviewed-by: Tao Tang <tangtao1634@phytium.com.cn> ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 2/2] tests/qtest: Add Intel IOMMU bare-metal test 2026-03-25 7:09 [PATCH v3 0/2] tests/qtest: Add Intel IOMMU bare-metal test using iommu-testdev Fengyuan Yu 2026-03-25 7:09 ` [PATCH v3 1/2] tests/qtest/libqos: Add Intel IOMMU helper library Fengyuan Yu @ 2026-03-25 7:09 ` Fengyuan Yu 2026-03-27 15:40 ` Fabiano Rosas 2026-03-30 9:11 ` Tao Tang 1 sibling, 2 replies; 7+ messages in thread From: Fengyuan Yu @ 2026-03-25 7:09 UTC (permalink / raw) To: Michael S. Tsirkin, Jason Wang, Yi Liu, Clément Mathieu--Drif, Fabiano Rosas, Laurent Vivier, Paolo Bonzini, Tao Tang Cc: qemu-devel, Chao Liu, Fengyuan Yu 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> --- MAINTAINERS | 1 + tests/qtest/iommu-intel-test.c | 216 +++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 2 + 3 files changed, 219 insertions(+) create mode 100644 tests/qtest/iommu-intel-test.c diff --git a/MAINTAINERS b/MAINTAINERS index ba0901bf4f..420f7ab6cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4016,6 +4016,7 @@ F: hw/i386/intel_iommu_accel.* F: include/hw/i386/intel_iommu.h F: tests/functional/x86_64/test_intel_iommu.py F: tests/qtest/intel-iommu-test.c +F: tests/qtest/iommu-intel-test.c AMD-Vi Emulation M: Alejandro Jimenez <alejandro.j.jimenez@oracle.com> diff --git a/tests/qtest/iommu-intel-test.c b/tests/qtest/iommu-intel-test.c new file mode 100644 index 0000000000..a52c45e298 --- /dev/null +++ b/tests/qtest/iommu-intel-test.c @@ -0,0 +1,216 @@ +/* + * QTest for Intel IOMMU (VT-d) with iommu-testdev + * + * This QTest file is used to test the Intel IOMMU with iommu-testdev so that + * we can test VT-d without any guest kernel or firmware. + * + * Copyright (c) 2026 Fengyuan Yu <15fengyuan@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/pci.h" +#include "libqos/pci-pc.h" +#include "hw/i386/intel_iommu_internal.h" +#include "hw/misc/iommu-testdev.h" +#include "libqos/qos-intel-iommu.h" + +#define DMA_LEN 4 + +static uint64_t intel_iommu_expected_gpa(uint64_t iova) +{ + return (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (iova & 0xfff); +} + +static void save_fn(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice **pdev = (QPCIDevice **) data; + + *pdev = dev; +} + +static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus, + QPCIBar *bar) +{ + QPCIDevice *dev = NULL; + + *pcibus = qpci_new_pc(qts, NULL); + g_assert(*pcibus != NULL); + + qpci_device_foreach(*pcibus, IOMMU_TESTDEV_VENDOR_ID, + IOMMU_TESTDEV_DEVICE_ID, save_fn, &dev); + + g_assert(dev); + qpci_device_enable(dev); + *bar = qpci_iomap(dev, 0, NULL); + g_assert_false(bar->is_io); + + return dev; +} + +static const char *qvtd_iommu_args(QVTDTransMode mode) +{ + switch (mode) { + case QVTD_TM_SCALABLE_FLT: + return "-device intel-iommu,x-scalable-mode=on,x-flts=on "; + case QVTD_TM_SCALABLE_PT: + case QVTD_TM_SCALABLE_SLT: + return "-device intel-iommu,x-scalable-mode=on "; + default: + return "-device intel-iommu "; + } +} + +static bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode) +{ + uint64_t ecap = qtest_readq(qts, + Q35_HOST_BRIDGE_IOMMU_ADDR + DMAR_ECAP_REG); + + /* All scalable modes require SMTS */ + if (qvtd_is_scalable(mode) && !(ecap & VTD_ECAP_SMTS)) { + g_test_skip("ECAP.SMTS not supported"); + return false; + } + + switch (mode) { + case QVTD_TM_SCALABLE_PT: + if (!(ecap & VTD_ECAP_PT)) { + g_test_skip("ECAP.PT not supported"); + return false; + } + break; + case QVTD_TM_SCALABLE_SLT: + if (!(ecap & VTD_ECAP_SSTS)) { + g_test_skip("ECAP.SSTS not supported"); + return false; + } + break; + case QVTD_TM_SCALABLE_FLT: + if (!(ecap & VTD_ECAP_FSTS)) { + g_test_skip("ECAP.FSTS not supported"); + return false; + } + break; + default: + break; + } + + return true; +} + +static void run_intel_iommu_translation(const QVTDTestConfig *cfg) +{ + QTestState *qts; + QPCIBus *pcibus; + QPCIDevice *dev; + QPCIBar bar; + + if (!qtest_has_machine("q35")) { + g_test_skip("q35 machine not available"); + return; + } + + /* Initialize QEMU environment for Intel IOMMU testing */ + qts = qtest_initf("-machine q35 -smp 1 -m 512 -net none " + "%s -device iommu-testdev", + qvtd_iommu_args(cfg->trans_mode)); + + /* Check CAP/ECAP capabilities for required translation mode */ + if (!qvtd_check_caps(qts, cfg->trans_mode)) { + qtest_quit(qts); + return; + } + + /* Setup and configure IOMMU-testdev PCI device */ + dev = setup_qtest_pci_device(qts, &pcibus, &bar); + g_assert(dev); + + g_test_message("### Intel IOMMU translation mode=%d ###", cfg->trans_mode); + qvtd_run_translation_case(qts, dev, bar, Q35_HOST_BRIDGE_IOMMU_ADDR, cfg); + g_free(dev); + qpci_free_pc(pcibus); + qtest_quit(qts); +} + +static void test_intel_iommu_legacy_pt(void) +{ + QVTDTestConfig cfg = { + .trans_mode = QVTD_TM_LEGACY_PT, + .dma_gpa = QVTD_IOVA, /* pass-through: GPA == IOVA */ + .dma_len = DMA_LEN, + .expected_result = 0, + }; + + run_intel_iommu_translation(&cfg); +} + +static void test_intel_iommu_legacy_trans(void) +{ + QVTDTestConfig cfg = { + .trans_mode = QVTD_TM_LEGACY_TRANS, + .dma_gpa = intel_iommu_expected_gpa(QVTD_IOVA), + .dma_len = DMA_LEN, + .expected_result = 0, + }; + + run_intel_iommu_translation(&cfg); +} + +static void test_intel_iommu_scalable_pt(void) +{ + QVTDTestConfig cfg = { + .trans_mode = QVTD_TM_SCALABLE_PT, + .dma_gpa = QVTD_IOVA, /* pass-through: GPA == IOVA */ + .dma_len = DMA_LEN, + .expected_result = 0, + }; + + run_intel_iommu_translation(&cfg); +} + +static void test_intel_iommu_scalable_slt(void) +{ + QVTDTestConfig cfg = { + .trans_mode = QVTD_TM_SCALABLE_SLT, + .dma_gpa = intel_iommu_expected_gpa(QVTD_IOVA), + .dma_len = DMA_LEN, + .expected_result = 0, + }; + + run_intel_iommu_translation(&cfg); +} + +static void test_intel_iommu_scalable_flt(void) +{ + QVTDTestConfig cfg = { + .trans_mode = QVTD_TM_SCALABLE_FLT, + .dma_gpa = intel_iommu_expected_gpa(QVTD_IOVA), + .dma_len = DMA_LEN, + .expected_result = 0, + }; + + run_intel_iommu_translation(&cfg); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* Legacy mode tests */ + qtest_add_func("/iommu-testdev/intel/legacy-pt", + test_intel_iommu_legacy_pt); + qtest_add_func("/iommu-testdev/intel/legacy-trans", + test_intel_iommu_legacy_trans); + + /* Scalable mode tests */ + qtest_add_func("/iommu-testdev/intel/scalable-pt", + test_intel_iommu_scalable_pt); + qtest_add_func("/iommu-testdev/intel/scalable-slt", + test_intel_iommu_scalable_slt); + qtest_add_func("/iommu-testdev/intel/scalable-flt", + test_intel_iommu_scalable_flt); + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index be4fa627b5..264bce9f81 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -96,6 +96,8 @@ qtests_i386 = \ (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_VTD') ? ['intel-iommu-test'] : []) + \ + (config_all_devices.has_key('CONFIG_VTD') and + config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-intel-test'] : []) + \ (host_os != 'windows' and \ config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ (config_all_devices.has_key('CONFIG_PCIE_PORT') and \ -- 2.39.5 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] tests/qtest: Add Intel IOMMU bare-metal test 2026-03-25 7:09 ` [PATCH v3 2/2] tests/qtest: Add Intel IOMMU bare-metal test Fengyuan Yu @ 2026-03-27 15:40 ` Fabiano Rosas 2026-03-30 9:11 ` Tao Tang 1 sibling, 0 replies; 7+ messages in thread From: Fabiano Rosas @ 2026-03-27 15:40 UTC (permalink / raw) To: Fengyuan Yu, Michael S. Tsirkin, Jason Wang, Yi Liu, Clément Mathieu--Drif, Laurent Vivier, Paolo Bonzini, Tao Tang Cc: qemu-devel, Chao Liu, Fengyuan Yu Fengyuan Yu <15fengyuan@gmail.com> writes: > 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> > --- > MAINTAINERS | 1 + > tests/qtest/iommu-intel-test.c | 216 +++++++++++++++++++++++++++++++++ > tests/qtest/meson.build | 2 + > 3 files changed, 219 insertions(+) > create mode 100644 tests/qtest/iommu-intel-test.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index ba0901bf4f..420f7ab6cf 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -4016,6 +4016,7 @@ F: hw/i386/intel_iommu_accel.* > F: include/hw/i386/intel_iommu.h > F: tests/functional/x86_64/test_intel_iommu.py > F: tests/qtest/intel-iommu-test.c > +F: tests/qtest/iommu-intel-test.c > > AMD-Vi Emulation > M: Alejandro Jimenez <alejandro.j.jimenez@oracle.com> > diff --git a/tests/qtest/iommu-intel-test.c b/tests/qtest/iommu-intel-test.c > new file mode 100644 > index 0000000000..a52c45e298 > --- /dev/null > +++ b/tests/qtest/iommu-intel-test.c > @@ -0,0 +1,216 @@ > +/* > + * QTest for Intel IOMMU (VT-d) with iommu-testdev > + * > + * This QTest file is used to test the Intel IOMMU with iommu-testdev so that > + * we can test VT-d without any guest kernel or firmware. > + * > + * Copyright (c) 2026 Fengyuan Yu <15fengyuan@gmail.com> > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#include "qemu/osdep.h" > +#include "libqtest.h" > +#include "libqos/pci.h" > +#include "libqos/pci-pc.h" > +#include "hw/i386/intel_iommu_internal.h" > +#include "hw/misc/iommu-testdev.h" > +#include "libqos/qos-intel-iommu.h" > + > +#define DMA_LEN 4 > + > +static uint64_t intel_iommu_expected_gpa(uint64_t iova) > +{ > + return (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (iova & 0xfff); > +} > + > +static void save_fn(QPCIDevice *dev, int devfn, void *data) > +{ > + QPCIDevice **pdev = (QPCIDevice **) data; > + > + *pdev = dev; > +} > + > +static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus, > + QPCIBar *bar) > +{ > + QPCIDevice *dev = NULL; > + > + *pcibus = qpci_new_pc(qts, NULL); > + g_assert(*pcibus != NULL); > + > + qpci_device_foreach(*pcibus, IOMMU_TESTDEV_VENDOR_ID, > + IOMMU_TESTDEV_DEVICE_ID, save_fn, &dev); > + > + g_assert(dev); > + qpci_device_enable(dev); > + *bar = qpci_iomap(dev, 0, NULL); > + g_assert_false(bar->is_io); > + > + return dev; > +} > + > +static const char *qvtd_iommu_args(QVTDTransMode mode) > +{ > + switch (mode) { > + case QVTD_TM_SCALABLE_FLT: > + return "-device intel-iommu,x-scalable-mode=on,x-flts=on "; > + case QVTD_TM_SCALABLE_PT: > + case QVTD_TM_SCALABLE_SLT: > + return "-device intel-iommu,x-scalable-mode=on "; > + default: > + return "-device intel-iommu "; > + } > +} > + > +static bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode) > +{ > + uint64_t ecap = qtest_readq(qts, > + Q35_HOST_BRIDGE_IOMMU_ADDR + DMAR_ECAP_REG); > + > + /* All scalable modes require SMTS */ > + if (qvtd_is_scalable(mode) && !(ecap & VTD_ECAP_SMTS)) { > + g_test_skip("ECAP.SMTS not supported"); > + return false; > + } > + > + switch (mode) { > + case QVTD_TM_SCALABLE_PT: > + if (!(ecap & VTD_ECAP_PT)) { > + g_test_skip("ECAP.PT not supported"); > + return false; > + } > + break; > + case QVTD_TM_SCALABLE_SLT: > + if (!(ecap & VTD_ECAP_SSTS)) { > + g_test_skip("ECAP.SSTS not supported"); > + return false; > + } > + break; > + case QVTD_TM_SCALABLE_FLT: > + if (!(ecap & VTD_ECAP_FSTS)) { > + g_test_skip("ECAP.FSTS not supported"); > + return false; > + } > + break; > + default: > + break; > + } > + > + return true; > +} > + > +static void run_intel_iommu_translation(const QVTDTestConfig *cfg) > +{ > + QTestState *qts; > + QPCIBus *pcibus; > + QPCIDevice *dev; > + QPCIBar bar; > + > + if (!qtest_has_machine("q35")) { > + g_test_skip("q35 machine not available"); > + return; > + } > + > + /* Initialize QEMU environment for Intel IOMMU testing */ > + qts = qtest_initf("-machine q35 -smp 1 -m 512 -net none " > + "%s -device iommu-testdev", > + qvtd_iommu_args(cfg->trans_mode)); > + > + /* Check CAP/ECAP capabilities for required translation mode */ > + if (!qvtd_check_caps(qts, cfg->trans_mode)) { > + qtest_quit(qts); > + return; > + } > + > + /* Setup and configure IOMMU-testdev PCI device */ > + dev = setup_qtest_pci_device(qts, &pcibus, &bar); > + g_assert(dev); > + > + g_test_message("### Intel IOMMU translation mode=%d ###", cfg->trans_mode); > + qvtd_run_translation_case(qts, dev, bar, Q35_HOST_BRIDGE_IOMMU_ADDR, cfg); > + g_free(dev); > + qpci_free_pc(pcibus); > + qtest_quit(qts); > +} > + > +static void test_intel_iommu_legacy_pt(void) > +{ > + QVTDTestConfig cfg = { > + .trans_mode = QVTD_TM_LEGACY_PT, > + .dma_gpa = QVTD_IOVA, /* pass-through: GPA == IOVA */ > + .dma_len = DMA_LEN, > + .expected_result = 0, > + }; > + > + run_intel_iommu_translation(&cfg); > +} > + > +static void test_intel_iommu_legacy_trans(void) > +{ > + QVTDTestConfig cfg = { > + .trans_mode = QVTD_TM_LEGACY_TRANS, > + .dma_gpa = intel_iommu_expected_gpa(QVTD_IOVA), > + .dma_len = DMA_LEN, > + .expected_result = 0, > + }; > + > + run_intel_iommu_translation(&cfg); > +} > + > +static void test_intel_iommu_scalable_pt(void) > +{ > + QVTDTestConfig cfg = { > + .trans_mode = QVTD_TM_SCALABLE_PT, > + .dma_gpa = QVTD_IOVA, /* pass-through: GPA == IOVA */ > + .dma_len = DMA_LEN, > + .expected_result = 0, > + }; > + > + run_intel_iommu_translation(&cfg); > +} > + > +static void test_intel_iommu_scalable_slt(void) > +{ > + QVTDTestConfig cfg = { > + .trans_mode = QVTD_TM_SCALABLE_SLT, > + .dma_gpa = intel_iommu_expected_gpa(QVTD_IOVA), > + .dma_len = DMA_LEN, > + .expected_result = 0, > + }; > + > + run_intel_iommu_translation(&cfg); > +} > + > +static void test_intel_iommu_scalable_flt(void) > +{ > + QVTDTestConfig cfg = { > + .trans_mode = QVTD_TM_SCALABLE_FLT, > + .dma_gpa = intel_iommu_expected_gpa(QVTD_IOVA), > + .dma_len = DMA_LEN, > + .expected_result = 0, > + }; > + > + run_intel_iommu_translation(&cfg); > +} > + > +int main(int argc, char **argv) > +{ > + g_test_init(&argc, &argv, NULL); > + > + /* Legacy mode tests */ > + qtest_add_func("/iommu-testdev/intel/legacy-pt", > + test_intel_iommu_legacy_pt); > + qtest_add_func("/iommu-testdev/intel/legacy-trans", > + test_intel_iommu_legacy_trans); > + > + /* Scalable mode tests */ > + qtest_add_func("/iommu-testdev/intel/scalable-pt", > + test_intel_iommu_scalable_pt); > + qtest_add_func("/iommu-testdev/intel/scalable-slt", > + test_intel_iommu_scalable_slt); > + qtest_add_func("/iommu-testdev/intel/scalable-flt", > + test_intel_iommu_scalable_flt); > + > + return g_test_run(); > +} > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build > index be4fa627b5..264bce9f81 100644 > --- a/tests/qtest/meson.build > +++ b/tests/qtest/meson.build > @@ -96,6 +96,8 @@ qtests_i386 = \ > (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_VTD') ? ['intel-iommu-test'] : []) + \ > + (config_all_devices.has_key('CONFIG_VTD') and > + config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-intel-test'] : []) + \ > (host_os != 'windows' and \ > config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ > (config_all_devices.has_key('CONFIG_PCIE_PORT') and \ Reviewed-by: Fabiano Rosas <farosas@suse.de> ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] tests/qtest: Add Intel IOMMU bare-metal test 2026-03-25 7:09 ` [PATCH v3 2/2] tests/qtest: Add Intel IOMMU bare-metal test Fengyuan Yu 2026-03-27 15:40 ` Fabiano Rosas @ 2026-03-30 9:11 ` Tao Tang 1 sibling, 0 replies; 7+ messages in thread From: Tao Tang @ 2026-03-30 9:11 UTC (permalink / raw) To: Fengyuan Yu, Michael S. Tsirkin, Jason Wang, Yi Liu, Clément Mathieu--Drif, Fabiano Rosas, Laurent Vivier, Paolo Bonzini Cc: qemu-devel, Chao Liu Hi Fengyuan, On 2026/3/25 15:09, Fengyuan Yu wrote: > 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: Tao Tang <tangtao1634@phytium.com.cn> ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-03-30 9:11 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-25 7:09 [PATCH v3 0/2] tests/qtest: Add Intel IOMMU bare-metal test using iommu-testdev Fengyuan Yu 2026-03-25 7:09 ` [PATCH v3 1/2] tests/qtest/libqos: Add Intel IOMMU helper library Fengyuan Yu 2026-03-27 15:39 ` Fabiano Rosas 2026-03-30 9:09 ` Tao Tang 2026-03-25 7:09 ` [PATCH v3 2/2] tests/qtest: Add Intel IOMMU bare-metal test Fengyuan Yu 2026-03-27 15:40 ` Fabiano Rosas 2026-03-30 9:11 ` Tao Tang
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.