mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:56:38 +00:00
Merge tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu into staging
pci, vhost, virtio, iommu: features, fixes, cleanups A new sp-mem device New tests for vtd New seg-max-adjust flag for vhost-user-blk Watchdog support for arm/virt Fixes, cleanups all over the place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # -----BEGIN PGP SIGNATURE----- # # iQFDBAABCgAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmpKWdQPHG1zdEByZWRo # YXQuY29tAAoJECgfDbjSjVRpMZgIALaDhbZFvYbdvDIzhX2MkSGWyMOU3ECsBojM # p6g8HimtYlRV0ep468fvnKdWchAncozMKawAGlUZoWQ5jX8rncBvR2cRE9oOQ7dd # JPOiz0bjB9USebD9NhQ61bdi0nlQHAcH8rhEt3qrw2j8LZOIeE63pEFE3NxIMO2e # gO+ECDMBGfXsupDM5KCfPRzXPDy17QwI7BYYU7iY2T505/Xkr+ICLwfQ/VLPCMwY # FG8pqJH/POexARuNaQWTAGpceAf/Pb0cYg9aKd6cxgxyBzP1fpAmL+C1e/cK5Zop # n8AxJfTD/HPAqWDA+YHAijfFdZQ64Hjor+5kGkgurovlYc48iP4= # =jg7f # -----END PGP SIGNATURE----- # gpg: Signature made Sun 05 Jul 2026 15:19:16 CEST # gpg: using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469 # gpg: issuer "mst@redhat.com" # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [full] # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" [full] # Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67 # Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469 * tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu: (44 commits) virtio-net: validate RSS indirections_len in post_load vhost-user-blk: add seg-max-adjust flag vhost-user-scmi: free vhost virtqueue array on cleanup hw/virtio-crypto: enforce max akcipher key length vhost-user: Guarantee that memory regions do not overlap tests: acpi: arm/virt: update expected GTDT blob tests: acpi: arm/virt: add GTDT watchdog table test case tests: acpi: arm/virt: whitelist GTDT table tests: acpi: arm/virt: update expected WDAT blob tests: acpi: arm/virt: add WDAT table test case tests: acpi: arm/virt: whitelist new WDAT table arm: virt: add support for WDAT based watchdog acpi: introduce WDAT table for GWDT arm: sbsa-gwdt: add 'wdat' option arm: virt: create sbsa-gwdt watchdog arm: sbsa_gwdt: rename device type to sbsa-gwdt arm: add tracing events to sbsa_gwdt arm: sbsa_gwdt: fixup default "clock-frequency" vdpa: fix use-after-free of vqs in vhost_vdpa_device_unrealize vhost-user-base: clean up vhost_dev on realize failure ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
BIN
tests/data/acpi/aarch64/virt/GTDT.gwdt
Normal file
BIN
tests/data/acpi/aarch64/virt/GTDT.gwdt
Normal file
Binary file not shown.
BIN
tests/data/acpi/aarch64/virt/WDAT.wdat
Normal file
BIN
tests/data/acpi/aarch64/virt/WDAT.wdat
Normal file
Binary file not shown.
BIN
tests/data/acpi/x86/q35/DSDT.spmem
Normal file
BIN
tests/data/acpi/x86/q35/DSDT.spmem
Normal file
Binary file not shown.
BIN
tests/data/acpi/x86/q35/SRAT.spmem
Normal file
BIN
tests/data/acpi/x86/q35/SRAT.spmem
Normal file
Binary file not shown.
@@ -1416,6 +1416,26 @@ static void test_acpi_q35_tcg_numamem(void)
|
||||
free_test_data(&data);
|
||||
}
|
||||
|
||||
static void test_acpi_q35_tcg_sp_mem(void)
|
||||
{
|
||||
test_data data = {};
|
||||
|
||||
data.machine = MACHINE_Q35;
|
||||
data.arch = "x86",
|
||||
data.variant = ".spmem";
|
||||
test_acpi_one(" -m 128M,slots=4,maxmem=1G"
|
||||
" -object memory-backend-ram,id=ram0,size=128M"
|
||||
" -numa node,nodeid=0,memdev=ram0"
|
||||
" -numa node,nodeid=1"
|
||||
" -numa node,nodeid=2"
|
||||
" -object memory-backend-ram,id=spm0,size=128M"
|
||||
" -object memory-backend-ram,id=spm1,size=128M"
|
||||
" -device sp-mem,id=sp0,memdev=spm0,node=1"
|
||||
" -device sp-mem,id=sp1,memdev=spm1,node=2",
|
||||
&data);
|
||||
free_test_data(&data);
|
||||
}
|
||||
|
||||
static void test_acpi_q35_kvm_xapic(void)
|
||||
{
|
||||
test_data data = {};
|
||||
@@ -2275,6 +2295,43 @@ static void test_acpi_aarch64_virt_tcg_msi_gicv2m(void)
|
||||
free_test_data(&data);
|
||||
}
|
||||
|
||||
static void test_acpi_aarch64_virt_tcg_wdat(void)
|
||||
{
|
||||
test_data data = {
|
||||
.machine = "virt",
|
||||
.arch = "aarch64",
|
||||
.variant = ".wdat",
|
||||
.tcg_only = true,
|
||||
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
|
||||
test_acpi_one("-cpu cortex-a57 "
|
||||
"-device sbsa-gwdt,wdat=on", &data);
|
||||
free_test_data(&data);
|
||||
}
|
||||
|
||||
static void test_acpi_aarch64_virt_tcg_gtdt_wd(void)
|
||||
{
|
||||
test_data data = {
|
||||
.machine = "virt",
|
||||
.arch = "aarch64",
|
||||
.variant = ".gwdt",
|
||||
.tcg_only = true,
|
||||
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
|
||||
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
|
||||
.ram_start = 0x40000000ULL,
|
||||
.scan_len = 128ULL * MiB,
|
||||
};
|
||||
|
||||
test_acpi_one("-cpu cortex-a57 -device sbsa-gwdt", &data);
|
||||
free_test_data(&data);
|
||||
}
|
||||
|
||||
static void test_acpi_q35_viot(void)
|
||||
{
|
||||
test_data data = {
|
||||
@@ -2807,6 +2864,7 @@ int main(int argc, char *argv[])
|
||||
if (strcmp(arch, "i386")) {
|
||||
qtest_add_func("acpi/q35/memhp", test_acpi_q35_tcg_memhp);
|
||||
qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm);
|
||||
qtest_add_func("acpi/q35/sp-mem", test_acpi_q35_tcg_sp_mem);
|
||||
qtest_add_func("acpi/q35/acpihmat",
|
||||
test_acpi_q35_tcg_acpi_hmat);
|
||||
qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64);
|
||||
@@ -2893,6 +2951,10 @@ int main(int argc, char *argv[])
|
||||
qtest_add_func("acpi/virt/smmuv3-dev",
|
||||
test_acpi_aarch64_virt_smmuv3_dev);
|
||||
}
|
||||
qtest_add_func("acpi/virt/acpi-watchdog",
|
||||
test_acpi_aarch64_virt_tcg_wdat);
|
||||
qtest_add_func("acpi/virt/gwdt-watchdog",
|
||||
test_acpi_aarch64_virt_tcg_gtdt_wd);
|
||||
}
|
||||
} else if (strcmp(arch, "riscv64") == 0) {
|
||||
if (has_tcg && qtest_has_device("virtio-blk-pci")) {
|
||||
|
||||
129
tests/qtest/e820-test.c
Normal file
129
tests/qtest/e820-test.c
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* qtest e820 fw_cfg test case
|
||||
*
|
||||
* Validate the "etc/e820" fw_cfg table that QEMU hands to the firmware.
|
||||
*
|
||||
* Copyright (c) 2026 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Authors:
|
||||
* FangSheng Huang <FangSheng.Huang@amd.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "libqtest.h"
|
||||
#include "libqos/fw_cfg.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/units.h"
|
||||
|
||||
/* e820 entry layout and types (cf. hw/i386/e820_memory_layout.h) */
|
||||
#define E820_RAM 1
|
||||
#define E820_SOFT_RESERVED 0xefffffff
|
||||
|
||||
struct e820_entry {
|
||||
uint64_t address;
|
||||
uint64_t length;
|
||||
uint32_t type;
|
||||
} QEMU_PACKED;
|
||||
|
||||
#define E820_MAX_ENTRIES 128
|
||||
|
||||
/*
|
||||
* Read and structurally validate "etc/e820": the file is a packed array
|
||||
* of struct e820_entry, so its size must be a whole multiple of the entry
|
||||
* size and every entry must have a non-zero length. Returns the entry
|
||||
* count and fills @table.
|
||||
*/
|
||||
static size_t get_e820_table(QFWCFG *fw_cfg, struct e820_entry *table)
|
||||
{
|
||||
size_t filesize, n, i;
|
||||
|
||||
filesize = qfw_cfg_get_file(fw_cfg, "etc/e820", table,
|
||||
E820_MAX_ENTRIES * sizeof(*table));
|
||||
g_assert_cmpint(filesize, >, 0);
|
||||
g_assert_cmpint(filesize % sizeof(struct e820_entry), ==, 0);
|
||||
|
||||
n = filesize / sizeof(struct e820_entry);
|
||||
g_assert_cmpint(n, <=, E820_MAX_ENTRIES);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
g_assert_cmpint(le64_to_cpu(table[i].length), >, 0);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void test_e820_basic(void)
|
||||
{
|
||||
struct e820_entry table[E820_MAX_ENTRIES];
|
||||
QFWCFG *fw_cfg;
|
||||
QTestState *s;
|
||||
size_t n, i;
|
||||
bool found_ram = false, found_soft_reserved = false;
|
||||
|
||||
s = qtest_init("-machine q35 -m 256M");
|
||||
fw_cfg = pc_fw_cfg_init(s);
|
||||
|
||||
n = get_e820_table(fw_cfg, table);
|
||||
for (i = 0; i < n; i++) {
|
||||
switch (le32_to_cpu(table[i].type)) {
|
||||
case E820_RAM:
|
||||
found_ram = true;
|
||||
break;
|
||||
case E820_SOFT_RESERVED:
|
||||
found_soft_reserved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* baseline: RAM present, no SOFT_RESERVED range */
|
||||
g_assert_true(found_ram);
|
||||
g_assert_false(found_soft_reserved);
|
||||
|
||||
pc_fw_cfg_uninit(fw_cfg);
|
||||
qtest_quit(s);
|
||||
}
|
||||
|
||||
static void test_e820_sp_mem(void)
|
||||
{
|
||||
struct e820_entry table[E820_MAX_ENTRIES];
|
||||
QFWCFG *fw_cfg;
|
||||
QTestState *s;
|
||||
size_t n, i;
|
||||
int soft_reserved = 0;
|
||||
uint64_t soft_reserved_len = 0;
|
||||
|
||||
s = qtest_init("-machine q35 -m 256M,slots=2,maxmem=2G "
|
||||
"-object memory-backend-ram,id=ram0,size=256M "
|
||||
"-numa node,nodeid=0,memdev=ram0 "
|
||||
"-object memory-backend-ram,id=spm0,size=128M "
|
||||
"-device sp-mem,id=sp0,memdev=spm0,node=0");
|
||||
fw_cfg = pc_fw_cfg_init(s);
|
||||
|
||||
n = get_e820_table(fw_cfg, table);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (le32_to_cpu(table[i].type) == E820_SOFT_RESERVED) {
|
||||
soft_reserved++;
|
||||
soft_reserved_len = le64_to_cpu(table[i].length);
|
||||
}
|
||||
}
|
||||
|
||||
/* exactly one SOFT_RESERVED range, sized to the backend */
|
||||
g_assert_cmpint(soft_reserved, ==, 1);
|
||||
g_assert_cmpint(soft_reserved_len, ==, 128 * MiB);
|
||||
|
||||
pc_fw_cfg_uninit(fw_cfg);
|
||||
qtest_quit(s);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
qtest_add_func("e820/basic", test_e820_basic);
|
||||
qtest_add_func("e820/sp-mem", test_e820_sp_mem);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
346
tests/qtest/iommu-intel-inv-test.c
Normal file
346
tests/qtest/iommu-intel-inv-test.c
Normal file
@@ -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 <junjie.cao@intel.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/pci.h"
|
||||
#include "libqos/pci-pc.h"
|
||||
#include "hw/i386/intel_iommu_internal.h"
|
||||
#include "hw/misc/iommu-testdev.h"
|
||||
#include "libqos/qos-intel-iommu.h"
|
||||
#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 mapping
|
||||
* 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=PA_A) -> success (IOTLB populates cache)
|
||||
* 3. Modify PTE: IOVA -> PA_B (no invalidation)
|
||||
* 4. DMA(gpa=PA_B) -> MISMATCH (stale IOTLB directs write to PA_A)
|
||||
* 5. Issue IOTLB invalidation + wait
|
||||
* 6. DMA(gpa=PA_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 gran)
|
||||
{
|
||||
QTestState *qts;
|
||||
QPCIBus *pcibus;
|
||||
QPCIDevice *dev;
|
||||
QPCIBar bar;
|
||||
uint32_t tail = 0;
|
||||
uint32_t result;
|
||||
uint64_t pa_a, pa_b;
|
||||
|
||||
if (!qtest_has_machine("q35")) {
|
||||
g_test_skip("q35 machine not available");
|
||||
return;
|
||||
}
|
||||
|
||||
qts = 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 = 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 = page_base + (IOVA & 0xfff).
|
||||
*/
|
||||
pa_a = (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff);
|
||||
pa_b = (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 = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_a,
|
||||
DMA_LEN, 0);
|
||||
g_assert_cmpuint(result, ==, 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 = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_b,
|
||||
DMA_LEN, 0);
|
||||
g_assert_cmpuint(result, ==, ITD_DMA_ERR_MISMATCH);
|
||||
|
||||
/* --- Phase 3: Invalidate IOTLB -> fresh page walk succeeds --- */
|
||||
switch (gran) {
|
||||
case IOTLB_INV_GLOBAL:
|
||||
tail = qvtd_submit_iotlb_global_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
|
||||
tail);
|
||||
break;
|
||||
case IOTLB_INV_DOMAIN:
|
||||
tail = qvtd_submit_iotlb_domain_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
|
||||
QVTD_DOMAIN_ID, tail);
|
||||
break;
|
||||
case IOTLB_INV_PAGE:
|
||||
tail = qvtd_submit_iotlb_page_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
|
||||
QVTD_DOMAIN_ID, QVTD_IOVA, 0,
|
||||
tail);
|
||||
break;
|
||||
}
|
||||
tail = 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 = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_b,
|
||||
DMA_LEN, 0);
|
||||
g_assert_cmpuint(result, ==, 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 entries of
|
||||
* the domain on a page-selective descriptor (vtd_hash_remove_by_page()
|
||||
* returns true for any pgtt==FST entry of the domain, matching the 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, then
|
||||
* 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 survived,
|
||||
* the stale cache still serves PA_A_2 (success); if it was flushed, the fresh
|
||||
* 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 = 0;
|
||||
uint32_t result;
|
||||
uint64_t pa_a, pa_b, pa_a2, pa_b2;
|
||||
bool fl_domain_wide = (mode == QVTD_TM_SCALABLE_FLT);
|
||||
|
||||
if (!qtest_has_machine("q35")) {
|
||||
g_test_skip("q35 machine not available");
|
||||
return;
|
||||
}
|
||||
|
||||
qts = 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 = qvtd_setup_qtest_pci_device(qts, &pcibus, &bar);
|
||||
|
||||
pa_a = (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff);
|
||||
pa_b = (QVTD_PT_VAL_B & VTD_PAGE_MASK_4K) + (QVTD_IOVA & 0xfff);
|
||||
pa_a2 = (QVTD_PT_VAL_2 & VTD_PAGE_MASK_4K) + (QVTD_IOVA_2 & 0xfff);
|
||||
pa_b2 = (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 = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_a,
|
||||
DMA_LEN, 0);
|
||||
g_assert_cmpuint(result, ==, 0);
|
||||
result = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA_2, pa_a2,
|
||||
DMA_LEN, 0);
|
||||
g_assert_cmpuint(result, ==, 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 = qvtd_submit_iotlb_page_inv(qts, Q35_HOST_BRIDGE_IOMMU_ADDR,
|
||||
QVTD_DOMAIN_ID, QVTD_IOVA, 0, tail);
|
||||
tail = 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 = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA, pa_b,
|
||||
DMA_LEN, 0);
|
||||
g_assert_cmpuint(result, ==, 0);
|
||||
|
||||
/*
|
||||
* IOVA_2's fate, verified against its original page PA_A_2:
|
||||
* - second-level: entry survives, stale cache serves PA_A_2 -> success;
|
||||
* - 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 = qos_iommu_testdev_trigger_dma(dev, bar, QVTD_IOVA_2, pa_a2,
|
||||
DMA_LEN, 0);
|
||||
if (fl_domain_wide) {
|
||||
g_assert_cmpuint(result, ==, ITD_DMA_ERR_MISMATCH);
|
||||
} else {
|
||||
g_assert_cmpuint(result, ==, 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[] = {
|
||||
{ "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[] = {
|
||||
{ "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 = opaque;
|
||||
|
||||
run_iotlb_inv_test(tc->mode, tc->gran);
|
||||
}
|
||||
|
||||
static void test_page_selectivity(const void *opaque)
|
||||
{
|
||||
const QVTDTransMode *mode = opaque;
|
||||
|
||||
run_page_selectivity_test(*mode);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
for (size_t m = 0; m < ARRAY_SIZE(trans_modes); m++) {
|
||||
for (size_t g = 0; g < ARRAY_SIZE(granularities); g++) {
|
||||
TestCase *tc = g_new(TestCase, 1);
|
||||
char *path;
|
||||
|
||||
tc->mode = trans_modes[m].mode;
|
||||
tc->gran = granularities[g].gran;
|
||||
|
||||
path = 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 = 0; m < ARRAY_SIZE(trans_modes); m++) {
|
||||
QVTDTransMode *mode = g_new(QVTDTransMode, 1);
|
||||
char *path;
|
||||
|
||||
*mode = trans_modes[m].mode;
|
||||
path = 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();
|
||||
}
|
||||
@@ -24,82 +24,6 @@ static uint64_t intel_iommu_expected_gpa(uint64_t iova)
|
||||
return (QVTD_PT_VAL & VTD_PAGE_MASK_4K) + (iova & 0xfff);
|
||||
}
|
||||
|
||||
static void save_fn(QPCIDevice *dev, int devfn, void *data)
|
||||
{
|
||||
QPCIDevice **pdev = (QPCIDevice **) data;
|
||||
|
||||
*pdev = dev;
|
||||
}
|
||||
|
||||
static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus,
|
||||
QPCIBar *bar)
|
||||
{
|
||||
QPCIDevice *dev = NULL;
|
||||
|
||||
*pcibus = qpci_new_pc(qts, NULL);
|
||||
g_assert(*pcibus != NULL);
|
||||
|
||||
qpci_device_foreach(*pcibus, IOMMU_TESTDEV_VENDOR_ID,
|
||||
IOMMU_TESTDEV_DEVICE_ID, save_fn, &dev);
|
||||
|
||||
g_assert(dev);
|
||||
qpci_device_enable(dev);
|
||||
*bar = qpci_iomap(dev, 0, NULL);
|
||||
g_assert_false(bar->is_io);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static const char *qvtd_iommu_args(QVTDTransMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case QVTD_TM_SCALABLE_FLT:
|
||||
return "-device intel-iommu,scalable-mode=on,fsts=on ";
|
||||
case QVTD_TM_SCALABLE_PT:
|
||||
case QVTD_TM_SCALABLE_SLT:
|
||||
return "-device intel-iommu,scalable-mode=on ";
|
||||
default:
|
||||
return "-device intel-iommu ";
|
||||
}
|
||||
}
|
||||
|
||||
static bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode)
|
||||
{
|
||||
uint64_t ecap = qtest_readq(qts,
|
||||
Q35_HOST_BRIDGE_IOMMU_ADDR + DMAR_ECAP_REG);
|
||||
|
||||
/* All scalable modes require SMTS */
|
||||
if (qvtd_is_scalable(mode) && !(ecap & VTD_ECAP_SMTS)) {
|
||||
g_test_skip("ECAP.SMTS not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case QVTD_TM_SCALABLE_PT:
|
||||
if (!(ecap & VTD_ECAP_PT)) {
|
||||
g_test_skip("ECAP.PT not supported");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case QVTD_TM_SCALABLE_SLT:
|
||||
if (!(ecap & VTD_ECAP_SSTS)) {
|
||||
g_test_skip("ECAP.SSTS not supported");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case QVTD_TM_SCALABLE_FLT:
|
||||
if (!(ecap & VTD_ECAP_FSTS)) {
|
||||
g_test_skip("ECAP.FSTS not supported");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void run_intel_iommu_translation(const QVTDTestConfig *cfg)
|
||||
{
|
||||
QTestState *qts;
|
||||
@@ -124,7 +48,7 @@ static void run_intel_iommu_translation(const QVTDTestConfig *cfg)
|
||||
}
|
||||
|
||||
/* Setup and configure IOMMU-testdev PCI device */
|
||||
dev = setup_qtest_pci_device(qts, &pcibus, &bar);
|
||||
dev = qvtd_setup_qtest_pci_device(qts, &pcibus, &bar);
|
||||
g_assert(dev);
|
||||
|
||||
g_test_message("### Intel IOMMU translation mode=%d ###", cfg->trans_mode);
|
||||
|
||||
@@ -11,9 +11,14 @@
|
||||
#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"
|
||||
|
||||
/* 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
|
||||
|
||||
uint32_t qvtd_expected_dma_result(QVTDTestContext *ctx)
|
||||
@@ -231,6 +236,30 @@ static uint64_t qvtd_get_fl_pte_attrs(bool is_leaf)
|
||||
return attrs;
|
||||
}
|
||||
|
||||
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() writes,
|
||||
* so a rewritten PTE stays consistent with the initial mapping. US=1 in
|
||||
* the first-level format is required because the PASID entry runs with
|
||||
* SRE=0, which makes every DMA appear as a user-mode access
|
||||
* (VT-d 3.6.2 / 9.9).
|
||||
*/
|
||||
if (mode == QVTD_TM_SCALABLE_FLT) {
|
||||
attrs = qvtd_get_fl_pte_attrs(true);
|
||||
} else {
|
||||
attrs = qvtd_get_pte_attrs();
|
||||
}
|
||||
return (pa & VTD_PAGE_MASK_4K) | attrs;
|
||||
}
|
||||
|
||||
void qvtd_setup_translation_tables(QTestState *qts, uint64_t iova,
|
||||
QVTDTransMode mode)
|
||||
{
|
||||
@@ -452,3 +481,159 @@ void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *qvtd_iommu_args(QVTDTransMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case QVTD_TM_SCALABLE_FLT:
|
||||
return "-device intel-iommu,scalable-mode=on,fsts=on ";
|
||||
case QVTD_TM_SCALABLE_PT:
|
||||
case QVTD_TM_SCALABLE_SLT:
|
||||
return "-device intel-iommu,scalable-mode=on ";
|
||||
default:
|
||||
return "-device intel-iommu ";
|
||||
}
|
||||
}
|
||||
|
||||
bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode)
|
||||
{
|
||||
uint64_t ecap = qtest_readq(qts,
|
||||
Q35_HOST_BRIDGE_IOMMU_ADDR + DMAR_ECAP_REG);
|
||||
|
||||
/* All scalable modes require SMTS */
|
||||
if (qvtd_is_scalable(mode) && !(ecap & VTD_ECAP_SMTS)) {
|
||||
g_test_skip("ECAP.SMTS not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case QVTD_TM_SCALABLE_PT:
|
||||
if (!(ecap & VTD_ECAP_PT)) {
|
||||
g_test_skip("ECAP.PT not supported");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case QVTD_TM_SCALABLE_SLT:
|
||||
if (!(ecap & VTD_ECAP_SSTS)) {
|
||||
g_test_skip("ECAP.SSTS not supported");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case QVTD_TM_SCALABLE_FLT:
|
||||
if (!(ecap & VTD_ECAP_FSTS)) {
|
||||
g_test_skip("ECAP.FSTS not supported");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void qvtd_save_pci_dev(QPCIDevice *dev, int devfn, void *data)
|
||||
{
|
||||
QPCIDevice **pdev = (QPCIDevice **)data;
|
||||
|
||||
*pdev = dev;
|
||||
}
|
||||
|
||||
QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qts, QPCIBus **pcibus,
|
||||
QPCIBar *bar)
|
||||
{
|
||||
QPCIDevice *dev = NULL;
|
||||
|
||||
*pcibus = qpci_new_pc(qts, NULL);
|
||||
g_assert(*pcibus != NULL);
|
||||
|
||||
qpci_device_foreach(*pcibus, IOMMU_TESTDEV_VENDOR_ID,
|
||||
IOMMU_TESTDEV_DEVICE_ID, qvtd_save_pci_dev, &dev);
|
||||
|
||||
g_assert(dev);
|
||||
qpci_device_enable(dev);
|
||||
*bar = qpci_iomap(dev, 0, NULL);
|
||||
g_assert_false(bar->is_io);
|
||||
|
||||
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 = QVTD_IQ_BASE + (uint64_t)tail * QVTD_IQ_DESC_SIZE;
|
||||
|
||||
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_base,
|
||||
uint32_t tail)
|
||||
{
|
||||
uint64_t lo, hi;
|
||||
uint32_t status = 0;
|
||||
int i;
|
||||
|
||||
qtest_writel(qts, QVTD_INV_WAIT_ADDR, 0);
|
||||
|
||||
lo = VTD_INV_DESC_WAIT | VTD_INV_DESC_WAIT_SW |
|
||||
((uint64_t)QVTD_INV_WAIT_DATA << VTD_INV_DESC_WAIT_DATA_SHIFT);
|
||||
hi = QVTD_INV_WAIT_ADDR;
|
||||
|
||||
tail = qvtd_submit_inv_desc(qts, iommu_base, lo, hi, tail);
|
||||
|
||||
for (i = 0; i < QVTD_INV_WAIT_POLL_MAX_ITERS; i++) {
|
||||
status = qtest_readl(qts, QVTD_INV_WAIT_ADDR);
|
||||
if (status == QVTD_INV_WAIT_DATA) {
|
||||
return tail;
|
||||
}
|
||||
g_usleep(QVTD_INV_WAIT_POLL_INTERVAL_US);
|
||||
}
|
||||
|
||||
g_assert_cmphex(status, ==, 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 = 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 = 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 = 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=0 → 4 KB, am=9 → 2 MB, am=18 → 1 GB.
|
||||
* IH (hi[6]) is left clear, requesting full invalidation
|
||||
* including non-leaf paging-structure caches.
|
||||
*/
|
||||
uint64_t hi = (addr & ~0xfffULL) | (am & 0x3fULL);
|
||||
|
||||
return qvtd_submit_inv_desc(qts, iommu_base, lo, hi, tail);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,16 @@
|
||||
*/
|
||||
#define QVTD_IQ_BASE (QVTD_MEM_BASE + 0x00020000)
|
||||
#define QVTD_IQ_QS 0 /* QS=0 → 256 entries */
|
||||
#define QVTD_IQ_DESC_SIZE 16 /* 128-bit descriptor (iq_dw=0) */
|
||||
#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
|
||||
|
||||
/*
|
||||
* Fault Event MSI configuration.
|
||||
@@ -182,4 +192,68 @@ void qvtd_run_translation_case(QTestState *qts, QPCIDevice *dev,
|
||||
QPCIBar bar, uint64_t iommu_base,
|
||||
const QVTDTestConfig *cfg);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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_base,
|
||||
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=0 → single 4 KB page).
|
||||
*/
|
||||
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 */
|
||||
|
||||
@@ -58,6 +58,7 @@ qtests_i386 = \
|
||||
(config_all_devices.has_key('CONFIG_AHCI_ICH9') ? ['tco-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_FDC_ISA') ? ['fdc-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_I440FX') ? ['fw_cfg-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_Q35') ? ['e820-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['vmcoreinfo-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_Q35') ? ['dump-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \
|
||||
@@ -99,7 +100,7 @@ qtests_i386 = \
|
||||
(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 != 'windows' and \
|
||||
config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_PCIE_PORT') and \
|
||||
|
||||
Reference in New Issue
Block a user