From: Junjie Cao <[email protected]>

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 <[email protected]>
Reviewed-by: Michael S. Tsirkin <[email protected]>
Signed-off-by: Michael S. Tsirkin <[email protected]>
Message-ID: <[email protected]>
---
 tests/qtest/libqos/qos-intel-iommu.h |  51 +++++++++++++
 tests/qtest/libqos/qos-intel-iommu.c | 108 +++++++++++++++++++++++++++
 2 files changed, 159 insertions(+)

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=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.
@@ -205,4 +215,45 @@ bool qvtd_check_caps(QTestState *qts, QVTDTransMode mode);
 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 */
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"
 
+/* 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)
@@ -232,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)
 {
@@ -529,3 +557,83 @@ QPCIDevice *qvtd_setup_qtest_pci_device(QTestState *qts, 
QPCIBus **pcibus,
 
     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);
+}
-- 
MST


Reply via email to