Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2215710/?format=api
{ "id": 2215710, "url": "http://patchwork.ozlabs.org/api/patches/2215710/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/c4f7bf5d7985891a2db291193669ebe15dd2ba15.1774421649.git.15fengyuan@gmail.com/", "project": { "id": 14, "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api", "name": "QEMU Development", "link_name": "qemu-devel", "list_id": "qemu-devel.nongnu.org", "list_email": "qemu-devel@nongnu.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<c4f7bf5d7985891a2db291193669ebe15dd2ba15.1774421649.git.15fengyuan@gmail.com>", "list_archive_url": null, "date": "2026-03-25T07:09:05", "name": "[v3,1/2] tests/qtest/libqos: Add Intel IOMMU helper library", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "7f47fa6cd82fcc94816207343ee83c230252a168", "submitter": { "id": 92576, "url": "http://patchwork.ozlabs.org/api/people/92576/?format=api", "name": "Fengyuan Yu", "email": "15fengyuan@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/c4f7bf5d7985891a2db291193669ebe15dd2ba15.1774421649.git.15fengyuan@gmail.com/mbox/", "series": [ { "id": 497387, "url": "http://patchwork.ozlabs.org/api/series/497387/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=497387", "date": "2026-03-25T07:09:04", "name": "tests/qtest: Add Intel IOMMU bare-metal test using iommu-testdev", "version": 3, "mbox": "http://patchwork.ozlabs.org/series/497387/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2215710/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2215710/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=HCzlYA86;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)" ], "Received": [ "from lists.gnu.org (lists.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fgdN12X6xz1xy3\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 25 Mar 2026 18:10:25 +1100 (AEDT)", "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1w5IN7-0005Dt-Uu; Wed, 25 Mar 2026 03:09:45 -0400", "from eggs.gnu.org ([2001:470:142:3::10])\n by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <15fengyuan@gmail.com>)\n id 1w5IN5-0005De-BN\n for qemu-devel@nongnu.org; Wed, 25 Mar 2026 03:09:43 -0400", "from mail-pj1-x1032.google.com ([2607:f8b0:4864:20::1032])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)\n (Exim 4.90_1) (envelope-from <15fengyuan@gmail.com>)\n id 1w5IN2-0001M1-5x\n for qemu-devel@nongnu.org; Wed, 25 Mar 2026 03:09:42 -0400", "by mail-pj1-x1032.google.com with SMTP id\n 98e67ed59e1d1-359fea895b5so1335429a91.0\n for <qemu-devel@nongnu.org>; Wed, 25 Mar 2026 00:09:39 -0700 (PDT)", "from orion-o6.tail020997.ts.net ([2408:8352:441:f661::1002])\n by smtp.gmail.com with ESMTPSA id\n 98e67ed59e1d1-35c031354f3sm5328074a91.6.2026.03.25.00.09.34\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Wed, 25 Mar 2026 00:09:37 -0700 (PDT)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1774422578; x=1775027378; darn=nongnu.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=oHvo8TXfdGAaP2q5C1aF3pbGkSdGqW64J3l25llGnTI=;\n b=HCzlYA860Vr4WcaDwhEBi7InanBn/xbVkPgNxBBZkMKjSB+NSpCk24C+kHkg/pBNZt\n SUGG5rsNkmGiHNW/Ys3rtsTxjaBN2aHc0553mKuxoxTezC1clhsIcf9wZOEUUiu2xcUH\n JafSNTPbL5fFBGOOWhgDOrtnGUhST9RbScMOvvQU62JvKHIcMiwzkXGYNNGExfGlnLII\n 6CUc55S207qOo94XH7Xj64tO0sPl/Q8Z/TK92ywNTvzbIjZdNY6B7wvBpZj2FXWrytP5\n H/kxkAxzCk6G2y3KnmRQLHmZRvbqx8LPCMa52qkjN2WiPTnUrIO0RgIc9K9Mpmg+p0tu\n 1+MA==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1774422578; x=1775027378;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=oHvo8TXfdGAaP2q5C1aF3pbGkSdGqW64J3l25llGnTI=;\n b=TmNw4qENZ1CKPGhgVEuuB2iHH0Prk24xVPyeBT263/ndmmFly+P8r+1fjajGg/5IPj\n gJHSU8K3szjqzLaHqgWzFvA2xqGwvyn7cjIDXJznBIX5439+b4xaIVEXOCIJ70x/nmBY\n UNCsk6c+rrzpqrFEUO69GGLvwRTPJ6krof1JHvdQ+jezKJeuAqYmlWA9bXwnmvYYXV2s\n cTR3D9mwsDB+YtGSS9ChPgj+qaoEgXxc/qtMunpx3tg5K0x4qKaxu6S9ViLEyX6abTSf\n H/Vx2o5BkEq7YFHdBnB9ko5Tl6HMmqc8xQazDuIYj72kPclBedP7i6nCB2b8d+fQwb+y\n SFyw==", "X-Gm-Message-State": "AOJu0YztEldrjD7Cys+NzUSTA9NO+FXHR/DO8DITDukTSRoD7/N+Hm4P\n wAhJzJeHNxsNrApzmEgpzRIbnStdTTK36YhPCBwEyuIwF6b2eKv/iFxm", "X-Gm-Gg": "ATEYQzz4FcgXGjAlAkqn+KF1ggQt72qZ1NW+m9iuQdOXzdgxBAOoa2A8rSEHHoEhVu8\n xA9MlCIE0gWO/m/+IW40uDPbrSMpZ5K+N9p3EMBHhOujbMQDBvRzJ3g9W9/e72psff5tSVeXH/d\n /o/OL+Vso4pyWODA2vI9nDtO+KmdLOV/sSGOfgeGvVaoTJXrwjsR/lKQ8dOP+j/S7fyrFmrxyh7\n 23byHtqmy/mIPEGIVpN2RoZ/kq8UZiwvB5wN52wrFaMWIFZsoT1VNYa4vxeUnjRcNR9lr+z6U5l\n ineRGTQLDFWsACVS7DgsOuPho4SJ+enMTekdH/HTZjDkeEBn8jf1lBe6yRfsPdwS7Uwirjo/TXL\n h7EkGid6+2Oydex6v1q+kzkh3frgYtwTvrX4xN2x3dl1noNr/uSx2ZKYxPpHrzeLtzIP/dsaWwd\n lqffITOhnNJCte/AYtAqs6IbfQCOc=", "X-Received": "by 2002:a17:90b:2b4f:b0:35b:99f8:966d with SMTP id\n 98e67ed59e1d1-35c0ddc3f25mr2036642a91.24.1774422578019;\n Wed, 25 Mar 2026 00:09:38 -0700 (PDT)", "From": "Fengyuan Yu <15fengyuan@gmail.com>", "To": "\"Michael S. Tsirkin\" <mst@redhat.com>, Jason Wang <jasowang@redhat.com>,\n Yi Liu <yi.l.liu@intel.com>,\n =?utf-8?q?Cl=C3=A9ment_Mathieu--Drif?= <clement.mathieu--drif@bull.com>,\n Fabiano Rosas <farosas@suse.de>, Laurent Vivier <lvivier@redhat.com>,\n Paolo Bonzini <pbonzini@redhat.com>, Tao Tang <tangtao1634@phytium.com.cn>", "Cc": "qemu-devel@nongnu.org, Chao Liu <chao.liu.zevorn@gmail.com>,\n Fengyuan Yu <15fengyuan@gmail.com>", "Subject": "[PATCH v3 1/2] tests/qtest/libqos: Add Intel IOMMU helper library", "Date": "Wed, 25 Mar 2026 15:09:05 +0800", "Message-Id": "\n <c4f7bf5d7985891a2db291193669ebe15dd2ba15.1774421649.git.15fengyuan@gmail.com>", "X-Mailer": "git-send-email 2.39.5", "In-Reply-To": "<cover.1774421649.git.15fengyuan@gmail.com>", "References": "<cover.1774421649.git.15fengyuan@gmail.com>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "Received-SPF": "pass client-ip=2607:f8b0:4864:20::1032;\n envelope-from=15fengyuan@gmail.com; helo=mail-pj1-x1032.google.com", "X-Spam_score_int": "-20", "X-Spam_score": "-2.1", "X-Spam_bar": "--", "X-Spam_report": "(-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,\n DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001,\n RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001,\n SPF_PASS=-0.001 autolearn=ham autolearn_force=no", "X-Spam_action": "no action", "X-BeenThere": "qemu-devel@nongnu.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "qemu development <qemu-devel.nongnu.org>", "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>", "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>", "List-Post": "<mailto:qemu-devel@nongnu.org>", "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>", "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>", "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org", "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org" }, "content": "Introduce a libqos helper module for Intel IOMMU (VT-d) bare-metal\ntesting via iommu-testdev. The helper provides routines to:\n\n- Build Legacy-mode structures: Root Entry Tables, Context Entry Tables,\n and 4-level page tables for 48-bit address translation\n- Build Scalable-mode structures: Scalable Context Entries, PASID\n Directory Entries, PASID Table Entries, and 4-level page tables for\n both second-level and first-level translation\n- Program VT-d registers (Root Table Address, Invalidation Queue,\n Fault Event MSI, Global Command) following the VT-d specification,\n with GSTS read-back verification for each step\n- Execute DMA translations through iommu-testdev and verify results\n by reading back guest memory\n\nThe module supports all major VT-d translation modes through the\nQVTDTransMode enum:\n- Legacy pass-through\n- Legacy translated with 4-level paging\n- Scalable pass-through\n- Scalable Second-Level Translation\n- Scalable First-Level Translation\n\nReviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>\nSigned-off-by: Fengyuan Yu <15fengyuan@gmail.com>\n---\n MAINTAINERS | 1 +\n tests/qtest/libqos/meson.build | 3 +\n tests/qtest/libqos/qos-intel-iommu.c | 454 +++++++++++++++++++++++++++\n tests/qtest/libqos/qos-intel-iommu.h | 185 +++++++++++\n 4 files changed, 643 insertions(+)\n create mode 100644 tests/qtest/libqos/qos-intel-iommu.c\n create mode 100644 tests/qtest/libqos/qos-intel-iommu.h", "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex cd8ba14450..ba0901bf4f 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -3606,6 +3606,7 @@ S: Maintained\n F: tests/qtest/libqos/qos-iommu*\n F: tests/qtest/libqos/qos-smmuv3*\n F: tests/qtest/libqos/qos-riscv-iommu*\n+F: tests/qtest/libqos/qos-intel-iommu*\n \n Device Fuzzing\n M: Alexander Bulekov <alxndr@bu.edu>\ndiff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build\nindex 4a69acad0d..96f2fc48b4 100644\n--- a/tests/qtest/libqos/meson.build\n+++ b/tests/qtest/libqos/meson.build\n@@ -73,6 +73,9 @@ endif\n if config_all_devices.has_key('CONFIG_RISCV_IOMMU')\n libqos_srcs += files('riscv-iommu.c', 'qos-riscv-iommu.c')\n endif\n+if config_all_devices.has_key('CONFIG_VTD')\n+ libqos_srcs += files('qos-intel-iommu.c')\n+endif\n if config_all_devices.has_key('CONFIG_TPCI200')\n libqos_srcs += files('tpci200.c')\n endif\ndiff --git a/tests/qtest/libqos/qos-intel-iommu.c b/tests/qtest/libqos/qos-intel-iommu.c\nnew file mode 100644\nindex 0000000000..f8ca4c871b\n--- /dev/null\n+++ b/tests/qtest/libqos/qos-intel-iommu.c\n@@ -0,0 +1,454 @@\n+/*\n+ * QOS Intel IOMMU (VT-d) Module Implementation\n+ *\n+ * This module provides Intel IOMMU-specific helper functions for libqos tests.\n+ *\n+ * Copyright (c) 2026 Fengyuan Yu <15fengyuan@gmail.com>\n+ *\n+ * SPDX-License-Identifier: GPL-2.0-or-later\n+ */\n+\n+#include \"qemu/osdep.h\"\n+#include \"hw/i386/intel_iommu_internal.h\"\n+#include \"tests/qtest/libqos/pci.h\"\n+#include \"qos-iommu-testdev.h\"\n+#include \"qos-intel-iommu.h\"\n+\n+#define QVTD_AW_48BIT_ENCODING 2\n+\n+uint32_t qvtd_expected_dma_result(QVTDTestContext *ctx)\n+{\n+ return ctx->config.expected_result;\n+}\n+\n+uint32_t qvtd_build_dma_attrs(void)\n+{\n+ /*\n+ * VT-d obtains the Requester ID (Source ID) from PCI bus/devfn routing\n+ * via pci_device_iommu_address_space(), not from DMA attributes.\n+ *\n+ * For scalable mode, iommu-testdev does not set MemTxAttrs.pid,\n+ * so the device's VTDAddressSpace has pasid=PCI_NO_PASID.\n+ * vtd_do_iommu_translate() remaps PCI_NO_PASID to PASID_0\n+ * when root_scalable is set, which matches the PASID=0 entry\n+ * we configure in qvtd_build_pasid_table_entry().\n+ */\n+ return 0;\n+}\n+\n+static void qvtd_build_root_entry(QTestState *qts, uint8_t bus,\n+ uint64_t context_table_ptr,\n+ QVTDTransMode mode)\n+{\n+ uint64_t root_entry_addr = QVTD_ROOT_TABLE_BASE +\n+ (bus * sizeof(VTDRootEntry));\n+ uint64_t lo, hi;\n+\n+ if (qvtd_is_scalable(mode)) {\n+ /*\n+ * Scalable-mode Root Entry (Section 9.2):\n+ * lo = Lower Context Table Pointer + LP (Lower Present)\n+ * hi = Upper Context Table Pointer + UP (Upper Present)\n+ *\n+ * Lower table covers devfn 0-127, Upper covers devfn 128-255.\n+ * Only lower half is needed for test device (devfn < 128).\n+ */\n+ lo = (context_table_ptr & VTD_ROOT_ENTRY_CTP) | VTD_ROOT_ENTRY_P;\n+ hi = 0; /* UP=0: upper context table not present */\n+ } else {\n+ /*\n+ * Legacy Root Entry (Section 9.1):\n+ * lo = Context Table Pointer + Present\n+ * hi = Reserved\n+ */\n+ lo = (context_table_ptr & VTD_ROOT_ENTRY_CTP) | VTD_ROOT_ENTRY_P;\n+ hi = 0;\n+ }\n+\n+ qtest_writeq(qts, root_entry_addr, lo);\n+ qtest_writeq(qts, root_entry_addr + 8, hi);\n+}\n+\n+static void qvtd_build_context_entry(QTestState *qts, uint16_t sid,\n+ QVTDTransMode mode, uint64_t ssptptr)\n+{\n+ uint8_t devfn = sid & 0xff;\n+ uint64_t context_entry_addr = QVTD_CONTEXT_TABLE_BASE +\n+ (devfn * VTD_CTX_ENTRY_LEGACY_SIZE);\n+ uint64_t lo, hi;\n+\n+ if (mode == QVTD_TM_LEGACY_PT) {\n+ /*\n+ * Pass-through mode (Section 9.3):\n+ * lo: P + FPD(=0, fault enabled) + TT(=Pass-through)\n+ * hi: DID + AW\n+ */\n+ lo = VTD_CONTEXT_ENTRY_P | VTD_CONTEXT_TT_PASS_THROUGH;\n+ hi = ((uint64_t)QVTD_DOMAIN_ID << 8) | QVTD_AW_48BIT_ENCODING;\n+ } else {\n+ /*\n+ * Translated mode (Section 9.3):\n+ * lo: P + FPD(=0, fault enabled) + TT(=Multi-level) + SSPTPTR\n+ * hi: DID + AW(=48-bit, 4-level)\n+ */\n+ lo = VTD_CONTEXT_ENTRY_P | VTD_CONTEXT_TT_MULTI_LEVEL |\n+ (ssptptr & VTD_CONTEXT_ENTRY_SSPTPTR);\n+ hi = ((uint64_t)QVTD_DOMAIN_ID << 8) | QVTD_AW_48BIT_ENCODING;\n+ }\n+\n+ qtest_writeq(qts, context_entry_addr, lo);\n+ qtest_writeq(qts, context_entry_addr + 8, hi);\n+}\n+\n+static void qvtd_build_scalable_context_entry(QTestState *qts, uint16_t sid)\n+{\n+ uint8_t devfn = sid & 0xff;\n+ uint64_t ce_addr = QVTD_CONTEXT_TABLE_BASE +\n+ (devfn * VTD_CTX_ENTRY_SCALABLE_SIZE);\n+\n+ /*\n+ * Scalable-Mode Context Entry (Section 9.4), 32 bytes = 4 qwords:\n+ *\n+ * val[0]: P + FPD(=0) + DTE(=0) + PASIDE(=0) + PRE(=0) + HPTE(=0)\n+ * + EPTR(=0) + PDTS(=0) + PASIDDIRPTR\n+ * val[1]: RID_PASID(=0) + PDTTE(=0) + PRE(=0) + RID_CG(=0)\n+ * val[2]: Reserved (must be 0)\n+ * val[3]: Reserved (must be 0)\n+ */\n+ qtest_writeq(qts, ce_addr,\n+ (QVTD_PASID_DIR_BASE & VTD_PASID_DIR_BASE_ADDR_MASK) |\n+ VTD_CONTEXT_ENTRY_P);\n+ qtest_writeq(qts, ce_addr + 8, 0);\n+ qtest_writeq(qts, ce_addr + 16, 0);\n+ qtest_writeq(qts, ce_addr + 24, 0);\n+}\n+\n+static void qvtd_build_pasid_dir_entry(QTestState *qts)\n+{\n+ uint64_t addr = QVTD_PASID_DIR_BASE +\n+ VTD_PASID_DIR_INDEX(0) * VTD_PASID_DIR_ENTRY_SIZE;\n+\n+ /*\n+ * PASID Directory Entry (Section 9.5):\n+ * P + FPD(=0, fault enabled) + SMPTBLPTR\n+ */\n+ qtest_writeq(qts, addr,\n+ (QVTD_PASID_TABLE_BASE & VTD_PASID_TABLE_BASE_ADDR_MASK) |\n+ VTD_PASID_ENTRY_P);\n+}\n+\n+static void qvtd_build_pasid_table_entry(QTestState *qts, QVTDTransMode mode,\n+ uint64_t ptptr)\n+{\n+ uint64_t addr = QVTD_PASID_TABLE_BASE +\n+ VTD_PASID_TABLE_INDEX(0) * VTD_PASID_ENTRY_SIZE;\n+ uint64_t val0, val1, val2;\n+\n+ /*\n+ * Scalable-Mode PASID Table Entry (Section 9.6), 64 bytes = 8 qwords:\n+ *\n+ * val[0]: P + FPD(=0) + AW + PGTT + SSADE(=0) + SSPTPTR\n+ * val[1]: DID + PWSNP(=0) + PGSNP(=0)\n+ * + CD(=0) + EMTE(=0) + PAT(=0): Memory Type,\n+ * all Reserved(0) since QEMU ECAP.MTS=0\n+ * val[2]: SRE(=0) + FSPM(=0, 4-level) + WPE(=0) + IGN + EAFE(=0) + FSPTPTR\n+ * val[3]: Reserved (must be 0)\n+ * val[4]: HPT fields, Reserved(0) since QEMU ECAP.HPTS=0\n+ * val[5]: HPT fields, Reserved(0) since QEMU ECAP.HPTS=0\n+ * val[6]: Reserved (must be 0)\n+ * val[7]: Reserved (must be 0)\n+ */\n+ switch (mode) {\n+ case QVTD_TM_SCALABLE_PT:\n+ val0 = VTD_PASID_ENTRY_P |\n+ ((uint64_t)VTD_SM_PASID_ENTRY_PT << 6);\n+ val1 = (uint64_t)QVTD_DOMAIN_ID;\n+ val2 = 0;\n+ break;\n+ case QVTD_TM_SCALABLE_SLT:\n+ val0 = VTD_PASID_ENTRY_P |\n+ ((uint64_t)VTD_SM_PASID_ENTRY_SST << 6) |\n+ ((uint64_t)QVTD_AW_48BIT_ENCODING << 2) |\n+ (ptptr & VTD_SM_PASID_ENTRY_SSPTPTR);\n+ val1 = (uint64_t)QVTD_DOMAIN_ID;\n+ val2 = 0;\n+ break;\n+ case QVTD_TM_SCALABLE_FLT:\n+ /*\n+ * val[2] fields for FLT (Section 9.6):\n+ * SRE(=0, user-level DMA only) + FSPM(=0, 4-level) +\n+ * WPE(=0, no supervisor write-protect) + IGN + EAFE(=0) + FSPTPTR\n+ */\n+ val0 = VTD_PASID_ENTRY_P |\n+ ((uint64_t)VTD_SM_PASID_ENTRY_FST << 6);\n+ val1 = (uint64_t)QVTD_DOMAIN_ID;\n+ val2 = ptptr & QVTD_SM_PASID_ENTRY_FSPTPTR;\n+ break;\n+ default:\n+ g_assert_not_reached();\n+ }\n+\n+ qtest_writeq(qts, addr, val0);\n+ qtest_writeq(qts, addr + 8, val1);\n+ qtest_writeq(qts, addr + 16, val2);\n+ qtest_writeq(qts, addr + 24, 0);\n+ qtest_writeq(qts, addr + 32, 0);\n+ qtest_writeq(qts, addr + 40, 0);\n+ qtest_writeq(qts, addr + 48, 0);\n+ qtest_writeq(qts, addr + 56, 0);\n+}\n+\n+/*\n+ * VT-d second-level paging helpers.\n+ * 4-level, 48-bit address space, 9 bits per level index.\n+ */\n+static uint32_t qvtd_get_table_index(uint64_t iova, int level)\n+{\n+ int shift = VTD_PAGE_SHIFT + VTD_LEVEL_BITS * (level - 1);\n+\n+ return (iova >> shift) & ((1u << VTD_LEVEL_BITS) - 1);\n+}\n+\n+static uint64_t qvtd_get_table_addr(uint64_t base, int level, uint64_t iova)\n+{\n+ return base + (qvtd_get_table_index(iova, level) * QVTD_PTE_SIZE);\n+}\n+\n+static uint64_t qvtd_get_pte_attrs(void)\n+{\n+ /* Second-level: R/W in every paging entry (Section 3.7.1) */\n+ return VTD_SS_R | VTD_SS_W;\n+}\n+\n+static uint64_t qvtd_get_fl_pte_attrs(bool is_leaf)\n+{\n+ /* First-level: x86 page table format (VT-d spec Section 9.9) */\n+ uint64_t attrs = VTD_FS_P | VTD_FS_RW | VTD_FS_US | VTD_FS_A;\n+\n+ if (is_leaf) {\n+ attrs |= VTD_FS_D;\n+ }\n+ return attrs;\n+}\n+\n+void qvtd_setup_translation_tables(QTestState *qts, uint64_t iova,\n+ QVTDTransMode mode)\n+{\n+ bool is_fl = (mode == QVTD_TM_SCALABLE_FLT);\n+ uint64_t non_leaf_attrs, leaf_attrs;\n+\n+ if (is_fl) {\n+ non_leaf_attrs = qvtd_get_fl_pte_attrs(false);\n+ leaf_attrs = qvtd_get_fl_pte_attrs(true);\n+ } else {\n+ /* Second-level: all levels use identical R/W attrs (spec 3.7.1) */\n+ non_leaf_attrs = qvtd_get_pte_attrs();\n+ leaf_attrs = non_leaf_attrs;\n+ }\n+\n+ g_test_message(\"Page table setup: IOVA=0x%\" PRIx64\n+ \" PA=0x%\" PRIx64 \" %s\",\n+ (uint64_t)iova, (uint64_t)QVTD_PT_VAL,\n+ is_fl ? \"first-level\" : \"second-level\");\n+\n+ /* PML4 (L4) -> PDPT (L3) -> PD (L2) -> PT (L1) -> PA */\n+ qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L4_BASE, 4, iova),\n+ QVTD_PT_L3_BASE | non_leaf_attrs);\n+ qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L3_BASE, 3, iova),\n+ QVTD_PT_L2_BASE | non_leaf_attrs);\n+ qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L2_BASE, 2, iova),\n+ QVTD_PT_L1_BASE | non_leaf_attrs);\n+ qtest_writeq(qts, qvtd_get_table_addr(QVTD_PT_L1_BASE, 1, iova),\n+ (QVTD_PT_VAL & VTD_PAGE_MASK_4K) | leaf_attrs);\n+}\n+\n+void qvtd_program_regs(QTestState *qts, uint64_t iommu_base,\n+ QVTDTransMode mode)\n+{\n+ uint32_t gcmd = 0;\n+ uint64_t rtaddr = QVTD_ROOT_TABLE_BASE;\n+\n+ /* Set SMT bit for scalable mode (VT-d spec Section 9.1) */\n+ if (qvtd_is_scalable(mode)) {\n+ rtaddr |= VTD_RTADDR_SMT;\n+ }\n+\n+ /* Set Root Table Address */\n+ qtest_writeq(qts, iommu_base + DMAR_RTADDR_REG, rtaddr);\n+\n+ /* Set Root Table Pointer and verify */\n+ gcmd |= VTD_GCMD_SRTP;\n+ qtest_writel(qts, iommu_base + DMAR_GCMD_REG, gcmd);\n+ g_assert(qtest_readl(qts, iommu_base + DMAR_GSTS_REG) & VTD_GSTS_RTPS);\n+\n+ /* Setup Invalidation Queue */\n+ qtest_writeq(qts, iommu_base + DMAR_IQA_REG,\n+ QVTD_IQ_BASE | QVTD_IQ_QS);\n+ qtest_writeq(qts, iommu_base + DMAR_IQH_REG, 0);\n+ qtest_writeq(qts, iommu_base + DMAR_IQT_REG, 0);\n+\n+ /* Enable Queued Invalidation and verify */\n+ gcmd |= VTD_GCMD_QIE;\n+ qtest_writel(qts, iommu_base + DMAR_GCMD_REG, gcmd);\n+ g_assert(qtest_readl(qts, iommu_base + DMAR_GSTS_REG) & VTD_GSTS_QIES);\n+\n+ /* Setup Fault Event MSI */\n+ qtest_writel(qts, iommu_base + DMAR_FECTL_REG, 0x0);\n+ qtest_writel(qts, iommu_base + DMAR_FEDATA_REG, QVTD_FAULT_IRQ_DATA);\n+ qtest_writel(qts, iommu_base + DMAR_FEADDR_REG, QVTD_FAULT_IRQ_ADDR);\n+\n+ /* Enable translation and verify */\n+ gcmd |= VTD_GCMD_TE;\n+ qtest_writel(qts, iommu_base + DMAR_GCMD_REG, gcmd);\n+ g_assert(qtest_readl(qts, iommu_base + DMAR_GSTS_REG) & VTD_GSTS_TES);\n+}\n+\n+uint32_t qvtd_build_translation(QTestState *qts, QVTDTransMode mode,\n+ uint16_t sid)\n+{\n+ uint8_t bus = (sid >> 8) & 0xff;\n+\n+ g_test_message(\"Build translation: IOVA=0x%\" PRIx64 \" PA=0x%\" PRIx64\n+ \" mode=%d\",\n+ (uint64_t)QVTD_IOVA, (uint64_t)QVTD_PT_VAL, mode);\n+\n+ /* Clear IOMMU structure regions to avoid stale entries */\n+ qtest_memset(qts, QVTD_ROOT_TABLE_BASE, 0, 0x1000);\n+ qtest_memset(qts, QVTD_PT_L4_BASE, 0, 0x4000);\n+\n+ if (qvtd_is_scalable(mode)) {\n+ /* Scalable: 32B context entries need 8KB */\n+ qtest_memset(qts, QVTD_CONTEXT_TABLE_BASE, 0, 0x2000);\n+ qtest_memset(qts, QVTD_PASID_DIR_BASE, 0, 0x1000);\n+ qtest_memset(qts, QVTD_PASID_TABLE_BASE, 0, 0x1000);\n+ } else {\n+ qtest_memset(qts, QVTD_CONTEXT_TABLE_BASE, 0, 0x1000);\n+ }\n+\n+ qvtd_build_root_entry(qts, bus, QVTD_CONTEXT_TABLE_BASE, mode);\n+\n+ if (qvtd_is_scalable(mode)) {\n+ /* Scalable path: context -> PASID dir -> PASID entry -> page tables */\n+ qvtd_build_scalable_context_entry(qts, sid);\n+ qvtd_build_pasid_dir_entry(qts);\n+\n+ if (mode == QVTD_TM_SCALABLE_PT) {\n+ qvtd_build_pasid_table_entry(qts, mode, 0);\n+ } else {\n+ qvtd_setup_translation_tables(qts, QVTD_IOVA, mode);\n+ qvtd_build_pasid_table_entry(qts, mode, QVTD_PT_L4_BASE);\n+ }\n+ } else {\n+ /* Legacy path */\n+ if (mode == QVTD_TM_LEGACY_PT) {\n+ qvtd_build_context_entry(qts, sid, mode, 0);\n+ } else {\n+ qvtd_setup_translation_tables(qts, QVTD_IOVA, mode);\n+ qvtd_build_context_entry(qts, sid, mode, QVTD_PT_L4_BASE);\n+ }\n+ }\n+\n+ return 0;\n+}\n+\n+uint32_t qvtd_setup_and_enable_translation(QVTDTestContext *ctx)\n+{\n+ uint32_t build_result;\n+\n+ /* Build translation structures first */\n+ build_result = qvtd_build_translation(ctx->qts, ctx->config.trans_mode,\n+ ctx->sid);\n+ if (build_result != 0) {\n+ g_test_message(\"Build failed: mode=%u sid=%u status=0x%x\",\n+ ctx->config.trans_mode, ctx->sid, build_result);\n+ ctx->trans_status = build_result;\n+ return ctx->trans_status;\n+ }\n+\n+ /* Program IOMMU registers (sets root table pointer, enables translation) */\n+ qvtd_program_regs(ctx->qts, ctx->iommu_base, ctx->config.trans_mode);\n+\n+ ctx->trans_status = 0;\n+ return ctx->trans_status;\n+}\n+\n+static bool qvtd_validate_test_result(QVTDTestContext *ctx)\n+{\n+ uint32_t expected = qvtd_expected_dma_result(ctx);\n+\n+ g_test_message(\"-> Validating result: expected=0x%x actual=0x%x\",\n+ expected, ctx->dma_result);\n+ return (ctx->dma_result == expected);\n+}\n+\n+static uint32_t qvtd_single_translation_setup(void *opaque)\n+{\n+ return qvtd_setup_and_enable_translation(opaque);\n+}\n+\n+static uint32_t qvtd_single_translation_attrs(void *opaque)\n+{\n+ return qvtd_build_dma_attrs();\n+}\n+\n+static bool qvtd_single_translation_validate(void *opaque)\n+{\n+ return qvtd_validate_test_result(opaque);\n+}\n+\n+static void qvtd_single_translation_report(void *opaque, uint32_t dma_result)\n+{\n+ QVTDTestContext *ctx = opaque;\n+\n+ if (dma_result != 0) {\n+ g_test_message(\"DMA failed: mode=%u result=0x%x\",\n+ ctx->config.trans_mode, dma_result);\n+ } else {\n+ g_test_message(\"-> DMA succeeded: mode=%u\",\n+ ctx->config.trans_mode);\n+ }\n+}\n+\n+void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev,\n+ QPCIBar bar, uint64_t iommu_base,\n+ const QVTDTestConfig *cfg)\n+{\n+ QVTDTestContext ctx = {\n+ .qts = qts,\n+ .dev = dev,\n+ .bar = bar,\n+ .iommu_base = iommu_base,\n+ .config = *cfg,\n+ .sid = dev->devfn,\n+ };\n+\n+ QOSIOMMUTestdevDmaCfg dma = {\n+ .dev = dev,\n+ .bar = bar,\n+ .iova = QVTD_IOVA,\n+ .gpa = cfg->dma_gpa,\n+ .len = cfg->dma_len,\n+ };\n+\n+ qtest_memset(qts, cfg->dma_gpa, 0x00, cfg->dma_len);\n+ qos_iommu_testdev_single_translation(&dma, &ctx,\n+ qvtd_single_translation_setup,\n+ qvtd_single_translation_attrs,\n+ qvtd_single_translation_validate,\n+ qvtd_single_translation_report,\n+ &ctx.dma_result);\n+\n+ if (ctx.dma_result == 0 && ctx.config.expected_result == 0) {\n+ g_autofree uint8_t *buf = NULL;\n+\n+ buf = g_malloc(ctx.config.dma_len);\n+ qtest_memread(ctx.qts, ctx.config.dma_gpa, buf, ctx.config.dma_len);\n+\n+ for (int i = 0; i < ctx.config.dma_len; i++) {\n+ uint8_t expected;\n+\n+ expected = (ITD_DMA_WRITE_VAL >> ((i % 4) * 8)) & 0xff;\n+ g_assert_cmpuint(buf[i], ==, expected);\n+ }\n+ }\n+}\ndiff --git a/tests/qtest/libqos/qos-intel-iommu.h b/tests/qtest/libqos/qos-intel-iommu.h\nnew file mode 100644\nindex 0000000000..c6cacc5c3f\n--- /dev/null\n+++ b/tests/qtest/libqos/qos-intel-iommu.h\n@@ -0,0 +1,185 @@\n+/*\n+ * QOS Intel IOMMU (VT-d) Module\n+ *\n+ * This module provides Intel IOMMU-specific helper functions for libqos tests,\n+ * encapsulating VT-d setup, assertion, and cleanup operations.\n+ *\n+ * Copyright (c) 2026 Fengyuan Yu <15fengyuan@gmail.com>\n+ *\n+ * SPDX-License-Identifier: GPL-2.0-or-later\n+ */\n+\n+#ifndef QTEST_LIBQOS_INTEL_IOMMU_H\n+#define QTEST_LIBQOS_INTEL_IOMMU_H\n+\n+#include \"hw/misc/iommu-testdev.h\"\n+#include \"hw/i386/intel_iommu_internal.h\"\n+\n+/*\n+ * Guest memory layout for IOMMU structures.\n+ * All structures are placed in guest physical memory inside the 512MB RAM.\n+ * Using 256MB mark (0x10000000) as base to ensure all structures fit in RAM.\n+ */\n+#define QVTD_MEM_BASE 0x10000000ULL\n+\n+/* Root Entry Table: 256 entries * 16 bytes = 4KB */\n+#define QVTD_ROOT_TABLE_BASE (QVTD_MEM_BASE + 0x00000000)\n+\n+/* Context Entry Table: 256 entries, 16B (legacy) or 32B (scalable) per entry */\n+#define QVTD_CONTEXT_TABLE_BASE (QVTD_MEM_BASE + 0x00001000)\n+\n+/* Page Tables: 4-level hierarchy for 48-bit address translation */\n+#define QVTD_PT_L4_BASE (QVTD_MEM_BASE + 0x00010000) /* PML4 */\n+#define QVTD_PT_L3_BASE (QVTD_MEM_BASE + 0x00011000) /* PDPT */\n+#define QVTD_PT_L2_BASE (QVTD_MEM_BASE + 0x00012000) /* PD */\n+#define QVTD_PT_L1_BASE (QVTD_MEM_BASE + 0x00013000) /* PT */\n+\n+/*\n+ * Invalidation Queue.\n+ * IQA_REG bits[2:0] = QS, entries = 1 << (QS + 8), each entry 16 bytes.\n+ */\n+#define QVTD_IQ_BASE (QVTD_MEM_BASE + 0x00020000)\n+#define QVTD_IQ_QS 0 /* QS=0 → 256 entries */\n+\n+/*\n+ * Fault Event MSI configuration.\n+ */\n+#define QVTD_FAULT_IRQ_ADDR 0xfee00000 /* APIC base */\n+#define QVTD_FAULT_IRQ_DATA 0x0\n+\n+/* Scalable mode PASID structures */\n+#define QVTD_PASID_DIR_BASE (QVTD_MEM_BASE + 0x00030000)\n+#define QVTD_PASID_TABLE_BASE (QVTD_MEM_BASE + 0x00031000)\n+\n+/* Page table entry size (8 bytes per PTE) */\n+#define QVTD_PTE_SIZE sizeof(uint64_t)\n+\n+/* FSPTPTR mask: same as VTD_SM_PASID_ENTRY_SSPTPTR, bits[63:12] */\n+#define QVTD_SM_PASID_ENTRY_FSPTPTR VTD_SM_PASID_ENTRY_SSPTPTR\n+\n+/* Default Domain ID for single-domain tests */\n+#define QVTD_DOMAIN_ID 0\n+\n+/* Test IOVA and target physical address */\n+#define QVTD_IOVA 0x0000000010200567ull\n+#define QVTD_PT_VAL (QVTD_MEM_BASE + 0x00100000)\n+\n+/*\n+ * Translation modes supported by Intel IOMMU\n+ */\n+typedef enum QVTDTransMode {\n+ QVTD_TM_LEGACY_PT, /* Legacy pass-through mode */\n+ QVTD_TM_LEGACY_TRANS, /* Legacy translated mode (4-level paging) */\n+ QVTD_TM_SCALABLE_PT, /* Scalable pass-through mode */\n+ QVTD_TM_SCALABLE_SLT, /* Scalable Second Level Translation */\n+ QVTD_TM_SCALABLE_FLT, /* Scalable First Level Translation */\n+} QVTDTransMode;\n+\n+static inline bool qvtd_is_scalable(QVTDTransMode mode)\n+{\n+ return mode == QVTD_TM_SCALABLE_PT ||\n+ mode == QVTD_TM_SCALABLE_SLT ||\n+ mode == QVTD_TM_SCALABLE_FLT;\n+}\n+\n+typedef struct QVTDTestConfig {\n+ QVTDTransMode trans_mode; /* Translation mode */\n+ uint64_t dma_gpa; /* GPA for readback validation */\n+ uint32_t dma_len; /* DMA length for testing */\n+ uint32_t expected_result; /* Expected DMA result */\n+} QVTDTestConfig;\n+\n+typedef struct QVTDTestContext {\n+ QTestState *qts; /* QTest state handle */\n+ QPCIDevice *dev; /* PCI device handle */\n+ QPCIBar bar; /* PCI BAR for MMIO access */\n+ QVTDTestConfig config; /* Test configuration */\n+ uint64_t iommu_base; /* Intel IOMMU base address */\n+ uint32_t trans_status; /* Translation configuration status */\n+ uint32_t dma_result; /* DMA operation result */\n+ uint16_t sid; /* Source ID (bus:devfn) */\n+} QVTDTestContext;\n+\n+/*\n+ * qvtd_setup_and_enable_translation - Complete translation setup and enable\n+ *\n+ * @ctx: Test context containing configuration and device handles\n+ *\n+ * Returns: Translation status (0 = success, non-zero = error)\n+ *\n+ * This function performs the complete translation setup sequence:\n+ * 1. Builds VT-d structures (root/context entry, page tables)\n+ * 2. Programs IOMMU registers and enables translation\n+ * 3. Returns configuration status\n+ */\n+uint32_t qvtd_setup_and_enable_translation(QVTDTestContext *ctx);\n+\n+/*\n+ * qvtd_build_translation - Build Intel IOMMU translation structures\n+ *\n+ * @qts: QTest state handle\n+ * @mode: Translation mode (pass-through or translated)\n+ * @sid: Source ID (bus:devfn)\n+ *\n+ * Returns: Build status (0 = success, non-zero = error)\n+ *\n+ * Constructs all necessary VT-d translation structures in guest memory:\n+ * - Root Entry for the device's bus\n+ * - Context Entry for the device\n+ * - Complete 4-level page table hierarchy (if translated mode)\n+ */\n+uint32_t qvtd_build_translation(QTestState *qts, QVTDTransMode mode,\n+ uint16_t sid);\n+\n+/*\n+ * qvtd_program_regs - Program Intel IOMMU registers and enable translation\n+ *\n+ * @qts: QTest state handle\n+ * @iommu_base: IOMMU base address\n+ * @mode: Translation mode (scalable modes set RTADDR SMT bit)\n+ *\n+ * Programs IOMMU registers with the following sequence:\n+ * 1. Set root table pointer (SRTP), with SMT bit for scalable mode\n+ * 2. Setup invalidation queue (QIE)\n+ * 3. Configure fault event MSI\n+ * 4. Enable translation (TE)\n+ *\n+ * Each step verifies completion via GSTS register read-back.\n+ */\n+void qvtd_program_regs(QTestState *qts, uint64_t iommu_base,\n+ QVTDTransMode mode);\n+\n+/*\n+ * qvtd_setup_translation_tables - Setup complete VT-d page table hierarchy\n+ *\n+ * @qts: QTest state handle\n+ * @iova: Input Virtual Address to translate\n+ * @mode: Translation mode\n+ *\n+ * This builds the 4-level page table structure for translating\n+ * the given IOVA to PA through Intel VT-d. The structure is:\n+ * - PML4 (Level 4): IOVA bits [47:39]\n+ * - PDPT (Level 3): IOVA bits [38:30]\n+ * - PD (Level 2): IOVA bits [29:21]\n+ * - PT (Level 1): IOVA bits [20:12]\n+ * - Page offset: IOVA bits [11:0]\n+ *\n+ * The function writes all necessary Page Table Entries (PTEs) to guest\n+ * memory using qtest_writeq(), setting up the complete translation path\n+ * that the VT-d hardware will traverse during DMA operations.\n+ */\n+void qvtd_setup_translation_tables(QTestState *qts, uint64_t iova,\n+ QVTDTransMode mode);\n+\n+/* Calculate expected DMA result */\n+uint32_t qvtd_expected_dma_result(QVTDTestContext *ctx);\n+\n+/* Build DMA attributes for Intel VT-d */\n+uint32_t qvtd_build_dma_attrs(void);\n+\n+/* High-level test execution helpers */\n+void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev,\n+ QPCIBar bar, uint64_t iommu_base,\n+ const QVTDTestConfig *cfg);\n+\n+#endif /* QTEST_LIBQOS_INTEL_IOMMU_H */\n", "prefixes": [ "v3", "1/2" ] }