From nobody Sun Jul 26 11:01:49 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1783065732; cv=none; d=zohomail.com; s=zohoarc; b=dWA0YklmIp5sfTZzXWEzUWh94YCHJ7fcnZ2GC/QxNbDAxJYCAkYGQZjdcPo7i/Ao2j1aux4sx6vKl3/r1dBgtZ9Zc5gSYUZUQ5qSPBxEMa26JfeZ9giJ7mulY+r+404nDOfFJKhxUhf7NREoO8DyHdzlvYl7Hd6I6mUxDXIpObo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783065732; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=MfrAblErKeyCFUABt2bvs1kQWerrjYQFCF0jEo4M+Ik=; b=SKplqwySK0it4laQa0pMjHO3x18ahFHhgBbaqDTwBDuvTibOBTktY3W/mvehxerPRZZPoF37B+ozFlSZpvbnvVU8LJe0lxVe99mpy8Q1q/uLrZLxkoAJlnExQrhzw7xeRdlp3MvFI00JCtQ4tPtQnXfmxZJMEY3xw8m/6M0u0Hc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783065732555678.1147998146326; Fri, 3 Jul 2026 01:02:12 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wfYqA-0004UQ-Iq; Fri, 03 Jul 2026 04:01:38 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYq0-0004Ra-NB for qemu-devel@nongnu.org; Fri, 03 Jul 2026 04:01:33 -0400 Received: from mgamail.intel.com ([192.198.163.9]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYpy-0001gX-R1 for qemu-devel@nongnu.org; Fri, 03 Jul 2026 04:01:28 -0400 Received: from fmviesa001.fm.intel.com ([10.60.135.141]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Jul 2026 01:01:26 -0700 Received: from junjie-desk-dev.bj.intel.com ([10.238.152.71]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Jul 2026 01:01:23 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1783065687; x=1814601687; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=bYXimPDPjWx48lnFAG4DCzOY+K7yAnsmiKAMaq78rzw=; b=ZBOvqPk6CR6mJXzhkJF1SWSUHmM0htlHESB5pLU8ARSTBQN5zDsUosm/ nj4+cSub7NqrLSJCokBU41wquNdxbLepqnJO+S+WnMJpU0U+6iHxTu90Q fYpLD15t2UYqfty/fKstA+BnV6oc1gx/sPLkYadu9ces2vGmuzkDtZ2l2 SXUQntAeQQBX6TuE2GJrnqQyzQzmEzWZSDK5ZVunj9gPmGNcNPd1YvcF6 WH9QNfM+NVamDkFE9/HdGs60KinkvzA8SR613dl3eCq1pbjH2Fx9qg2nm 7hysHNzMRJ8kcGSCngJYE36AtExZ/y1fTVvlTBazX9PLJhzCbOFjfoXTd Q==; X-CSE-ConnectionGUID: ij22QdCaRbiEsXh7dBDgAQ== X-CSE-MsgGUID: 5/yuup/mR4yNqmUxM64fBA== X-IronPort-AV: E=McAfee;i="6800,10657,11835"; a="94464656" X-IronPort-AV: E=Sophos;i="6.25,145,1779174000"; d="scan'208";a="94464656" X-CSE-ConnectionGUID: 3Ox7USkoRkG/2oOsB9nnKA== X-CSE-MsgGUID: UfBClxU5QuqKcgL0QeNliw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,145,1779174000"; d="scan'208";a="277379553" From: Junjie Cao To: qemu-devel@nongnu.org Cc: "Michael S . Tsirkin" , Jason Wang , Yi Liu , Zhenzhong Duan , =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= , Fabiano Rosas , Laurent Vivier , Tao Tang , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Paolo Bonzini , Fengyuan Yu <15fengyuan@gmail.com>, Chao Liu , junjie.cao@intel.com Subject: [PATCH v1 1/3] tests/qtest/libqos: share Intel IOMMU test setup helpers Date: Fri, 3 Jul 2026 15:21:56 +0800 Message-ID: <20260703072200.463082-2-junjie.cao@intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260703072200.463082-1-junjie.cao@intel.com> References: <20260703072200.463082-1-junjie.cao@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=192.198.163.9; envelope-from=junjie.cao@intel.com; helo=mgamail.intel.com X-Spam_score_int: -47 X-Spam_score: -4.8 X-Spam_bar: ---- X-Spam_report: (-4.8 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.445, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_NONE=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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @intel.com) X-ZM-MESSAGEID: 1783065734292158500 Content-Type: text/plain; charset="utf-8" iommu-intel-test.c keeps the iommu-testdev PCI setup (save_fn(), setup_qtest_pci_device()) and the VT-d command-line / capability helpers (qvtd_iommu_args(), qvtd_check_caps()) as file-local statics. A second Intel IOMMU test would have to copy them, which defeats the purpose of the shared qos-intel-iommu module. Move them into qos-intel-iommu so sibling tests can reuse them: save_fn() becomes qvtd_save_pci_dev() and setup_qtest_pci_device() becomes qvtd_setup_qtest_pci_device(); qvtd_iommu_args() and qvtd_check_caps() keep their names. No functional change: iommu-intel-test now calls the public qvtd_setup_qtest_pci_device() instead of its file-local copy. Signed-off-by: Junjie Cao --- tests/qtest/iommu-intel-test.c | 78 +--------------------------- tests/qtest/libqos/qos-intel-iommu.c | 77 +++++++++++++++++++++++++++ tests/qtest/libqos/qos-intel-iommu.h | 23 ++++++++ 3 files changed, 101 insertions(+), 77 deletions(-) diff --git a/tests/qtest/iommu-intel-test.c b/tests/qtest/iommu-intel-test.c index 703bbfef73..c2744def6f 100644 --- a/tests/qtest/iommu-intel-test.c +++ b/tests/qtest/iommu-intel-test.c @@ -24,82 +24,6 @@ static uint64_t intel_iommu_expected_gpa(uint64_t iova) return (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (iova & 0xfff); } =20 -static void save_fn(QPCIDevice *dev, int devfn, void *data) -{ - QPCIDevice **pdev =3D (QPCIDevice **) data; - - *pdev =3D dev; -} - -static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibu= s, - QPCIBar *bar) -{ - QPCIDevice *dev =3D NULL; - - *pcibus =3D qpci_new_pc(qts, NULL); - g_assert(*pcibus !=3D NULL); - - qpci_device_foreach(*pcibus, IOMMU_TESTDEV_VENDOR_ID, - IOMMU_TESTDEV_DEVICE_ID, save_fn, &dev); - - g_assert(dev); - qpci_device_enable(dev); - *bar =3D 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,scalable-mode=3Don,fsts=3Don "; - case QVTD_TM_SCALABLE_PT: - case QVTD_TM_SCALABLE_SLT: - return "-device intel-iommu,scalable-mode=3Don "; - default: - return "-device intel-iommu "; - } -} - -static bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode) -{ - uint64_t ecap =3D 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; @@ -124,7 +48,7 @@ static void run_intel_iommu_translation(const QVTDTestCo= nfig *cfg) } =20 /* Setup and configure IOMMU-testdev PCI device */ - dev =3D setup_qtest_pci_device(qts, &pcibus, &bar); + dev =3D qvtd_setup_qtest_pci_device(qts, &pcibus, &bar); g_assert(dev); =20 g_test_message("### Intel IOMMU translation mode=3D%d ###", cfg->trans= _mode); diff --git a/tests/qtest/libqos/qos-intel-iommu.c b/tests/qtest/libqos/qos-= intel-iommu.c index f8ca4c871b..4095ac0917 100644 --- a/tests/qtest/libqos/qos-intel-iommu.c +++ b/tests/qtest/libqos/qos-intel-iommu.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "hw/i386/intel_iommu_internal.h" #include "tests/qtest/libqos/pci.h" +#include "tests/qtest/libqos/pci-pc.h" #include "qos-iommu-testdev.h" #include "qos-intel-iommu.h" =20 @@ -452,3 +453,79 @@ void qvtd_run_translation_case(QTestState *qts, QPCIDe= vice *dev, } } } + +const char *qvtd_iommu_args(QVTDTransMode mode) +{ + switch (mode) { + case QVTD_TM_SCALABLE_FLT: + return "-device intel-iommu,scalable-mode=3Don,fsts=3Don "; + case QVTD_TM_SCALABLE_PT: + case QVTD_TM_SCALABLE_SLT: + return "-device intel-iommu,scalable-mode=3Don "; + default: + return "-device intel-iommu "; + } +} + +bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode) +{ + uint64_t ecap =3D 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 qvtd_save_pci_dev(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice **pdev =3D (QPCIDevice **)data; + + *pdev =3D dev; +} + +QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus, + QPCIBar *bar) +{ + QPCIDevice *dev =3D NULL; + + *pcibus =3D qpci_new_pc(qts, NULL); + g_assert(*pcibus !=3D NULL); + + qpci_device_foreach(*pcibus, IOMMU_TESTDEV_VENDOR_ID, + IOMMU_TESTDEV_DEVICE_ID, qvtd_save_pci_dev, &dev); + + g_assert(dev); + qpci_device_enable(dev); + *bar =3D qpci_iomap(dev, 0, NULL); + g_assert_false(bar->is_io); + + return dev; +} diff --git a/tests/qtest/libqos/qos-intel-iommu.h b/tests/qtest/libqos/qos-= intel-iommu.h index c6cacc5c3f..dd6f920464 100644 --- a/tests/qtest/libqos/qos-intel-iommu.h +++ b/tests/qtest/libqos/qos-intel-iommu.h @@ -182,4 +182,27 @@ void qvtd_run_translation_case(QTestState *qts, QPCIDe= vice *dev, QPCIBar bar, uint64_t iommu_base, const QVTDTestConfig *cfg); =20 +/* + * qvtd_iommu_args - Build the -device intel-iommu command-line fragment + * for the requested translation mode. + */ +const char *qvtd_iommu_args(QVTDTransMode mode); + +/* + * qvtd_check_caps - Check whether the running QEMU exposes the ECAP bits + * required by @mode. Calls g_test_skip() and returns + * false when a required capability is missing. + */ +bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode); + +/* + * qvtd_setup_qtest_pci_device - Create a QPCIBus, locate the iommu-testdev + * PCI function, enable it and map BAR0. + * + * On success, *pcibus and *bar are populated and the returned QPCIDevice + * must be released by the caller via g_free(). + */ +QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus, + QPCIBar *bar); + #endif /* QTEST_LIBQOS_INTEL_IOMMU_H */ --=20 2.43.0 From nobody Sun Jul 26 11:01:49 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1783065732; cv=none; d=zohomail.com; s=zohoarc; b=UrdWBrZF3whD0iY9OGotabA1zaWHrMDL15xn9feAURziRW125uqy1r8LQmZ2prKlFPKDiDJIsscdvFG/pKqMhk1PsexrHRAim7kpo+wrMnBYjodJMc9o4dBJAxVxFSOciyjO7KBh+WTZi3fkAdMvBSHpCnNoyUnLcNkTKZGUGVo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783065732; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=LDOs60ScLFlhCvcBflQhAii63J+qqMq9volvHI4oyBM=; b=G4UBNBb7WOlmfXgBnJZ87vn0EjSzP5wMjG3Rih6cPIr8xilB8KmIQlwAdNCOvm2bB0+wcqJX0M5om5XJyBWlnDlIyuG/V3p+D1czY4/qgQfOx1CS5bTfIuQFguctOLF0YvOA/qiKyEQi8j8s24hUGboTqv34peu6klU3tmzPWaw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783065732557491.8942290069567; Fri, 3 Jul 2026 01:02:12 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wfYqE-0004Uu-ES; Fri, 03 Jul 2026 04:01:42 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYq9-0004UH-0U for qemu-devel@nongnu.org; Fri, 03 Jul 2026 04:01:37 -0400 Received: from mgamail.intel.com ([192.198.163.9]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYq7-0001iw-3E for qemu-devel@nongnu.org; Fri, 03 Jul 2026 04:01:36 -0400 Received: from fmviesa001.fm.intel.com ([10.60.135.141]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Jul 2026 01:01:32 -0700 Received: from junjie-desk-dev.bj.intel.com ([10.238.152.71]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Jul 2026 01:01:29 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1783065695; x=1814601695; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=O89BeKg4AxgXXbHx+MKTu9C8fTGOCKAkuWa6bKKKYD8=; b=WFwPkMmZhq2Hdi4kUI6KCnD4U7H0htcoCxBBno1c3e12m6jHC2LXbZjT 2ZHK8cJFJpZCZbrb5A0DqrdM5LhSrstFwA4uWjHHdWfWFIogyvHZ40Ym6 qR3lfZPp+SYRvJNVa6CUQMvujE4V33Mkine4EWbouXxxhNtUsjtj7qcSe RcFCioz+GTOoZH2BD4vwqyleSiTZQNPw5FRFTxmkyVRdOjPkWkDl/hiZo J8kvfNbe2GhQRy4i+BSYtqUbeItZQ0+ntsVsEaPzHO3QHDVeq4prVku2x rMUPat8iGqCJMlXM9IvyvE0T5BcQOYfgAYSGtx4sTr8g04jA8HQCSM6zn g==; X-CSE-ConnectionGUID: 92fP59o/S/aer1x0yX5grQ== X-CSE-MsgGUID: 8/ZkIrB5TFC3eaIyjgiQ6A== X-IronPort-AV: E=McAfee;i="6800,10657,11835"; a="94464667" X-IronPort-AV: E=Sophos;i="6.25,145,1779174000"; d="scan'208";a="94464667" X-CSE-ConnectionGUID: 2n7stN4MQrGC6K/3FMe+2A== X-CSE-MsgGUID: wG2WigkwTuuBk0RrkB+f5w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,145,1779174000"; d="scan'208";a="277379589" From: Junjie Cao To: qemu-devel@nongnu.org Cc: "Michael S . Tsirkin" , Jason Wang , Yi Liu , Zhenzhong Duan , =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= , Fabiano Rosas , Laurent Vivier , Tao Tang , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Paolo Bonzini , Fengyuan Yu <15fengyuan@gmail.com>, Chao Liu , junjie.cao@intel.com Subject: [PATCH v1 2/3] tests/qtest/libqos: add Intel IOMMU invalidation helpers Date: Fri, 3 Jul 2026 15:21:57 +0800 Message-ID: <20260703072200.463082-3-junjie.cao@intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260703072200.463082-1-junjie.cao@intel.com> References: <20260703072200.463082-1-junjie.cao@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=192.198.163.9; envelope-from=junjie.cao@intel.com; helo=mgamail.intel.com X-Spam_score_int: -47 X-Spam_score: -4.8 X-Spam_bar: ---- X-Spam_report: (-4.8 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.445, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_NONE=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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @intel.com) X-ZM-MESSAGEID: 1783065735283158500 Add the building blocks a queued-invalidation test needs on top of the existing translation helpers: - qvtd_leaf_pte_addr() / qvtd_make_leaf_pte() let a test locate and rewrite the leaf PTE built by qvtd_setup_translation_tables() without re-deriving the page-table index or leaf attributes by hand. The attributes reuse qvtd_get_fl_pte_attrs()/qvtd_get_pte_attrs() so the first- and second-level leaf formats stay defined in one place. - qvtd_submit_iotlb_global_inv() / _domain_inv() / _page_inv() write an IOTLB Invalidation Descriptor (global / domain-selective / page-selective) into the Invalidation Queue and advance IQT_REG. - qvtd_submit_inv_wait_and_poll() submits an Invalidation Wait Descriptor with Status Write and polls the status word with a bounded retry loop, asserting on timeout. No caller yet; used by the IOTLB invalidation test that follows. Signed-off-by: Junjie Cao --- tests/qtest/libqos/qos-intel-iommu.c | 108 +++++++++++++++++++++++++++ tests/qtest/libqos/qos-intel-iommu.h | 51 +++++++++++++ 2 files changed, 159 insertions(+) diff --git a/tests/qtest/libqos/qos-intel-iommu.c b/tests/qtest/libqos/qos-= intel-iommu.c index 4095ac0917..c793ec899c 100644 --- a/tests/qtest/libqos/qos-intel-iommu.c +++ b/tests/qtest/libqos/qos-intel-iommu.c @@ -15,6 +15,10 @@ #include "qos-iommu-testdev.h" #include "qos-intel-iommu.h" =20 +/* Bounded poll for the invalidation-wait Status Write. */ +#define QVTD_INV_WAIT_POLL_MAX_ITERS 1000 +#define QVTD_INV_WAIT_POLL_INTERVAL_US 1000 + #define QVTD_AW_48BIT_ENCODING 2 =20 uint32_t qvtd_expected_dma_result(QVTDTestContext *ctx) @@ -232,6 +236,30 @@ static uint64_t qvtd_get_fl_pte_attrs(bool is_leaf) return attrs; } =20 +uint64_t qvtd_leaf_pte_addr(uint64_t iova) +{ + return qvtd_get_table_addr(QVTD_PT_L1_BASE, 1, iova); +} + +uint64_t qvtd_make_leaf_pte(uint64_t pa, QVTDTransMode mode) +{ + uint64_t attrs; + + /* + * Reuse the same leaf attributes qvtd_setup_translation_tables() writ= es, + * so a rewritten PTE stays consistent with the initial mapping. US= =3D1 in + * the first-level format is required because the PASID entry runs with + * SRE=3D0, which makes every DMA appear as a user-mode access + * (VT-d 3.6.2 / 9.9). + */ + if (mode =3D=3D QVTD_TM_SCALABLE_FLT) { + attrs =3D qvtd_get_fl_pte_attrs(true); + } else { + attrs =3D qvtd_get_pte_attrs(); + } + return (pa & VTD_PAGE_MASK_4K) | attrs; +} + void qvtd_setup_translation_tables(QTestState *qts, uint64_t iova, QVTDTransMode mode) { @@ -529,3 +557,83 @@ QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qt= s, QPCIBus **pcibus, =20 return dev; } + +/* + * Write a 128-bit invalidation descriptor at the current tail and advance + * IQT_REG. Internal helper for the IOTLB / wait variants below. + */ +static uint32_t qvtd_submit_inv_desc(QTestState *qts, uint64_t iommu_base, + uint64_t desc_lo, uint64_t desc_hi, + uint32_t tail) +{ + uint64_t desc_addr =3D QVTD_IQ_BASE + (uint64_t)tail * QVTD_IQ_DESC_SI= ZE; + + qtest_writeq(qts, desc_addr, desc_lo); + qtest_writeq(qts, desc_addr + 8, desc_hi); + tail++; + + qtest_writeq(qts, iommu_base + DMAR_IQT_REG, + (uint64_t)tail << QVTD_IQT_SHIFT); + return tail; +} + +uint32_t qvtd_submit_inv_wait_and_poll(QTestState *qts, uint64_t iommu_bas= e, + uint32_t tail) +{ + uint64_t lo, hi; + uint32_t status =3D 0; + int i; + + qtest_writel(qts, QVTD_INV_WAIT_ADDR, 0); + + lo =3D VTD_INV_DESC_WAIT | VTD_INV_DESC_WAIT_SW | + ((uint64_t)QVTD_INV_WAIT_DATA << VTD_INV_DESC_WAIT_DATA_SHIFT); + hi =3D QVTD_INV_WAIT_ADDR; + + tail =3D qvtd_submit_inv_desc(qts, iommu_base, lo, hi, tail); + + for (i =3D 0; i < QVTD_INV_WAIT_POLL_MAX_ITERS; i++) { + status =3D qtest_readl(qts, QVTD_INV_WAIT_ADDR); + if (status =3D=3D QVTD_INV_WAIT_DATA) { + return tail; + } + g_usleep(QVTD_INV_WAIT_POLL_INTERVAL_US); + } + + g_assert_cmphex(status, =3D=3D, QVTD_INV_WAIT_DATA); + return tail; +} + +uint32_t qvtd_submit_iotlb_global_inv(QTestState *qts, uint64_t iommu_base, + uint32_t tail) +{ + uint64_t lo =3D VTD_INV_DESC_IOTLB | VTD_INV_DESC_IOTLB_GLOBAL; + + return qvtd_submit_inv_desc(qts, iommu_base, lo, 0, tail); +} + +uint32_t qvtd_submit_iotlb_domain_inv(QTestState *qts, uint64_t iommu_base, + uint16_t domain_id, uint32_t tail) +{ + uint64_t lo =3D VTD_INV_DESC_IOTLB | VTD_INV_DESC_IOTLB_DOMAIN | + ((uint64_t)domain_id << 16); + + return qvtd_submit_inv_desc(qts, iommu_base, lo, 0, tail); +} + +uint32_t qvtd_submit_iotlb_page_inv(QTestState *qts, uint64_t iommu_base, + uint16_t domain_id, uint64_t addr, + uint8_t am, uint32_t tail) +{ + uint64_t lo =3D VTD_INV_DESC_IOTLB | VTD_INV_DESC_IOTLB_PAGE | + ((uint64_t)domain_id << 16); + /* + * AM selects the invalidation range per VT-d 6.5.2.4: + * am=3D0 =E2=86=92 4 KB, am=3D9 =E2=86=92 2 MB, am=3D18 =E2=86= =92 1 GB. + * IH (hi[6]) is left clear, requesting full invalidation + * including non-leaf paging-structure caches. + */ + uint64_t hi =3D (addr & ~0xfffULL) | (am & 0x3fULL); + + return qvtd_submit_inv_desc(qts, iommu_base, lo, hi, tail); +} diff --git a/tests/qtest/libqos/qos-intel-iommu.h b/tests/qtest/libqos/qos-= intel-iommu.h index dd6f920464..3a97d14786 100644 --- a/tests/qtest/libqos/qos-intel-iommu.h +++ b/tests/qtest/libqos/qos-intel-iommu.h @@ -40,6 +40,16 @@ */ #define QVTD_IQ_BASE (QVTD_MEM_BASE + 0x00020000) #define QVTD_IQ_QS 0 /* QS=3D0 =E2=86=92 256 entries */ +#define QVTD_IQ_DESC_SIZE 16 /* 128-bit descriptor (iq_dw=3D0) */ +#define QVTD_IQT_SHIFT 4 /* IQT_REG[18:4] is the tail index = */ + +/* + * Invalidation Wait Descriptor with Status Write (VT-d 6.5.2.8). + * The IOMMU writes QVTD_INV_WAIT_DATA to QVTD_INV_WAIT_ADDR after all + * preceding invalidation descriptors complete. + */ +#define QVTD_INV_WAIT_ADDR (QVTD_MEM_BASE + 0x00040000) +#define QVTD_INV_WAIT_DATA 0x1u =20 /* * Fault Event MSI configuration. @@ -205,4 +215,45 @@ bool qvtd_check_caps(QTestState *qts, QVTDTransMode mo= de); QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus, QPCIBar *bar); =20 +/* + * qvtd_leaf_pte_addr - Address of the leaf (L1) PTE for @iova, assuming + * the hierarchy was built by + * qvtd_setup_translation_tables() with 4 KB leaves. + */ +uint64_t qvtd_leaf_pte_addr(uint64_t iova); + +/* + * qvtd_make_leaf_pte - Build a leaf PTE value mapping @pa, for the + * 4-level / 4 KB-leaf layout used by + * qvtd_setup_translation_tables(). + * Selects FLT or SLT/legacy attributes from @mode. + */ +uint64_t qvtd_make_leaf_pte(uint64_t pa, QVTDTransMode mode); + +/* + * qvtd_submit_inv_wait_and_poll - Submit an Invalidation Wait Descriptor + * with Status Write and poll until the + * IOMMU writes the expected status data. + * + * Asserts on timeout. Returns the new tail index. + */ +uint32_t qvtd_submit_inv_wait_and_poll(QTestState *qts, uint64_t iommu_bas= e, + uint32_t tail); + +/* + * qvtd_submit_iotlb_global_inv - global IOTLB invalidation. + * qvtd_submit_iotlb_domain_inv - domain-selective IOTLB invalidation. + * qvtd_submit_iotlb_page_inv - page-selective IOTLB invalidation; + * @addr must be 4 KB aligned. @am + * selects the address mask per VT-d + * 6.5.2.4 (am=3D0 =E2=86=92 single 4 KB p= age). + */ +uint32_t qvtd_submit_iotlb_global_inv(QTestState *qts, uint64_t iommu_base, + uint32_t tail); +uint32_t qvtd_submit_iotlb_domain_inv(QTestState *qts, uint64_t iommu_base, + uint16_t domain_id, uint32_t tail); +uint32_t qvtd_submit_iotlb_page_inv(QTestState *qts, uint64_t iommu_base, + uint16_t domain_id, uint64_t addr, + uint8_t am, uint32_t tail); + #endif /* QTEST_LIBQOS_INTEL_IOMMU_H */ --=20 2.43.0 From nobody Sun Jul 26 11:01:49 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1783065732; cv=none; d=zohomail.com; s=zohoarc; b=EsLe2Iphp8x0a9gPPkBONZrKWri+mu17stC9FJmAA8ybHDTwdzAuR+HBie4Ln45sslIq4x4qz/Z2kfAbm4WoQYNnCpeagrcQWLQynVHJ3sk8r/e7bt5+4AonhqaPjLuifjAWningPS7aDytV/QjdVzWys5iI678n4VXTEjM9L+0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783065732; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Ia/wD3AusJuWwkagDxd8NoQLHpK6U4b4NJPZ2M+gSus=; b=Nx1IYK9u1aEYVXHvOKcugzWZM0nEfkppKIrWtmmqAekrYzoU/OCAjz9bRhPlCRoQ1oaFEl9IKBqBIFe5DgNnbo5MnArK+xe7zltOPiGV43kmN9C0gPIwquXfUATUTgdYecTLrjAj+K7/GcSl7vD7RFqNBfC2mbV7r7IewQHVZFs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=@intel.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1783065732646557.13818494383; Fri, 3 Jul 2026 01:02:12 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wfYqK-0004Xa-2F; Fri, 03 Jul 2026 04:01:48 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYqE-0004Vn-Qb for qemu-devel@nongnu.org; Fri, 03 Jul 2026 04:01:46 -0400 Received: from mgamail.intel.com ([192.198.163.9]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wfYqC-0001iw-5M for qemu-devel@nongnu.org; Fri, 03 Jul 2026 04:01:42 -0400 Received: from fmviesa001.fm.intel.com ([10.60.135.141]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Jul 2026 01:01:39 -0700 Received: from junjie-desk-dev.bj.intel.com ([10.238.152.71]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 03 Jul 2026 01:01:36 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1783065700; x=1814601700; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=UUmDZnLxHb/740hRe2mHgGyprZ7ToOyz1L+j8UIFURc=; b=nmknWGk0YD9m0BFgOEGnUf848BaU+kpJyBViDgbVfjqdUGehJ7xjWMsK Ujkg/H2aCE9/Xd0qladBSrqVG0IylQQrtQGI9M21yQfWhZ0AS3uIxGU7M IDEnhIiWG7sOXoaNZzHObzM7f+rWY8MRqJ6brA8C+vaMT3eGko7sb/V0H IiVbwuvcyL039oxb5T7jefkTR8tnjXmflNYsBNxacUS0huz3a0sWAInA5 xlKz9Gzf99LihjKWnzElHfnoo16ytm1jfsiEwJj8W/OdlZUcmL1ZzgL9L RZdXBQV62VfCi7sZFwavOZaN6GstDaaXwRbiPqgLzn4Urs5+F7Cl43yVg Q==; X-CSE-ConnectionGUID: +nGwWnMXQZOW14lE8JMlnA== X-CSE-MsgGUID: MRhNTzvMRX2Honxt9tnE+Q== X-IronPort-AV: E=McAfee;i="6800,10657,11835"; a="94464689" X-IronPort-AV: E=Sophos;i="6.25,145,1779174000"; d="scan'208";a="94464689" X-CSE-ConnectionGUID: cVdB8W08T9mWFN/p0ip/6Q== X-CSE-MsgGUID: ZiBgp63WQ6CE1oBZpZoqAw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,145,1779174000"; d="scan'208";a="277379660" From: Junjie Cao To: qemu-devel@nongnu.org Cc: "Michael S . Tsirkin" , Jason Wang , Yi Liu , Zhenzhong Duan , =?UTF-8?q?Cl=C3=A9ment=20Mathieu--Drif?= , Fabiano Rosas , Laurent Vivier , Tao Tang , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Paolo Bonzini , Fengyuan Yu <15fengyuan@gmail.com>, Chao Liu , junjie.cao@intel.com Subject: [PATCH v1 3/3] tests/qtest: add IOTLB invalidation test for Intel IOMMU Date: Fri, 3 Jul 2026 15:21:58 +0800 Message-ID: <20260703072200.463082-4-junjie.cao@intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260703072200.463082-1-junjie.cao@intel.com> References: <20260703072200.463082-1-junjie.cao@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=192.198.163.9; envelope-from=junjie.cao@intel.com; helo=mgamail.intel.com X-Spam_score_int: -47 X-Spam_score: -4.8 X-Spam_bar: ---- X-Spam_report: (-4.8 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.445, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_NONE=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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @intel.com) X-ZM-MESSAGEID: 1783065734580158500 Content-Type: text/plain; charset="utf-8" Nothing in tree exercises IOTLB invalidation for any emulated vIOMMU: the existing iommu-testdev tests only check one-shot translation, so a regression that failed to flush a stale IOTLB entry would go unnoticed. Add a test that drives the queued-invalidation path end to end (vtd_process_inv_desc -> vtd_process_iotlb_desc -> vtd_iotlb_{global,domain,page}_invalidate). For each {legacy, scalable-slt, scalable-flt} x {global, domain, page} combination it: 1. maps IOVA -> PA_A and DMAs, populating the IOTLB; 2. rewrites the leaf PTE to PA_B *without* invalidating and DMAs again, asserting the stale entry is still served (MISMATCH); 3. submits the IOTLB invalidation plus a wait descriptor, then DMAs and asserts the fresh page walk now reaches PA_B. Step 2 makes the flush observable: it fails loudly if the IOTLB is not actually caching the first translation. For scalable first-level (flt), QEMU keeps first- and second-level mappings in a single IOTLB that the legacy VTD_INV_DESC_IOTLB descriptor flushes for every level, so the test uses that descriptor across all modes. PASID-selective invalidation (VTD_INV_DESC_PIOTLB, vtd_piotlb_*) is a separate path and is left for a follow-up. It also adds three page-selective cases that cache a second page and check its fate after invalidating the first: for second-level (legacy, scalable-s= lt) the second page survives, while for first-level (scalable-flt) QEMU flushes= all first-stage entries of the domain, so it does not. This distinguishes a page-selective flush from a domain-wide or global one. Signed-off-by: Junjie Cao --- MAINTAINERS | 1 + tests/qtest/iommu-intel-inv-test.c | 346 +++++++++++++++++++++++++++++ tests/qtest/meson.build | 2 +- 3 files changed, 348 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/iommu-intel-inv-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 97dcc78ded..906de6e39f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4103,6 +4103,7 @@ 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 +F: tests/qtest/iommu-intel-inv-test.c =20 AMD-Vi Emulation M: Alejandro Jimenez diff --git a/tests/qtest/iommu-intel-inv-test.c b/tests/qtest/iommu-intel-i= nv-test.c new file mode 100644 index 0000000000..1467eef647 --- /dev/null +++ b/tests/qtest/iommu-intel-inv-test.c @@ -0,0 +1,346 @@ +/* + * QTest for Intel IOMMU (VT-d) IOTLB invalidation via Invalidation Queue + * + * Validates that IOTLB invalidation descriptors submitted through the + * queued invalidation interface correctly flush cached translations, + * forcing the IOMMU to re-walk page tables on subsequent DMA. + * + * Copyright (c) 2026 Intel Corporation. + * + * Author: Junjie Cao + * + * 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" +#include "libqos/qos-iommu-testdev.h" + +#define DMA_LEN 4 + +/* + * Second DMA target page, chosen to fall well outside any address used by + * qos-intel-iommu's fixed structure layout. + */ +#define QVTD_PT_VAL_B (QVTD_MEM_BASE + 0x00200000) + +/* + * A second IOVA/target page for the page-selectivity test. QVTD_IOVA_2 is + * QVTD_IOVA + 4K: it shares the L4/L3/L2 walk built by + * qvtd_build_translation() and differs only in the leaf (L1) slot, so map= ping + * it costs one extra leaf PTE. QVTD_PT_VAL_2 is its distinct target page. + */ +#define QVTD_IOVA_2 (QVTD_IOVA + 0x1000) +#define QVTD_PT_VAL_2 (QVTD_MEM_BASE + 0x00300000) + +typedef enum { + IOTLB_INV_GLOBAL, + IOTLB_INV_DOMAIN, + IOTLB_INV_PAGE, +} IOTLBInvGranularity; + +/* + * Core invalidation test, parameterized by translation mode and + * invalidation granularity. + * + * The iommu-testdev device performs DMA writes via the IOMMU (using the + * IOVA) and verifies by reading back from the expected GPA directly. If + * the IOTLB is stale, the DMA write lands at the old PA while readback + * uses the GPA we supply, causing a mismatch (ITD_DMA_ERR_MISMATCH). + * + * Test sequence: + * 1. Setup translation: IOVA -> PA_A + * 2. DMA(gpa=3DPA_A) -> success (IOTLB populates cache) + * 3. Modify PTE: IOVA -> PA_B (no invalidation) + * 4. DMA(gpa=3DPA_B) -> MISMATCH (stale IOTLB directs write to PA_A) + * 5. Issue IOTLB invalidation + wait + * 6. DMA(gpa=3DPA_B) -> success (cache flushed, fresh page walk) + * + * Phase 4 depends on QEMU's IOTLB caching the Phase 1 translation; if a + * future change makes IOTLB caching lazy this assertion would no longer + * exercise the stale-cache path. + */ +static void run_iotlb_inv_test(QVTDTransMode mode, IOTLBInvGranularity gra= n) +{ + QTestState *qts; + QPCIBus *pcibus; + QPCIDevice *dev; + QPCIBar bar; + uint32_t tail =3D 0; + uint32_t result; + uint64_t pa_a, pa_b; + + if (!qtest_has_machine("q35")) { + g_test_skip("q35 machine not available"); + return; + } + + qts =3D qtest_initf("-machine q35 -smp 1 -m 512 -net none " + "%s -device iommu-testdev", + qvtd_iommu_args(mode)); + + if (!qvtd_check_caps(qts, mode)) { + qtest_quit(qts); + return; + } + + dev =3D qvtd_setup_qtest_pci_device(qts, &pcibus, &bar); + + /* + * The IOMMU translates an IOVA to a page base, then the page offset + * from the IOVA is added. So GPA =3D page_base + (IOVA & 0xfff). + */ + pa_a =3D (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff); + pa_b =3D (QVTD_PT_VAL_B & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff); + + /* --- Phase 1: Setup and initial DMA (populates IOTLB) --- */ + qvtd_build_translation(qts, mode, dev->devfn); + qvtd_program_regs(qts, Q35_HOST_BRIDGE_IOMMU_ADDR, mode); + + qtest_memset(qts, pa_a, 0, DMA_LEN); + qtest_memset(qts, pa_b, 0, DMA_LEN); + + result =3D qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_a, + DMA_LEN, 0); + g_assert_cmpuint(result, =3D=3D, 0); + + /* --- Phase 2: Modify PTE without invalidation -> stale IOTLB --- */ + qtest_writeq(qts, qvtd_leaf_pte_addr(QVTD_IOVA), + qvtd_make_leaf_pte(QVTD_PT_VAL_B & VTD_PAGE_MASK_4K, mode= )); + qtest_memset(qts, pa_a, 0, DMA_LEN); + qtest_memset(qts, pa_b, 0, DMA_LEN); + + result =3D qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_b, + DMA_LEN, 0); + g_assert_cmpuint(result, =3D=3D, ITD_DMA_ERR_MISMATCH); + + /* --- Phase 3: Invalidate IOTLB -> fresh page walk succeeds --- */ + switch (gran) { + case IOTLB_INV_GLOBAL: + tail =3D qvtd_submit_iotlb_global_inv(qts, Q35_HOST_BRIDGE_IOMMU_A= DDR, + tail); + break; + case IOTLB_INV_DOMAIN: + tail =3D qvtd_submit_iotlb_domain_inv(qts, Q35_HOST_BRIDGE_IOMMU_A= DDR, + QVTD_DOMAIN_ID, tail); + break; + case IOTLB_INV_PAGE: + tail =3D qvtd_submit_iotlb_page_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADD= R, + QVTD_DOMAIN_ID, QVTD_IOVA, 0, + tail); + break; + } + tail =3D qvtd_submit_inv_wait_and_poll(qts, Q35_HOST_BRIDGE_IOMMU_ADDR, + tail); + + qtest_memset(qts, pa_a, 0, DMA_LEN); + qtest_memset(qts, pa_b, 0, DMA_LEN); + + result =3D qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_b, + DMA_LEN, 0); + g_assert_cmpuint(result, =3D=3D, 0); + + g_free(dev); + qpci_free_pc(pcibus); + qtest_quit(qts); +} + +/* + * Page-selectivity test: verify that a page-selective invalidation flushes + * the named page and touches other cached pages only as far as the model + * intends. run_iotlb_inv_test() caches a single entry, so it cannot tell= a + * page-selective flush from a domain-wide or global one; this test caches= two + * pages in the same domain and checks the second one's fate. + * + * The expected fate of the second page depends on the translation level: + * + * - second-level (legacy / scalable-slt): a page-selective descriptor + * evicts only the matching gfn, so IOVA_2 survives. + * - first-level (scalable-flt): QEMU invalidates all first-stage entrie= s of + * the domain on a page-selective descriptor (vtd_hash_remove_by_page() + * returns true for any pgtt=3D=3DFST entry of the domain, matching th= e VT-d + * spec for first-stage IOTLB invalidation), so IOVA_2 is flushed too. + * + * Method: map IOVA -> PA_A and IOVA_2 -> PA_A_2, DMA both to populate two + * IOTLB entries, rewrite both leaf PTEs to PA_B* without invalidating, th= en + * page-invalidate IOVA only. IOVA always re-walks to PA_B. For IOVA_2 we + * verify the DMA against its *original* page PA_A_2: if the entry survive= d, + * the stale cache still serves PA_A_2 (success); if it was flushed, the f= resh + * walk reaches PA_B_2 and mismatches PA_A_2. So a survived entry gives + * success and a flushed entry gives MISMATCH, and we assert whichever the + * mode requires -- catching both an over-matching second-level flush and a + * regression that stopped flushing first-stage entries domain-wide. + */ +static void run_page_selectivity_test(QVTDTransMode mode) +{ + QTestState *qts; + QPCIBus *pcibus; + QPCIDevice *dev; + QPCIBar bar; + uint32_t tail =3D 0; + uint32_t result; + uint64_t pa_a, pa_b, pa_a2, pa_b2; + bool fl_domain_wide =3D (mode =3D=3D QVTD_TM_SCALABLE_FLT); + + if (!qtest_has_machine("q35")) { + g_test_skip("q35 machine not available"); + return; + } + + qts =3D qtest_initf("-machine q35 -smp 1 -m 512 -net none " + "%s -device iommu-testdev", + qvtd_iommu_args(mode)); + + if (!qvtd_check_caps(qts, mode)) { + qtest_quit(qts); + return; + } + + dev =3D qvtd_setup_qtest_pci_device(qts, &pcibus, &bar); + + pa_a =3D (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff); + pa_b =3D (QVTD_PT_VAL_B & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff); + pa_a2 =3D (QVTD_PT_VAL_2 & VTD_PAGE_MASK_4K) + (QVTD_IOVA_2 & 0xfff); + pa_b2 =3D (QVTD_PT_VAL_B & VTD_PAGE_MASK_4K) + (QVTD_IOVA_2 & 0xfff); + + /* --- Setup: IOVA -> PA_A (built by helper) and IOVA_2 -> PA_A_2 --- = */ + qvtd_build_translation(qts, mode, dev->devfn); + qtest_writeq(qts, qvtd_leaf_pte_addr(QVTD_IOVA_2), + qvtd_make_leaf_pte(QVTD_PT_VAL_2 & VTD_PAGE_MASK_4K, mode= )); + qvtd_program_regs(qts, Q35_HOST_BRIDGE_IOMMU_ADDR, mode); + + /* Populate both IOTLB entries. */ + qtest_memset(qts, pa_a, 0, DMA_LEN); + qtest_memset(qts, pa_a2, 0, DMA_LEN); + result =3D qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_a, + DMA_LEN, 0); + g_assert_cmpuint(result, =3D=3D, 0); + result =3D qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA_2, pa_a2, + DMA_LEN, 0); + g_assert_cmpuint(result, =3D=3D, 0); + + /* Rewrite both leaf PTEs to PA_B* without invalidating. */ + qtest_writeq(qts, qvtd_leaf_pte_addr(QVTD_IOVA), + qvtd_make_leaf_pte(QVTD_PT_VAL_B & VTD_PAGE_MASK_4K, mode= )); + qtest_writeq(qts, qvtd_leaf_pte_addr(QVTD_IOVA_2), + qvtd_make_leaf_pte(QVTD_PT_VAL_B & VTD_PAGE_MASK_4K, mode= )); + + /* Page-selective invalidation of IOVA only. */ + tail =3D qvtd_submit_iotlb_page_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADDR, + QVTD_DOMAIN_ID, QVTD_IOVA, 0, tail); + tail =3D qvtd_submit_inv_wait_and_poll(qts, Q35_HOST_BRIDGE_IOMMU_ADDR, + tail); + + /* IOVA was flushed: fresh walk reaches PA_B. */ + qtest_memset(qts, pa_a, 0, DMA_LEN); + qtest_memset(qts, pa_b, 0, DMA_LEN); + result =3D qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_b, + DMA_LEN, 0); + g_assert_cmpuint(result, =3D=3D, 0); + + /* + * IOVA_2's fate, verified against its original page PA_A_2: + * - second-level: entry survives, stale cache serves PA_A_2 -> succ= ess; + * - first-level: entry was flushed domain-wide, fresh walk reaches + * PA_B_2 -> MISMATCH against PA_A_2. + */ + qtest_memset(qts, pa_a2, 0, DMA_LEN); + qtest_memset(qts, pa_b2, 0, DMA_LEN); + result =3D qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA_2, pa_a2, + DMA_LEN, 0); + if (fl_domain_wide) { + g_assert_cmpuint(result, =3D=3D, ITD_DMA_ERR_MISMATCH); + } else { + g_assert_cmpuint(result, =3D=3D, 0); + } + + g_free(dev); + qpci_free_pc(pcibus); + qtest_quit(qts); +} + +/* + * scalable-flt is covered here even though, per the VT-d spec, first-level + * mappings are invalidated with the PASID-based descriptor + * (VTD_INV_DESC_PIOTLB). QEMU keeps first- and second-level mappings in a + * single IOTLB that the legacy VTD_INV_DESC_IOTLB descriptor flushes for + * every level, so this test drives that descriptor across all modes. + * PASID-selective (PIOTLB) invalidation is a separate path, left for a + * follow-up. + */ +static const struct { + const char *name; + QVTDTransMode mode; +} trans_modes[] =3D { + { "legacy", QVTD_TM_LEGACY_TRANS }, + { "scalable-slt", QVTD_TM_SCALABLE_SLT }, + { "scalable-flt", QVTD_TM_SCALABLE_FLT }, +}; + +static const struct { + const char *name; + IOTLBInvGranularity gran; +} granularities[] =3D { + { "global", IOTLB_INV_GLOBAL }, + { "domain", IOTLB_INV_DOMAIN }, + { "page", IOTLB_INV_PAGE }, +}; + +typedef struct { + QVTDTransMode mode; + IOTLBInvGranularity gran; +} TestCase; + +static void test_iotlb_inv(const void *opaque) +{ + const TestCase *tc =3D opaque; + + run_iotlb_inv_test(tc->mode, tc->gran); +} + +static void test_page_selectivity(const void *opaque) +{ + const QVTDTransMode *mode =3D opaque; + + run_page_selectivity_test(*mode); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + for (size_t m =3D 0; m < ARRAY_SIZE(trans_modes); m++) { + for (size_t g =3D 0; g < ARRAY_SIZE(granularities); g++) { + TestCase *tc =3D g_new(TestCase, 1); + char *path; + + tc->mode =3D trans_modes[m].mode; + tc->gran =3D granularities[g].gran; + + path =3D g_strdup_printf("/iommu-testdev/intel/iotlb-inv/%s-%s= ", + granularities[g].name, + trans_modes[m].name); + qtest_add_data_func_full(path, tc, test_iotlb_inv, g_free); + g_free(path); + } + } + + for (size_t m =3D 0; m < ARRAY_SIZE(trans_modes); m++) { + QVTDTransMode *mode =3D g_new(QVTDTransMode, 1); + char *path; + + *mode =3D trans_modes[m].mode; + path =3D g_strdup_printf( + "/iommu-testdev/intel/iotlb-inv/page-selective/%s", + trans_modes[m].name); + qtest_add_data_func_full(path, mode, test_page_selectivity, g_free= ); + g_free(path); + } + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 5e18b947c7..7da66802e1 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -99,7 +99,7 @@ qtests_i386 =3D \ (config_all_devices.has_key('CONFIG_AMD_IOMMU') ? ['amd-iommu-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= '] : []) + \ + config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-intel-test= ', 'iommu-intel-inv-test'] : []) + \ (host_os !=3D 'windows' and = \ config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + = \ (config_all_devices.has_key('CONFIG_PCIE_PORT') and = \ --=20 2.43.0