get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2215710/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "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"
    ]
}