qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [RFC RESEND v5 0/4] hw/misc: Introduce a generalized IOMMU test framework
@ 2025-11-26 15:45 Tao Tang
  2025-11-26 15:45 ` [RFC RESEND v5 1/4] hw/arm/smmuv3: Extract common definitions to smmuv3-common.h Tao Tang
                   ` (3 more replies)
  0 siblings, 4 replies; 23+ messages in thread
From: Tao Tang @ 2025-11-26 15:45 UTC (permalink / raw)
  To: Paolo Bonzini, Fabiano Rosas, Laurent Vivier, Eric Auger,
	Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Jean-Philippe Brucker, Mostafa Saleh,
	CLEMENT MATHIEU--DRIF, Tao Tang

This is v5 of the IOMMU test framework. This series builds upon v3 by adding a
shared smmuv3-common.h so the model and libqos consume the same STE/CD and
register definitions. V5 also removes redundant iommu-testdev registers,
introduces the DMA doorbell helper / “not armed” status, optimizes code style
and refreshes the libqos/qtest stack so it uses the shared header and the
simplified device API.

Motivation
----------

Currently, thoroughly testing IOMMU emulation (e.g., ARM SMMUv3) requires
a significant software stack. We need to boot a full guest operating
system (like Linux) with the appropriate drivers (e.g., IOMMUFD) and rely
on firmware (e.g., ACPI with IORT tables or Hafnium) to correctly
configure the IOMMU and orchestrate DMA from a peripheral device.

This dependency on a complex software stack presents several challenges:

* High Barrier to Entry: Writing targeted tests for specific IOMMU
    features (like fault handling, specific translation regimes, etc.)
    becomes cumbersome.

* Difficult to Debug: It's hard to distinguish whether a bug originates
    from the IOMMU emulation itself, the guest driver, the firmware
    tables, or the guest kernel's configuration.

* Slow Iteration: The need to boot a full guest OS slows down the
    development and testing cycle.

The primary goal of this work is to create a lightweight, self-contained
testing environment that allows us to exercise the IOMMU's core logic
directly at the qtest level, removing the need for any guest-side software.

Our Approach: A Dedicated Test Framework
-----------------------------------------

To achieve this, we introduce three main components:

* A minimal hardware device: iommu-testdev
* A reusable IOMMU helper library: libqos/qos-smmuv3
* A comprehensive qtest suite: iommu-smmuv3-test

The iommu-testdev is intentionally not a conformant, general-purpose PCIe
or platform device. It is a purpose-built, highly simplified "DMA engine"
designed to be analogous to a minimal PCIe Root Complex that bypasses the
full, realistic topology (Host Bridges, Switches, Endpoints) to provide a
direct, programmable path for a DMA request to reach the IOMMU. Its sole
purpose is to trigger a DMA transaction when its registers are written to,
making it perfectly suited for direct control from a test environment like
qtest.

The Qtest Framework
-------------------

The new qtest (iommu-smmuv3-test.c) serves as the "bare-metal driver"
for both the IOMMU and the iommu-testdev. It leverages the libqos helper
library to manually perform all the setup that would typically be handled
by the guest kernel and firmware, but in a completely controlled and
predictable manner:

1.  IOMMU Configuration: It directly initializes the SMMU's registers to a
    known state using helper functions from qos-smmuv3.

2.  Translation Structure Setup: It uses the libqos library to construct
    the necessary translation structures in memory, including Stream Table
    Entries (STEs), Context Descriptors (CDs), and Page Tables (PTEs).

3.  DMA Trigger: It programs the iommu-testdev to initiate a DMA operation
    targeting a specific IOVA with configurable attributes.

4.  Verification: It waits for the transaction to complete and verifies
    that the memory was accessed correctly after address translation by
    the IOMMU.

This framework provides a solid and extensible foundation for validating
the IOMMU's core translation paths. The current test suite covers:

- Stage 1 only translation (VA -> PA via CD page tables)
- Stage 2 only translation (IPA -> PA via STE S2 tables)
- Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)

The infrastructure is designed to be easily extended to support multiple
security spaces (Non-Secure, Secure, Root, Realm) and additional IOMMU
features.


Testing:
--------
QTEST_QEMU_BINARY=qemu-system-aarch64 tests/qtest/iommu-smmuv3-test --tap -k


Major Changes from v4 to v5:
-----------------------------
 - Remove a duplicated patch that was accidentally included in v4.


Major Changes from v3 to v4:
-----------------------------

1. Added shared smmuv3-common.h so both the device and libqos consume the same
   STE/CD/register definitions as Alex suggested [1]
2. Slimmed iommu-testdev down to a pure DMA trigger with a tighter MMIO
   contract (new doorbell helper, simplified attributes/errors).
3. Updated `qos-smmuv3` and the qtest so they include the common header,
   honor per-test expected results, and rely solely on the streamlined device
   interface.
4. Compacted changes of v2 to v3.

[1] https://lore.kernel.org/qemu-devel/87zf8jk244.fsf@draig.linaro.org/


Major Changes from v2 to v3:
-----------------------------

1. Generalization/Renaming: rebranded `smmu-testdev` → `iommu-testdev` (code,
   headers, docs) to reflect the broadened scope.
2. Separation of concerns: iommu-testdev is now a pure DMA trigger; all
   SMMUv3-specific setup (STE/CD/page tables, multi-mode support, space offsets)
   lives in `qos-smmuv3.{c,h}` and is consumed by the new qtest.
3. Improved modularity & coverage: the stacked design (device + helper + qtest)
   made it straightforward to add S1/S2/Nested tests, a cleaner config system,
   and clearer validation logic.
4. Code/documentation quality: added tracepoints, better error handling/naming,
   and refreshed `docs/specs/iommu-testdev.rst` with the new layout.

Future Work
-----------

The current implementation focuses on basic translation path validation
in the Non-Secure address space. Future extensions could include:

* Multi-space testing (Secure, Root, Realm) for SMMUv3
* Support for other IOMMU types (Intel VT-d, AMD-Vi, RISC-V IOMMU)

Tao Tang (4):
  hw/arm/smmuv3: Extract common definitions to smmuv3-common.h
  hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing
  tests/qtest/libqos: Add SMMUv3 helper library
  tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev

 docs/specs/index.rst            |   1 +
 docs/specs/iommu-testdev.rst    | 109 +++++
 hw/arm/smmuv3-internal.h        | 255 +----------
 hw/misc/Kconfig                 |   5 +
 hw/misc/iommu-testdev.c         | 278 ++++++++++++
 hw/misc/meson.build             |   1 +
 hw/misc/trace-events            |  10 +
 include/hw/arm/smmuv3-common.h  | 461 ++++++++++++++++++++
 include/hw/misc/iommu-testdev.h |  70 +++
 tests/qtest/iommu-smmuv3-test.c | 114 +++++
 tests/qtest/libqos/meson.build  |   3 +
 tests/qtest/libqos/qos-smmuv3.c | 731 ++++++++++++++++++++++++++++++++
 tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
 tests/qtest/meson.build         |   1 +
 14 files changed, 2052 insertions(+), 254 deletions(-)
 create mode 100644 docs/specs/iommu-testdev.rst
 create mode 100644 hw/misc/iommu-testdev.c
 create mode 100644 include/hw/arm/smmuv3-common.h
 create mode 100644 include/hw/misc/iommu-testdev.h
 create mode 100644 tests/qtest/iommu-smmuv3-test.c
 create mode 100644 tests/qtest/libqos/qos-smmuv3.c
 create mode 100644 tests/qtest/libqos/qos-smmuv3.h

-- 
2.34.1



^ permalink raw reply	[flat|nested] 23+ messages in thread

* [RFC RESEND v5 1/4] hw/arm/smmuv3: Extract common definitions to smmuv3-common.h
  2025-11-26 15:45 [RFC RESEND v5 0/4] hw/misc: Introduce a generalized IOMMU test framework Tao Tang
@ 2025-11-26 15:45 ` Tao Tang
  2025-12-04 18:19   ` Pierrick Bouvier
  2025-11-26 15:45 ` [RFC RESEND v5 2/4] hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing Tao Tang
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 23+ messages in thread
From: Tao Tang @ 2025-11-26 15:45 UTC (permalink / raw)
  To: Paolo Bonzini, Fabiano Rosas, Laurent Vivier, Eric Auger,
	Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Jean-Philippe Brucker, Mostafa Saleh,
	CLEMENT MATHIEU--DRIF, Tao Tang

Move register definitions, command enums, and Stream Table Entry (STE) /
Context Descriptor (CD) structure definitions from the internal header
hw/arm/smmuv3-internal.h to a new common header
include/hw/arm/smmuv3-common.h.

This allows other components, such as generic SMMUv3 tests or test devices,
to utilize these definitions without including the specific SMMUv3 device
internal state.

In addition, refactor the STE and CD field definitions to consistently use
the 'hw/registerfields.h' API and introduce corresponding setter macros to
facilitate structure manipulation.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 hw/arm/smmuv3-internal.h       | 255 +-----------------
 include/hw/arm/smmuv3-common.h | 461 +++++++++++++++++++++++++++++++++
 2 files changed, 462 insertions(+), 254 deletions(-)
 create mode 100644 include/hw/arm/smmuv3-common.h

diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
index b6b7399347..8679ab6d09 100644
--- a/hw/arm/smmuv3-internal.h
+++ b/hw/arm/smmuv3-internal.h
@@ -23,6 +23,7 @@
 
 #include "hw/registerfields.h"
 #include "hw/arm/smmu-common.h"
+#include "hw/arm/smmuv3-common.h"
 
 typedef enum SMMUTranslationStatus {
     SMMU_TRANS_DISABLE,
@@ -38,147 +39,6 @@ typedef enum SMMUTranslationClass {
     SMMU_CLASS_IN,
 } SMMUTranslationClass;
 
-/* MMIO Registers */
-
-REG32(IDR0,                0x0)
-    FIELD(IDR0, S2P,         0 , 1)
-    FIELD(IDR0, S1P,         1 , 1)
-    FIELD(IDR0, TTF,         2 , 2)
-    FIELD(IDR0, COHACC,      4 , 1)
-    FIELD(IDR0, BTM,         5 , 1)
-    FIELD(IDR0, HTTU,        6 , 2)
-    FIELD(IDR0, DORMHINT,    8 , 1)
-    FIELD(IDR0, HYP,         9 , 1)
-    FIELD(IDR0, ATS,         10, 1)
-    FIELD(IDR0, NS1ATS,      11, 1)
-    FIELD(IDR0, ASID16,      12, 1)
-    FIELD(IDR0, MSI,         13, 1)
-    FIELD(IDR0, SEV,         14, 1)
-    FIELD(IDR0, ATOS,        15, 1)
-    FIELD(IDR0, PRI,         16, 1)
-    FIELD(IDR0, VMW,         17, 1)
-    FIELD(IDR0, VMID16,      18, 1)
-    FIELD(IDR0, CD2L,        19, 1)
-    FIELD(IDR0, VATOS,       20, 1)
-    FIELD(IDR0, TTENDIAN,    21, 2)
-    FIELD(IDR0, ATSRECERR,   23, 1)
-    FIELD(IDR0, STALL_MODEL, 24, 2)
-    FIELD(IDR0, TERM_MODEL,  26, 1)
-    FIELD(IDR0, STLEVEL,     27, 2)
-    FIELD(IDR0, RME_IMPL,    30, 1)
-
-REG32(IDR1,                0x4)
-    FIELD(IDR1, SIDSIZE,      0 , 6)
-    FIELD(IDR1, SSIDSIZE,     6 , 5)
-    FIELD(IDR1, PRIQS,        11, 5)
-    FIELD(IDR1, EVENTQS,      16, 5)
-    FIELD(IDR1, CMDQS,        21, 5)
-    FIELD(IDR1, ATTR_PERMS_OVR, 26, 1)
-    FIELD(IDR1, ATTR_TYPES_OVR, 27, 1)
-    FIELD(IDR1, REL,          28, 1)
-    FIELD(IDR1, QUEUES_PRESET, 29, 1)
-    FIELD(IDR1, TABLES_PRESET, 30, 1)
-    FIELD(IDR1, ECMDQ,        31, 1)
-
-#define SMMU_IDR1_SIDSIZE 16
-#define SMMU_CMDQS   19
-#define SMMU_EVENTQS 19
-
-REG32(IDR2,                0x8)
-     FIELD(IDR2, BA_VATOS, 0, 10)
-
-REG32(IDR3,                0xc)
-     FIELD(IDR3, HAD,         2, 1);
-     FIELD(IDR3, PBHA,        3, 1);
-     FIELD(IDR3, XNX,         4, 1);
-     FIELD(IDR3, PPS,         5, 1);
-     FIELD(IDR3, MPAM,        7, 1);
-     FIELD(IDR3, FWB,         8, 1);
-     FIELD(IDR3, STT,         9, 1);
-     FIELD(IDR3, RIL,        10, 1);
-     FIELD(IDR3, BBML,       11, 2);
-     FIELD(IDR3, E0PD,       13, 1);
-     FIELD(IDR3, PTWNNC,     14, 1);
-     FIELD(IDR3, DPT,        15, 1);
-
-REG32(IDR4,                0x10)
-
-REG32(IDR5,                0x14)
-     FIELD(IDR5, OAS,         0, 3);
-     FIELD(IDR5, GRAN4K,      4, 1);
-     FIELD(IDR5, GRAN16K,     5, 1);
-     FIELD(IDR5, GRAN64K,     6, 1);
-     FIELD(IDR5, VAX,        10, 2);
-     FIELD(IDR5, STALL_MAX,  16, 16);
-
-#define SMMU_IDR5_OAS 4
-
-REG32(IIDR,                0x18)
-REG32(AIDR,                0x1c)
-REG32(CR0,                 0x20)
-    FIELD(CR0, SMMU_ENABLE,   0, 1)
-    FIELD(CR0, EVENTQEN,      2, 1)
-    FIELD(CR0, CMDQEN,        3, 1)
-
-#define SMMU_CR0_RESERVED 0xFFFFFC20
-
-REG32(CR0ACK,              0x24)
-REG32(CR1,                 0x28)
-REG32(CR2,                 0x2c)
-REG32(STATUSR,             0x40)
-REG32(GBPA,                0x44)
-    FIELD(GBPA, ABORT,        20, 1)
-    FIELD(GBPA, UPDATE,       31, 1)
-
-/* Use incoming. */
-#define SMMU_GBPA_RESET_VAL 0x1000
-
-REG32(IRQ_CTRL,            0x50)
-    FIELD(IRQ_CTRL, GERROR_IRQEN,        0, 1)
-    FIELD(IRQ_CTRL, PRI_IRQEN,           1, 1)
-    FIELD(IRQ_CTRL, EVENTQ_IRQEN,        2, 1)
-
-REG32(IRQ_CTRL_ACK,        0x54)
-REG32(GERROR,              0x60)
-    FIELD(GERROR, CMDQ_ERR,           0, 1)
-    FIELD(GERROR, EVENTQ_ABT_ERR,     2, 1)
-    FIELD(GERROR, PRIQ_ABT_ERR,       3, 1)
-    FIELD(GERROR, MSI_CMDQ_ABT_ERR,   4, 1)
-    FIELD(GERROR, MSI_EVENTQ_ABT_ERR, 5, 1)
-    FIELD(GERROR, MSI_PRIQ_ABT_ERR,   6, 1)
-    FIELD(GERROR, MSI_GERROR_ABT_ERR, 7, 1)
-    FIELD(GERROR, MSI_SFM_ERR,        8, 1)
-
-REG32(GERRORN,             0x64)
-
-#define A_GERROR_IRQ_CFG0  0x68 /* 64b */
-REG32(GERROR_IRQ_CFG1, 0x70)
-REG32(GERROR_IRQ_CFG2, 0x74)
-
-#define A_STRTAB_BASE      0x80 /* 64b */
-
-#define SMMU_BASE_ADDR_MASK 0xfffffffffffc0
-
-REG32(STRTAB_BASE_CFG,     0x88)
-    FIELD(STRTAB_BASE_CFG, FMT,      16, 2)
-    FIELD(STRTAB_BASE_CFG, SPLIT,    6 , 5)
-    FIELD(STRTAB_BASE_CFG, LOG2SIZE, 0 , 6)
-
-#define A_CMDQ_BASE        0x90 /* 64b */
-REG32(CMDQ_PROD,           0x98)
-REG32(CMDQ_CONS,           0x9c)
-    FIELD(CMDQ_CONS, ERR, 24, 7)
-
-#define A_EVENTQ_BASE      0xa0 /* 64b */
-REG32(EVENTQ_PROD,         0xa8)
-REG32(EVENTQ_CONS,         0xac)
-
-#define A_EVENTQ_IRQ_CFG0  0xb0 /* 64b */
-REG32(EVENTQ_IRQ_CFG1,     0xb8)
-REG32(EVENTQ_IRQ_CFG2,     0xbc)
-
-#define A_IDREGS           0xfd0
-
 static inline int smmu_enabled(SMMUv3State *s)
 {
     return FIELD_EX32(s->cr[0], CR0, SMMU_ENABLE);
@@ -272,37 +132,6 @@ static inline void smmu_write_cmdq_err(SMMUv3State *s, uint32_t err_type)
     s->cmdq.cons = FIELD_DP32(s->cmdq.cons, CMDQ_CONS, ERR, err_type);
 }
 
-/* Commands */
-
-typedef enum SMMUCommandType {
-    SMMU_CMD_NONE            = 0x00,
-    SMMU_CMD_PREFETCH_CONFIG       ,
-    SMMU_CMD_PREFETCH_ADDR,
-    SMMU_CMD_CFGI_STE,
-    SMMU_CMD_CFGI_STE_RANGE,
-    SMMU_CMD_CFGI_CD,
-    SMMU_CMD_CFGI_CD_ALL,
-    SMMU_CMD_CFGI_ALL,
-    SMMU_CMD_TLBI_NH_ALL     = 0x10,
-    SMMU_CMD_TLBI_NH_ASID,
-    SMMU_CMD_TLBI_NH_VA,
-    SMMU_CMD_TLBI_NH_VAA,
-    SMMU_CMD_TLBI_EL3_ALL    = 0x18,
-    SMMU_CMD_TLBI_EL3_VA     = 0x1a,
-    SMMU_CMD_TLBI_EL2_ALL    = 0x20,
-    SMMU_CMD_TLBI_EL2_ASID,
-    SMMU_CMD_TLBI_EL2_VA,
-    SMMU_CMD_TLBI_EL2_VAA,
-    SMMU_CMD_TLBI_S12_VMALL  = 0x28,
-    SMMU_CMD_TLBI_S2_IPA     = 0x2a,
-    SMMU_CMD_TLBI_NSNH_ALL   = 0x30,
-    SMMU_CMD_ATC_INV         = 0x40,
-    SMMU_CMD_PRI_RESP,
-    SMMU_CMD_RESUME          = 0x44,
-    SMMU_CMD_STALL_TERM,
-    SMMU_CMD_SYNC,
-} SMMUCommandType;
-
 static const char *cmd_stringify[] = {
     [SMMU_CMD_PREFETCH_CONFIG] = "SMMU_CMD_PREFETCH_CONFIG",
     [SMMU_CMD_PREFETCH_ADDR]   = "SMMU_CMD_PREFETCH_ADDR",
@@ -525,64 +354,6 @@ typedef struct SMMUEventInfo {
 
 void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event);
 
-/* Configuration Data */
-
-/* STE Level 1 Descriptor */
-typedef struct STEDesc {
-    uint32_t word[2];
-} STEDesc;
-
-/* CD Level 1 Descriptor */
-typedef struct CDDesc {
-    uint32_t word[2];
-} CDDesc;
-
-/* Stream Table Entry(STE) */
-typedef struct STE {
-    uint32_t word[16];
-} STE;
-
-/* Context Descriptor(CD) */
-typedef struct CD {
-    uint32_t word[16];
-} CD;
-
-/* STE fields */
-
-#define STE_VALID(x)   extract32((x)->word[0], 0, 1)
-
-#define STE_CONFIG(x)  extract32((x)->word[0], 1, 3)
-#define STE_CFG_S1_ENABLED(config) (config & 0x1)
-#define STE_CFG_S2_ENABLED(config) (config & 0x2)
-#define STE_CFG_ABORT(config)      (!(config & 0x4))
-#define STE_CFG_BYPASS(config)     (config == 0x4)
-
-#define STE_S1FMT(x)       extract32((x)->word[0], 4 , 2)
-#define STE_S1CDMAX(x)     extract32((x)->word[1], 27, 5)
-#define STE_S1STALLD(x)    extract32((x)->word[2], 27, 1)
-#define STE_EATS(x)        extract32((x)->word[2], 28, 2)
-#define STE_STRW(x)        extract32((x)->word[2], 30, 2)
-#define STE_S2VMID(x)      extract32((x)->word[4], 0 , 16)
-#define STE_S2T0SZ(x)      extract32((x)->word[5], 0 , 6)
-#define STE_S2SL0(x)       extract32((x)->word[5], 6 , 2)
-#define STE_S2TG(x)        extract32((x)->word[5], 14, 2)
-#define STE_S2PS(x)        extract32((x)->word[5], 16, 3)
-#define STE_S2AA64(x)      extract32((x)->word[5], 19, 1)
-#define STE_S2ENDI(x)      extract32((x)->word[5], 20, 1)
-#define STE_S2AFFD(x)      extract32((x)->word[5], 21, 1)
-#define STE_S2HD(x)        extract32((x)->word[5], 23, 1)
-#define STE_S2HA(x)        extract32((x)->word[5], 24, 1)
-#define STE_S2S(x)         extract32((x)->word[5], 25, 1)
-#define STE_S2R(x)         extract32((x)->word[5], 26, 1)
-
-#define STE_CTXPTR(x)                                   \
-    ((extract64((x)->word[1], 0, 16) << 32) |           \
-     ((x)->word[0] & 0xffffffc0))
-
-#define STE_S2TTB(x)                                    \
-    ((extract64((x)->word[7], 0, 16) << 32) |           \
-     ((x)->word[6] & 0xfffffff0))
-
 static inline int oas2bits(int oas_field)
 {
     switch (oas_field) {
@@ -603,30 +374,6 @@ static inline int oas2bits(int oas_field)
     g_assert_not_reached();
 }
 
-/* CD fields */
-
-#define CD_VALID(x)   extract32((x)->word[0], 31, 1)
-#define CD_ASID(x)    extract32((x)->word[1], 16, 16)
-#define CD_TTB(x, sel)                                          \
-    ((extract64((x)->word[(sel) * 2 + 3], 0, 19) << 32) |       \
-     ((x)->word[(sel) * 2 + 2] & ~0xfULL))
-
-#define CD_HAD(x, sel)   extract32((x)->word[(sel) * 2 + 2], 1, 1)
-
-#define CD_TSZ(x, sel)   extract32((x)->word[0], (16 * (sel)) + 0, 6)
-#define CD_TG(x, sel)    extract32((x)->word[0], (16 * (sel)) + 6, 2)
-#define CD_EPD(x, sel)   extract32((x)->word[0], (16 * (sel)) + 14, 1)
-#define CD_ENDI(x)       extract32((x)->word[0], 15, 1)
-#define CD_IPS(x)        extract32((x)->word[1], 0 , 3)
-#define CD_AFFD(x)       extract32((x)->word[1], 3 , 1)
-#define CD_TBI(x)        extract32((x)->word[1], 6 , 2)
-#define CD_HD(x)         extract32((x)->word[1], 10 , 1)
-#define CD_HA(x)         extract32((x)->word[1], 11 , 1)
-#define CD_S(x)          extract32((x)->word[1], 12, 1)
-#define CD_R(x)          extract32((x)->word[1], 13, 1)
-#define CD_A(x)          extract32((x)->word[1], 14, 1)
-#define CD_AARCH64(x)    extract32((x)->word[1], 9 , 1)
-
 /**
  * tg2granule - Decodes the CD translation granule size field according
  * to the ttbr in use
diff --git a/include/hw/arm/smmuv3-common.h b/include/hw/arm/smmuv3-common.h
new file mode 100644
index 0000000000..814ebbdec9
--- /dev/null
+++ b/include/hw/arm/smmuv3-common.h
@@ -0,0 +1,461 @@
+/*
+ * ARM SMMUv3 support
+ *
+ * Copyright (C) 2014-2016 Broadcom Corporation
+ * Copyright (c) 2017 Red Hat, Inc.
+ * Written by Prem Mallappa, Eric Auger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_ARM_SMMUV3_COMMON_H
+#define HW_ARM_SMMUV3_COMMON_H
+
+#include "hw/registerfields.h"
+
+/* Configuration Data */
+
+/* STE Level 1 Descriptor */
+typedef struct STEDesc {
+    uint32_t word[2];
+} STEDesc;
+
+/* CD Level 1 Descriptor */
+typedef struct CDDesc {
+    uint32_t word[2];
+} CDDesc;
+
+/* Stream Table Entry(STE) */
+typedef struct STE {
+    uint32_t word[16];
+} STE;
+
+/* Context Descriptor(CD) */
+typedef struct CD {
+    uint32_t word[16];
+} CD;
+
+/* STE fileds */
+REG32(STE_0, 0)
+    FIELD(STE_0, VALID, 0, 1)
+    FIELD(STE_0, CONFIG, 1, 3)
+    FIELD(STE_0, S1FMT, 4, 2)
+    FIELD(STE_0, CTXPTR, 6, 26)
+REG32(STE_1, 4)
+    FIELD(STE_1, CTXPTR_HI, 0, 16)
+    FIELD(STE_1, S1CDMAX, 27, 5)
+REG32(STE_2, 8)
+    FIELD(STE_2, NSCFG, 14, 2)
+    FIELD(STE_2, S1STALLD, 27, 1)
+    FIELD(STE_2, EATS, 28, 2)
+    FIELD(STE_2, STRW, 30, 2)
+REG32(STE_4, 16)
+    FIELD(STE_4, S2VMID, 0, 16)
+REG32(STE_5, 20)
+    FIELD(STE_5, S2T0SZ, 0, 6)
+    FIELD(STE_5, S2SL0, 6, 2)
+    FIELD(STE_5, S2TG, 14, 2)
+    FIELD(STE_5, S2PS, 16, 3)
+    FIELD(STE_5, S2AA64, 19, 1)
+    FIELD(STE_5, S2ENDI, 20, 1)
+    FIELD(STE_5, S2AFFD, 21, 1)
+    FIELD(STE_5, S2HD, 23, 1)
+    FIELD(STE_5, S2HA, 24, 1)
+    FIELD(STE_5, S2S, 25, 1)
+    FIELD(STE_5, S2R, 26, 1)
+REG32(STE_6, 24)
+    FIELD(STE_6, S2TTB_LO, 4, 28)
+REG32(STE_7, 28)
+    FIELD(STE_7, S2TTB_HI, 0, 16)
+
+/* CD fields */
+REG32(CD_0, 0)
+    FIELD(CD_0, TSZ0, 0, 6)
+    FIELD(CD_0, TG0, 6, 2)
+    FIELD(CD_0, EPD0, 14, 1)
+    FIELD(CD_0, ENDI, 15, 1)
+    FIELD(CD_0, TSZ1, 16, 6)
+    FIELD(CD_0, TG1, 22, 2)
+    FIELD(CD_0, EPD1, 30, 1)
+    FIELD(CD_0, VALID, 31, 1)
+REG32(CD_1, 4)
+    FIELD(CD_1, IPS, 0, 3)
+    FIELD(CD_1, AFFD, 3, 1)
+    FIELD(CD_1, TBI, 6, 2)
+    FIELD(CD_1, AARCH64, 9, 1)
+    FIELD(CD_1, HD, 10, 1)
+    FIELD(CD_1, HA, 11, 1)
+    FIELD(CD_1, S, 12, 1)
+    FIELD(CD_1, R, 13, 1)
+    FIELD(CD_1, A, 14, 1)
+    FIELD(CD_1, ASID, 16, 16)
+REG32(CD_2, 8)
+    FIELD(CD_2, NSCFG0, 0, 1)
+    FIELD(CD_2, HAD0, 1, 1)
+    FIELD(CD_2, TTB0_LO, 4, 28)
+REG32(CD_3, 12)
+    FIELD(CD_3, TTB0_HI, 0, 19)
+REG32(CD_4, 16)
+    FIELD(CD_4, NSCFG1, 0, 1)
+    FIELD(CD_4, HAD1, 1, 1)
+    FIELD(CD_4, TTB1_LO, 4, 28)
+REG32(CD_5, 20)
+    FIELD(CD_5, TTB1_HI, 0, 19)
+REG32(CD_6, 24)
+REG32(CD_7, 28)
+
+/* Get STE fields */
+#define STE_VALID(x)      FIELD_EX32((x)->word[0], STE_0, VALID)
+#define STE_CONFIG(x)     FIELD_EX32((x)->word[0], STE_0, CONFIG)
+#define STE_S1FMT(x)      FIELD_EX32((x)->word[0], STE_0, S1FMT)
+#define STE_S1CDMAX(x)    FIELD_EX32((x)->word[1], STE_1, S1CDMAX)
+#define STE_S1STALLD(x)   FIELD_EX32((x)->word[2], STE_2, S1STALLD)
+#define STE_EATS(x)       FIELD_EX32((x)->word[2], STE_2, EATS)
+#define STE_STRW(x)       FIELD_EX32((x)->word[2], STE_2, STRW)
+#define STE_NSCFG(x)      FIELD_EX32((x)->word[2], STE_2, NSCFG)
+#define STE_S2VMID(x)     FIELD_EX32((x)->word[4], STE_4, S2VMID)
+#define STE_S2T0SZ(x)     FIELD_EX32((x)->word[5], STE_5, S2T0SZ)
+#define STE_S2SL0(x)      FIELD_EX32((x)->word[5], STE_5, S2SL0)
+#define STE_S2TG(x)       FIELD_EX32((x)->word[5], STE_5, S2TG)
+#define STE_S2PS(x)       FIELD_EX32((x)->word[5], STE_5, S2PS)
+#define STE_S2AA64(x)     FIELD_EX32((x)->word[5], STE_5, S2AA64)
+#define STE_S2ENDI(x)     FIELD_EX32((x)->word[5], STE_5, S2ENDI)
+#define STE_S2AFFD(x)     FIELD_EX32((x)->word[5], STE_5, S2AFFD)
+#define STE_S2HD(x)       FIELD_EX32((x)->word[5], STE_5, S2HD)
+#define STE_S2HA(x)       FIELD_EX32((x)->word[5], STE_5, S2HA)
+#define STE_S2S(x)        FIELD_EX32((x)->word[5], STE_5, S2S)
+#define STE_S2R(x)        FIELD_EX32((x)->word[5], STE_5, S2R)
+#define STE_CTXPTR(x)                                            \
+    (((uint64_t)FIELD_EX32((x)->word[1], STE_1, CTXPTR_HI) << 32) | \
+     ((uint64_t)FIELD_EX32((x)->word[0], STE_0, CTXPTR) << 6))
+#define STE_S2TTB(x)                                              \
+    (((uint64_t)FIELD_EX32((x)->word[7], STE_7, S2TTB_HI) << 32) | \
+     ((uint64_t)FIELD_EX32((x)->word[6], STE_6, S2TTB_LO) << 4))
+#define STE_CFG_S1_ENABLED(config) ((config) & 0x1)
+#define STE_CFG_S2_ENABLED(config) ((config) & 0x2)
+#define STE_CFG_ABORT(config)      (!((config) & 0x4))
+#define STE_CFG_BYPASS(config)     ((config) == 0x4)
+
+/* Update STE fields */
+#define STE_SET_VALID(ste, v) \
+    ((ste)->word[0] = FIELD_DP32((ste)->word[0], STE_0, VALID, (v)))
+#define STE_SET_CONFIG(ste, v) \
+    ((ste)->word[0] = FIELD_DP32((ste)->word[0], STE_0, CONFIG, (v)))
+#define STE_SET_S1FMT(ste, v) \
+    ((ste)->word[0] = FIELD_DP32((ste)->word[0], STE_0, S1FMT, (v)))
+#define STE_SET_CTXPTR(ste, v) do { \
+    (ste)->word[0] = FIELD_DP32((ste)->word[0], STE_0, CTXPTR, (v) >> 6); \
+    (ste)->word[1] = FIELD_DP32((ste)->word[1], STE_1, CTXPTR_HI, (v) >> 32); \
+} while (0)
+#define STE_SET_S1CDMAX(ste, v) \
+    ((ste)->word[1] = FIELD_DP32((ste)->word[1], STE_1, S1CDMAX, (v)))
+#define STE_SET_S1STALLD(ste, v) \
+    ((ste)->word[2] = FIELD_DP32((ste)->word[2], STE_2, S1STALLD, (v)))
+#define STE_SET_EATS(ste, v) \
+    ((ste)->word[2] = FIELD_DP32((ste)->word[2], STE_2, EATS, (v)))
+#define STE_SET_STRW(ste, v) \
+    ((ste)->word[2] = FIELD_DP32((ste)->word[2], STE_2, STRW, (v)))
+#define STE_SET_NSCFG(ste, v) \
+    ((ste)->word[2] = FIELD_DP32((ste)->word[2], STE_2, NSCFG, (v)))
+#define STE_SET_S2VMID(ste, v) \
+    ((ste)->word[4] = FIELD_DP32((ste)->word[4], STE_4, S2VMID, (v)))
+#define STE_SET_S2T0SZ(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2T0SZ, (v)))
+#define STE_SET_S2SL0(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2SL0, (v)))
+#define STE_SET_S2TG(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2TG, (v)))
+#define STE_SET_S2PS(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2PS, (v)))
+#define STE_SET_S2AA64(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2AA64, (v)))
+#define STE_SET_S2ENDI(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2ENDI, (v)))
+#define STE_SET_S2AFFD(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2AFFD, (v)))
+#define STE_SET_S2HD(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2HD, (v)))
+#define STE_SET_S2HA(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2HA, (v)))
+#define STE_SET_S2S(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2S, (v)))
+#define STE_SET_S2R(ste, v) \
+    ((ste)->word[5] = FIELD_DP32((ste)->word[5], STE_5, S2R, (v)))
+#define STE_SET_S2TTB(ste, v) do { \
+    (ste)->word[6] = FIELD_DP32((ste)->word[6], STE_6, S2TTB_LO, (v) >> 4); \
+    (ste)->word[7] = FIELD_DP32((ste)->word[7], STE_7, S2TTB_HI, (v) >> 32); \
+} while (0)
+
+/* Get CD fields */
+#define CD_VALID(x)      FIELD_EX32((x)->word[0], CD_0, VALID)
+#define CD_ASID(x)       FIELD_EX32((x)->word[1], CD_1, ASID)
+#define CD_TTB(x, sel)                                           \
+    ((sel) ? (((uint64_t)FIELD_EX32((x)->word[5], CD_5, TTB1_HI) << 32) | \
+              ((uint64_t)FIELD_EX32((x)->word[4], CD_4, TTB1_LO) << 4)) :    \
+             (((uint64_t)FIELD_EX32((x)->word[3], CD_3, TTB0_HI) << 32) | \
+              ((uint64_t)FIELD_EX32((x)->word[2], CD_2, TTB0_LO) << 4)))
+#define CD_HAD(x, sel)   ((sel) ? \
+    FIELD_EX32((x)->word[4], CD_4, HAD1) : \
+    FIELD_EX32((x)->word[2], CD_2, HAD0))
+#define CD_TSZ(x, sel)   ((sel) ? \
+    FIELD_EX32((x)->word[0], CD_0, TSZ1) : \
+    FIELD_EX32((x)->word[0], CD_0, TSZ0))
+#define CD_TG(x, sel)    ((sel) ? \
+    FIELD_EX32((x)->word[0], CD_0, TG1) : \
+    FIELD_EX32((x)->word[0], CD_0, TG0))
+#define CD_EPD(x, sel)   ((sel) ? \
+    FIELD_EX32((x)->word[0], CD_0, EPD1) : \
+    FIELD_EX32((x)->word[0], CD_0, EPD0))
+#define CD_ENDI(x)       FIELD_EX32((x)->word[0], CD_0, ENDI)
+#define CD_IPS(x)        FIELD_EX32((x)->word[1], CD_1, IPS)
+#define CD_AFFD(x)       FIELD_EX32((x)->word[1], CD_1, AFFD)
+#define CD_TBI(x)        FIELD_EX32((x)->word[1], CD_1, TBI)
+#define CD_HD(x)         FIELD_EX32((x)->word[1], CD_1, HD)
+#define CD_HA(x)         FIELD_EX32((x)->word[1], CD_1, HA)
+#define CD_S(x)          FIELD_EX32((x)->word[1], CD_1, S)
+#define CD_R(x)          FIELD_EX32((x)->word[1], CD_1, R)
+#define CD_A(x)          FIELD_EX32((x)->word[1], CD_1, A)
+#define CD_AARCH64(x)    FIELD_EX32((x)->word[1], CD_1, AARCH64)
+#define CD_NSCFG(x, sel) ((sel) ? \
+    FIELD_EX32((x)->word[4], CD_4, NSCFG1) : \
+    FIELD_EX32((x)->word[2], CD_2, NSCFG0))
+
+/* Update CD fields */
+#define CD_SET_VALID(cd, v) \
+    ((cd)->word[0] = FIELD_DP32((cd)->word[0], CD_0, VALID, (v)))
+#define CD_SET_ASID(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, ASID, (v)))
+#define CD_SET_TTB(cd, sel, v) do { \
+    if (sel) { \
+        (cd)->word[4] = FIELD_DP32((cd)->word[4], CD_4, TTB1_LO, (v) >> 4); \
+        (cd)->word[5] = FIELD_DP32((cd)->word[5], CD_5, TTB1_HI, (v) >> 32); \
+    } else { \
+        (cd)->word[2] = FIELD_DP32((cd)->word[2], CD_2, TTB0_LO, (v) >> 4); \
+        (cd)->word[3] = FIELD_DP32((cd)->word[3], CD_3, TTB0_HI, (v) >> 32); \
+    } \
+} while (0)
+#define CD_SET_HAD(cd, sel, v) do { \
+    if (sel) { \
+        (cd)->word[4] = FIELD_DP32((cd)->word[4], CD_4, HAD1, (v)); \
+    } else { \
+        (cd)->word[2] = FIELD_DP32((cd)->word[2], CD_2, HAD0, (v)); \
+    } \
+} while (0)
+#define CD_SET_TSZ(cd, sel, v) \
+    ((cd)->word[0] = (sel) ? FIELD_DP32((cd)->word[0], CD_0, TSZ1, (v)) : \
+                             FIELD_DP32((cd)->word[0], CD_0, TSZ0, (v)))
+#define CD_SET_TG(cd, sel, v) \
+    ((cd)->word[0] = (sel) ? FIELD_DP32((cd)->word[0], CD_0, TG1, (v)) : \
+                             FIELD_DP32((cd)->word[0], CD_0, TG0, (v)))
+#define CD_SET_EPD(cd, sel, v) \
+    ((cd)->word[0] = (sel) ? FIELD_DP32((cd)->word[0], CD_0, EPD1, (v)) : \
+                             FIELD_DP32((cd)->word[0], CD_0, EPD0, (v)))
+#define CD_SET_ENDI(cd, v) \
+    ((cd)->word[0] = FIELD_DP32((cd)->word[0], CD_0, ENDI, (v)))
+#define CD_SET_IPS(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, IPS, (v)))
+#define CD_SET_AFFD(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, AFFD, (v)))
+#define CD_SET_TBI(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, TBI, (v)))
+#define CD_SET_HD(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, HD, (v)))
+#define CD_SET_HA(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, HA, (v)))
+#define CD_SET_S(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, S, (v)))
+#define CD_SET_R(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, R, (v)))
+#define CD_SET_A(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, A, (v)))
+#define CD_SET_AARCH64(cd, v) \
+    ((cd)->word[1] = FIELD_DP32((cd)->word[1], CD_1, AARCH64, (v)))
+#define CD_SET_NSCFG(cd, sel, v) \
+    ((sel) ? ((cd)->word[4] = FIELD_DP32((cd)->word[4], CD_4, NSCFG1, (v))) : \
+             ((cd)->word[2] = FIELD_DP32((cd)->word[2], CD_2, NSCFG0, (v))))
+#define CD_SET_MAIR0(cd, v) ((cd)->word[6] = (v))
+#define CD_SET_MAIR1(cd, v) ((cd)->word[7] = (v))
+
+/* MMIO Registers */
+
+REG32(IDR0,                0x0)
+    FIELD(IDR0, S2P,         0 , 1)
+    FIELD(IDR0, S1P,         1 , 1)
+    FIELD(IDR0, TTF,         2 , 2)
+    FIELD(IDR0, COHACC,      4 , 1)
+    FIELD(IDR0, BTM,         5 , 1)
+    FIELD(IDR0, HTTU,        6 , 2)
+    FIELD(IDR0, DORMHINT,    8 , 1)
+    FIELD(IDR0, HYP,         9 , 1)
+    FIELD(IDR0, ATS,         10, 1)
+    FIELD(IDR0, NS1ATS,      11, 1)
+    FIELD(IDR0, ASID16,      12, 1)
+    FIELD(IDR0, MSI,         13, 1)
+    FIELD(IDR0, SEV,         14, 1)
+    FIELD(IDR0, ATOS,        15, 1)
+    FIELD(IDR0, PRI,         16, 1)
+    FIELD(IDR0, VMW,         17, 1)
+    FIELD(IDR0, VMID16,      18, 1)
+    FIELD(IDR0, CD2L,        19, 1)
+    FIELD(IDR0, VATOS,       20, 1)
+    FIELD(IDR0, TTENDIAN,    21, 2)
+    FIELD(IDR0, ATSRECERR,   23, 1)
+    FIELD(IDR0, STALL_MODEL, 24, 2)
+    FIELD(IDR0, TERM_MODEL,  26, 1)
+    FIELD(IDR0, STLEVEL,     27, 2)
+    FIELD(IDR0, RME_IMPL,    30, 1)
+
+REG32(IDR1,                0x4)
+    FIELD(IDR1, SIDSIZE,      0 , 6)
+    FIELD(IDR1, SSIDSIZE,     6 , 5)
+    FIELD(IDR1, PRIQS,        11, 5)
+    FIELD(IDR1, EVENTQS,      16, 5)
+    FIELD(IDR1, CMDQS,        21, 5)
+    FIELD(IDR1, ATTR_PERMS_OVR, 26, 1)
+    FIELD(IDR1, ATTR_TYPES_OVR, 27, 1)
+    FIELD(IDR1, REL,          28, 1)
+    FIELD(IDR1, QUEUES_PRESET, 29, 1)
+    FIELD(IDR1, TABLES_PRESET, 30, 1)
+    FIELD(IDR1, ECMDQ,        31, 1)
+
+#define SMMU_IDR1_SIDSIZE 16
+#define SMMU_CMDQS   19
+#define SMMU_EVENTQS 19
+
+REG32(IDR2,                0x8)
+     FIELD(IDR2, BA_VATOS, 0, 10)
+
+REG32(IDR3,                0xc)
+     FIELD(IDR3, HAD,         2, 1);
+     FIELD(IDR3, PBHA,        3, 1);
+     FIELD(IDR3, XNX,         4, 1);
+     FIELD(IDR3, PPS,         5, 1);
+     FIELD(IDR3, MPAM,        7, 1);
+     FIELD(IDR3, FWB,         8, 1);
+     FIELD(IDR3, STT,         9, 1);
+     FIELD(IDR3, RIL,        10, 1);
+     FIELD(IDR3, BBML,       11, 2);
+     FIELD(IDR3, E0PD,       13, 1);
+     FIELD(IDR3, PTWNNC,     14, 1);
+     FIELD(IDR3, DPT,        15, 1);
+
+REG32(IDR4,                0x10)
+
+REG32(IDR5,                0x14)
+     FIELD(IDR5, OAS,         0, 3);
+     FIELD(IDR5, GRAN4K,      4, 1);
+     FIELD(IDR5, GRAN16K,     5, 1);
+     FIELD(IDR5, GRAN64K,     6, 1);
+     FIELD(IDR5, VAX,        10, 2);
+     FIELD(IDR5, STALL_MAX,  16, 16);
+
+#define SMMU_IDR5_OAS 4
+
+REG32(IIDR,                0x18)
+REG32(AIDR,                0x1c)
+REG32(CR0,                 0x20)
+    FIELD(CR0, SMMU_ENABLE,   0, 1)
+    FIELD(CR0, EVENTQEN,      2, 1)
+    FIELD(CR0, CMDQEN,        3, 1)
+
+#define SMMU_CR0_RESERVED 0xFFFFFC20
+
+REG32(CR0ACK,              0x24)
+REG32(CR1,                 0x28)
+REG32(CR2,                 0x2c)
+REG32(STATUSR,             0x40)
+REG32(GBPA,                0x44)
+    FIELD(GBPA, ABORT,        20, 1)
+    FIELD(GBPA, UPDATE,       31, 1)
+
+/* Use incoming. */
+#define SMMU_GBPA_RESET_VAL 0x1000
+
+REG32(IRQ_CTRL,            0x50)
+    FIELD(IRQ_CTRL, GERROR_IRQEN,        0, 1)
+    FIELD(IRQ_CTRL, PRI_IRQEN,           1, 1)
+    FIELD(IRQ_CTRL, EVENTQ_IRQEN,        2, 1)
+
+REG32(IRQ_CTRL_ACK,        0x54)
+REG32(GERROR,              0x60)
+    FIELD(GERROR, CMDQ_ERR,           0, 1)
+    FIELD(GERROR, EVENTQ_ABT_ERR,     2, 1)
+    FIELD(GERROR, PRIQ_ABT_ERR,       3, 1)
+    FIELD(GERROR, MSI_CMDQ_ABT_ERR,   4, 1)
+    FIELD(GERROR, MSI_EVENTQ_ABT_ERR, 5, 1)
+    FIELD(GERROR, MSI_PRIQ_ABT_ERR,   6, 1)
+    FIELD(GERROR, MSI_GERROR_ABT_ERR, 7, 1)
+    FIELD(GERROR, MSI_SFM_ERR,        8, 1)
+
+REG32(GERRORN,             0x64)
+
+#define A_GERROR_IRQ_CFG0  0x68 /* 64b */
+REG32(GERROR_IRQ_CFG1, 0x70)
+REG32(GERROR_IRQ_CFG2, 0x74)
+
+#define A_STRTAB_BASE      0x80 /* 64b */
+
+#define SMMU_BASE_ADDR_MASK 0xfffffffffffc0
+
+REG32(STRTAB_BASE_CFG,     0x88)
+    FIELD(STRTAB_BASE_CFG, FMT,      16, 2)
+    FIELD(STRTAB_BASE_CFG, SPLIT,    6 , 5)
+    FIELD(STRTAB_BASE_CFG, LOG2SIZE, 0 , 6)
+
+#define A_CMDQ_BASE        0x90 /* 64b */
+REG32(CMDQ_PROD,           0x98)
+REG32(CMDQ_CONS,           0x9c)
+    FIELD(CMDQ_CONS, ERR, 24, 7)
+
+#define A_EVENTQ_BASE      0xa0 /* 64b */
+REG32(EVENTQ_PROD,         0xa8)
+REG32(EVENTQ_CONS,         0xac)
+
+#define A_EVENTQ_IRQ_CFG0  0xb0 /* 64b */
+REG32(EVENTQ_IRQ_CFG1,     0xb8)
+REG32(EVENTQ_IRQ_CFG2,     0xbc)
+
+#define A_IDREGS           0xfd0
+
+/* Commands */
+
+typedef enum SMMUCommandType {
+    SMMU_CMD_NONE            = 0x00,
+    SMMU_CMD_PREFETCH_CONFIG       ,
+    SMMU_CMD_PREFETCH_ADDR,
+    SMMU_CMD_CFGI_STE,
+    SMMU_CMD_CFGI_STE_RANGE,
+    SMMU_CMD_CFGI_CD,
+    SMMU_CMD_CFGI_CD_ALL,
+    SMMU_CMD_CFGI_ALL,
+    SMMU_CMD_TLBI_NH_ALL     = 0x10,
+    SMMU_CMD_TLBI_NH_ASID,
+    SMMU_CMD_TLBI_NH_VA,
+    SMMU_CMD_TLBI_NH_VAA,
+    SMMU_CMD_TLBI_EL3_ALL    = 0x18,
+    SMMU_CMD_TLBI_EL3_VA     = 0x1a,
+    SMMU_CMD_TLBI_EL2_ALL    = 0x20,
+    SMMU_CMD_TLBI_EL2_ASID,
+    SMMU_CMD_TLBI_EL2_VA,
+    SMMU_CMD_TLBI_EL2_VAA,
+    SMMU_CMD_TLBI_S12_VMALL  = 0x28,
+    SMMU_CMD_TLBI_S2_IPA     = 0x2a,
+    SMMU_CMD_TLBI_NSNH_ALL   = 0x30,
+    SMMU_CMD_ATC_INV         = 0x40,
+    SMMU_CMD_PRI_RESP,
+    SMMU_CMD_RESUME          = 0x44,
+    SMMU_CMD_STALL_TERM,
+    SMMU_CMD_SYNC,
+} SMMUCommandType;
+
+#endif /* HW_ARM_SMMUV3_COMMON_H */
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC RESEND v5 2/4] hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing
  2025-11-26 15:45 [RFC RESEND v5 0/4] hw/misc: Introduce a generalized IOMMU test framework Tao Tang
  2025-11-26 15:45 ` [RFC RESEND v5 1/4] hw/arm/smmuv3: Extract common definitions to smmuv3-common.h Tao Tang
@ 2025-11-26 15:45 ` Tao Tang
  2025-12-04 18:36   ` Pierrick Bouvier
  2025-12-10 18:35   ` Eric Auger
  2025-11-26 15:45 ` [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library Tao Tang
  2025-11-26 15:45 ` [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev Tao Tang
  3 siblings, 2 replies; 23+ messages in thread
From: Tao Tang @ 2025-11-26 15:45 UTC (permalink / raw)
  To: Paolo Bonzini, Fabiano Rosas, Laurent Vivier, Eric Auger,
	Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Jean-Philippe Brucker, Mostafa Saleh,
	CLEMENT MATHIEU--DRIF, Tao Tang

Add a minimal PCI test device designed to exercise IOMMU translation
(such as ARM SMMUv3) without requiring guest firmware or OS. The device
provides MMIO registers to configure and trigger DMA operations with
controllable attributes (security state, address space), enabling
deterministic IOMMU testing.

Key features:
- Bare-metal IOMMU testing via simple MMIO interface
- Configurable DMA attributes for security states and address spaces
- Write-then-read verification pattern with automatic result checking

The device performs a deterministic DMA test pattern: write a known
value (0x88888888) to a configured IOVA, read it back, and verify data
integrity. Results are reported through a dedicated result register,
eliminating the need for complex interrupt handling or driver
infrastructure in tests.

This is purely a test device and not intended for production use or
machine realism. It complements existing test infrastructure like
pci-testdev but focuses specifically on IOMMU translation path
validation.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 docs/specs/index.rst            |   1 +
 docs/specs/iommu-testdev.rst    | 109 +++++++++++++
 hw/misc/Kconfig                 |   5 +
 hw/misc/iommu-testdev.c         | 278 ++++++++++++++++++++++++++++++++
 hw/misc/meson.build             |   1 +
 hw/misc/trace-events            |  10 ++
 include/hw/misc/iommu-testdev.h |  70 ++++++++
 7 files changed, 474 insertions(+)
 create mode 100644 docs/specs/iommu-testdev.rst
 create mode 100644 hw/misc/iommu-testdev.c
 create mode 100644 include/hw/misc/iommu-testdev.h

diff --git a/docs/specs/index.rst b/docs/specs/index.rst
index f19d73c9f6..1fc7fae6bb 100644
--- a/docs/specs/index.rst
+++ b/docs/specs/index.rst
@@ -39,3 +39,4 @@ guest hardware that is specific to QEMU.
    riscv-iommu
    riscv-aia
    aspeed-intc
+   iommu-testdev
\ No newline at end of file
diff --git a/docs/specs/iommu-testdev.rst b/docs/specs/iommu-testdev.rst
new file mode 100644
index 0000000000..fdc7f2ee89
--- /dev/null
+++ b/docs/specs/iommu-testdev.rst
@@ -0,0 +1,109 @@
+iommu-testdev — IOMMU test device for bare-metal testing
+=========================================================
+
+Overview
+--------
+``iommu-testdev`` is a minimal, test-only PCI device designed to exercise
+IOMMU translation (such as ARM SMMUv3) without requiring firmware or a guest
+OS. Tests can populate IOMMU translation tables with known values and trigger
+DMA operations that flow through the IOMMU translation path. It is **not** a
+faithful PCIe endpoint and must be considered a QEMU-internal test vehicle.
+
+Key Features
+------------
+* **Bare-metal IOMMU testing**: No guest kernel or firmware required
+* **Configurable DMA attributes**: Supports address space  configuration via
+  MMIO registers
+* **Deterministic verification**: Write-then-read DMA pattern with automatic
+  result checking
+
+Status
+------
+* Location: ``hw/misc/iommu-testdev.c``
+* Header: ``include/hw/misc/iommu-testdev.h``
+* Build guard: ``CONFIG_IOMMU_TESTDEV``
+
+Device Interface
+----------------
+The device exposes a single PCI BAR0 with MMIO registers:
+
+* ``ITD_REG_DMA_TRIGGERING`` (0x00): Reading triggers DMA execution
+* ``ITD_REG_DMA_GVA_LO`` (0x04): IOVA/GVA bits [31:0]
+* ``ITD_REG_DMA_GVA_HI`` (0x08): IOVA/GVA bits [63:32]
+* ``ITD_REG_DMA_LEN`` (0x0C): DMA transfer length
+* ``ITD_REG_DMA_RESULT`` (0x10): DMA operation result (0=success)
+* ``ITD_REG_DMA_DBELL`` (0x14): Write 1 to arm DMA
+* ``ITD_REG_DMA_ATTRS`` (0x18): DMA attributes
+
+  - bit[0]: secure (1=Secure, 0=Non-Secure)
+  - bits[2:1]: address space (0=Non-Secure, 1=Secure, 2=Root, 3=Realm)
+    Only these MemTxAttrs fields (``secure`` and ``space``) are consumed today;
+    other bits are reserved but can be wired up easily if future tests need
+    to pass extra attributes.
+
+Translation Setup Workflow
+--------------------------
+``iommu-testdev`` never builds SMMU/AMD-Vi/RISC-V IOMMU structures on its own.
+Architecture-specific construction lives entirely in qtest/libqos helpers.
+Those helpers populate guest memory with page tables/architecture-specific
+structures and program the emulated IOMMU registers directly. See the
+``qsmmu_setup_and_enable_translation()`` function in
+``tests/qtest/libqos/qos-smmuv3.c`` for an example of how SMMUv3 translation
+is set up for this device.
+
+DMA Operation Flow
+------------------
+1. Test programs IOMMU translation tables
+2. Test configures DMA address (GVA_LO/HI), length, and attributes
+3. Test writes 1 to DMA_DBELL to arm the operation
+4. Test reads DMA_TRIGGERING to execute DMA
+5. Test polls DMA_RESULT:
+
+   - 0x00000000: Success
+   - 0xFFFFFFFE: Busy (still in progress)
+   - 0xDEAD000X: Various error codes
+
+The device performs a write-then-read sequence using a known pattern
+(0x88888888) and verifies data integrity automatically.
+
+Running the qtest
+-----------------
+The SMMUv3 test suite uses this device and covers multiple translation modes::
+
+    cd build-debug
+    QTEST_QEMU_BINARY=./qemu-system-aarch64 \\
+        ./tests/qtest/iommu-smmuv3-test --tap -k
+
+This test suite exercises:
+
+* Stage 1 only translation
+* Stage 2 only translation
+* Nested (Stage 1 + Stage 2) translation
+* Multiple security spaces (Non-Secure, Secure, Root, Realm)
+
+Instantiation
+-------------
+The device is not wired into any board by default. Tests instantiate it
+via QEMU command line::
+
+    -device iommu-testdev
+
+For ARM platforms with SMMUv3::
+
+    -M virt,iommu=smmuv3 -device iommu-testdev
+
+The device will be placed behind the IOMMU automatically.
+
+Limitations
+-----------
+* No realistic PCIe enumeration, MSI/MSI-X, or interrupt handling
+* No ATS/PRI support
+* No actual device functionality beyond DMA test pattern
+* Test-only; not suitable for production or machine realism
+* Address space support (Secure/Root/Realm) is architecture-dependent
+
+See also
+--------
+* ``tests/qtest/iommu-smmuv3-test.c`` — SMMUv3 test suite
+* ``tests/qtest/libqos/qos-smmuv3.{c,h}`` — SMMUv3 test library
+* SMMUv3 emulation: ``hw/arm/smmu*``
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index fccd735c24..b5f6fdbd9c 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -25,6 +25,11 @@ config PCI_TESTDEV
     default y if TEST_DEVICES
     depends on PCI
 
+config IOMMU_TESTDEV
+    bool
+    default y if TEST_DEVICES
+    depends on PCI
+
 config EDU
     bool
     default y if TEST_DEVICES
diff --git a/hw/misc/iommu-testdev.c b/hw/misc/iommu-testdev.c
new file mode 100644
index 0000000000..3182ccea4d
--- /dev/null
+++ b/hw/misc/iommu-testdev.c
@@ -0,0 +1,278 @@
+/*
+ * A test device for IOMMU
+ *
+ * This test device is a minimal IOMMU-aware device used to test the IOMMU.
+ *
+ * Copyright (c) 2025 Phytium Technology
+ *
+ * Author:
+ *  Tao Tang <tangtao1634@phytium.com.cn>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "system/address-spaces.h"
+#include "trace.h"
+#include "hw/pci/pci_device.h"
+#include "hw/qdev-properties.h"
+#include "qom/object.h"
+#include "hw/misc/iommu-testdev.h"
+
+#define TYPE_IOMMU_TESTDEV "iommu-testdev"
+OBJECT_DECLARE_SIMPLE_TYPE(IOMMUTestDevState, IOMMU_TESTDEV)
+
+struct IOMMUTestDevState {
+    PCIDevice parent_obj;
+    MemoryRegion bar0;
+    uint64_t dma_vaddr;
+    uint32_t dma_len;
+    uint32_t dma_result;
+    bool dma_pending;
+
+    AddressSpace *dma_as;   /* IOMMU-mediated DMA AS for this device */
+    uint32_t dma_attrs_cfg; /* bit0 secure, bits[2:1] space, bit3 unspecified */
+};
+
+static void iommu_testdev_maybe_run_dma(IOMMUTestDevState *s)
+{
+    uint32_t expected_val, actual_val;
+    g_autofree uint8_t *write_buf = NULL;
+    g_autofree uint8_t *read_buf = NULL;
+    MemTxResult write_res, read_res;
+    MemTxAttrs attrs;
+    AddressSpace *as;
+
+    if (!s->dma_pending) {
+        s->dma_result = ITD_DMA_ERR_NOT_ARMED;
+        trace_iommu_testdev_dma_result(s->dma_result);
+        return;
+    }
+    trace_iommu_testdev_dma_start();
+
+    s->dma_pending = false;
+
+    if (!s->dma_len) {
+        s->dma_result = ITD_DMA_ERR_BAD_LEN;
+        return;
+    }
+
+    write_buf = g_malloc(s->dma_len);
+    read_buf = g_malloc(s->dma_len);
+
+    /* Initialize MemTxAttrs from generic register */
+    attrs = MEMTXATTRS_UNSPECIFIED;
+    attrs.secure = ITD_ATTRS_GET_SECURE(s->dma_attrs_cfg);
+
+    /*
+     * The 'space' field in MemTxAttrs is ARM-specific.
+     * On other architectures where this field doesn't exist.
+     */
+    attrs.space = ITD_ATTRS_GET_SPACE(s->dma_attrs_cfg);
+
+    as = s->dma_as;
+
+    /* Step 1: Write ITD_DMA_WRITE_VAL to DMA address */
+    trace_iommu_testdev_dma_write(s->dma_vaddr, s->dma_len);
+
+    for (int i = 0; i < s->dma_len; i++) {
+        /* Data is written in little-endian order */
+        write_buf[i] = (ITD_DMA_WRITE_VAL >> ((i % 4) * 8)) & 0xff;
+    }
+    write_res = dma_memory_write(as, s->dma_vaddr, write_buf, s->dma_len,
+                                 attrs);
+
+    if (write_res != MEMTX_OK) {
+        s->dma_result = ITD_DMA_ERR_TX_FAIL;
+        trace_iommu_testdev_dma_result(s->dma_result);
+        return;
+    }
+
+    /* Step 2: Read back from the same DMA address */
+    trace_iommu_testdev_dma_read(s->dma_vaddr, s->dma_len);
+
+    read_res = dma_memory_read(as, s->dma_vaddr, read_buf, s->dma_len, attrs);
+
+    if (read_res != MEMTX_OK) {
+        s->dma_result = ITD_DMA_ERR_RD_FAIL;
+        trace_iommu_testdev_dma_result(s->dma_result);
+        return;
+    }
+
+    /* Step 3: Verify the read data matches what we wrote */
+    for (int i = 0; i < s->dma_len; i += 4) {
+        int remaining_bytes = MIN(4, s->dma_len - i);
+
+        expected_val = 0;
+        actual_val = 0;
+
+        for (int j = 0; j < remaining_bytes; j++) {
+            expected_val |= ((uint32_t)write_buf[i + j]) << (j * 8);
+            actual_val |= ((uint32_t)read_buf[i + j]) << (j * 8);
+        }
+
+        trace_iommu_testdev_dma_verify(expected_val, actual_val);
+
+        if (expected_val != actual_val) {
+            s->dma_result = ITD_DMA_ERR_MISMATCH;
+            trace_iommu_testdev_dma_result(s->dma_result);
+            return;
+        }
+    }
+
+    /* All checks passed */
+    s->dma_result = 0;
+    trace_iommu_testdev_dma_result(s->dma_result);
+}
+
+static uint64_t iommu_testdev_mmio_read(void *opaque, hwaddr addr,
+                                        unsigned size)
+{
+    IOMMUTestDevState *s = opaque;
+    uint64_t value = 0;
+
+    switch (addr) {
+    case ITD_REG_DMA_TRIGGERING:
+        /*
+         * This lets tests poll ITD_REG_DMA_RESULT to observe BUSY before
+         * consuming the DMA.
+         */
+        iommu_testdev_maybe_run_dma(s);
+        value = 0;
+        break;
+    case ITD_REG_DMA_GVA_LO:
+        value = (uint32_t)(s->dma_vaddr & 0xffffffffu);
+        break;
+    case ITD_REG_DMA_GVA_HI:
+        value = (uint32_t)(s->dma_vaddr >> 32);
+        break;
+    case ITD_REG_DMA_LEN:
+        value = s->dma_len;
+        break;
+    case ITD_REG_DMA_RESULT:
+        value = s->dma_result;
+        break;
+    case ITD_REG_DMA_ATTRS:
+        value = s->dma_attrs_cfg;
+        break;
+    default:
+        value = 0;
+        break;
+    }
+
+    trace_iommu_testdev_mmio_read(addr, value, size);
+    return value;
+}
+
+static void iommu_testdev_mmio_write(void *opaque, hwaddr addr, uint64_t val,
+                                     unsigned size)
+{
+    IOMMUTestDevState *s = opaque;
+    uint32_t data = val;
+
+    trace_iommu_testdev_mmio_write(addr, val, size);
+
+    switch (addr) {
+    case ITD_REG_DMA_GVA_LO:
+        s->dma_vaddr = (s->dma_vaddr & ~0xffffffffull) | data;
+        break;
+    case ITD_REG_DMA_GVA_HI:
+        s->dma_vaddr = (s->dma_vaddr & 0xffffffffull) |
+                       ((uint64_t)data << 32);
+        break;
+    case ITD_REG_DMA_LEN:
+        s->dma_len = data;
+        break;
+    case ITD_REG_DMA_RESULT:
+        s->dma_result = data;
+        break;
+    case ITD_REG_DMA_DBELL:
+        if (data & ITD_DMA_DBELL_ARM) {
+            /* Arm the DMA operation */
+            s->dma_pending = true;
+            s->dma_result = ITD_DMA_RESULT_BUSY;
+            trace_iommu_testdev_dma_pending(true);
+        } else {
+            /* Disarm the DMA operation */
+            s->dma_pending = false;
+            s->dma_result = ITD_DMA_RESULT_IDLE;
+            trace_iommu_testdev_dma_pending(false);
+        }
+        break;
+    case ITD_REG_DMA_ATTRS:
+        s->dma_attrs_cfg = data;
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps iommu_testdev_mmio_ops = {
+    .read = iommu_testdev_mmio_read,
+    .write = iommu_testdev_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void iommu_testdev_realize(PCIDevice *pdev, Error **errp)
+{
+    IOMMUTestDevState *s = IOMMU_TESTDEV(pdev);
+
+    s->dma_vaddr = 0;
+    s->dma_len = 0;
+    s->dma_result = ITD_DMA_RESULT_IDLE;
+    s->dma_pending = false;
+    s->dma_attrs_cfg = 0;
+    s->dma_as = pci_device_iommu_address_space(pdev);
+
+    memory_region_init_io(&s->bar0, OBJECT(pdev), &iommu_testdev_mmio_ops, s,
+                          TYPE_IOMMU_TESTDEV ".bar0", BAR0_SIZE);
+    pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0);
+}
+
+static void iommu_testdev_reset(DeviceState *dev)
+{
+    IOMMUTestDevState *s = IOMMU_TESTDEV(dev);
+
+    s->dma_vaddr = 0;
+    s->dma_len = 0;
+    s->dma_result = ITD_DMA_RESULT_IDLE;
+    s->dma_pending = false;
+    s->dma_attrs_cfg = 0;
+}
+
+static void iommu_testdev_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+
+    pc->realize = iommu_testdev_realize;
+    pc->vendor_id = IOMMU_TESTDEV_VENDOR_ID;
+    pc->device_id = IOMMU_TESTDEV_DEVICE_ID;
+    pc->revision = 0;
+    pc->class_id = PCI_CLASS_OTHERS;
+    dc->desc = "A test device for IOMMU";
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+    device_class_set_legacy_reset(dc, iommu_testdev_reset);
+}
+
+static const TypeInfo iommu_testdev_info = {
+    .name          = TYPE_IOMMU_TESTDEV,
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(IOMMUTestDevState),
+    .class_init    = iommu_testdev_class_init,
+    .interfaces    = (const InterfaceInfo[]) {
+        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+        { }
+    },
+};
+
+static void iommu_testdev_register_types(void)
+{
+    type_register_static(&iommu_testdev_info);
+}
+
+type_init(iommu_testdev_register_types);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index b1d8d8e5d2..6f9bb9bb0f 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c'))
 system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
 system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
 system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
+system_ss.add(when: 'CONFIG_IOMMU_TESTDEV', if_true: files('iommu-testdev.c'))
 system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
 system_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index eeb9243898..84fd349fb8 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -409,3 +409,13 @@ ivshmem_flat_interrupt_peer(uint16_t peer_id, uint16_t vector_id) "Interrupting
 i2c_echo_event(const char *id, const char *event) "%s: %s"
 i2c_echo_recv(const char *id, uint8_t data) "%s: recv 0x%02" PRIx8
 i2c_echo_send(const char *id, uint8_t data) "%s: send 0x%02" PRIx8
+
+# iommu-testdev.c
+iommu_testdev_mmio_read(uint64_t addr, uint64_t value, unsigned size) "addr=0x%" PRIx64 " value=0x%" PRIx64 " size=%u"
+iommu_testdev_mmio_write(uint64_t addr, uint64_t value, unsigned size) "addr=0x%" PRIx64 " value=0x%" PRIx64 " size=%u"
+iommu_testdev_dma_start(void) "DMA operation started"
+iommu_testdev_dma_write(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u"
+iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u"
+iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x"
+iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x"
+iommu_testdev_dma_pending(bool pending) "pending=%d"
diff --git a/include/hw/misc/iommu-testdev.h b/include/hw/misc/iommu-testdev.h
new file mode 100644
index 0000000000..06924e737c
--- /dev/null
+++ b/include/hw/misc/iommu-testdev.h
@@ -0,0 +1,70 @@
+/*
+ * A test device for IOMMU
+ *
+ * This test device is a minimal IOMMU-aware device used to test the IOMMU.
+ *
+ * Copyright (c) 2025 Phytium Technology
+ *
+ * Author:
+ *  Tao Tang <tangtao1634@phytium.com.cn>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_IOMMU_TESTDEV_H
+#define HW_MISC_IOMMU_TESTDEV_H
+
+#include "hw/pci/pci.h"
+
+#define IOMMU_TESTDEV_VENDOR_ID     PCI_VENDOR_ID_REDHAT
+#define IOMMU_TESTDEV_DEVICE_ID     PCI_DEVICE_ID_REDHAT_TEST
+
+/* DMA_ATTRS register bit definitions (architecture-agnostic) */
+#define ITD_ATTRS_SECURE_SHIFT      0
+#define ITD_ATTRS_SECURE_MASK       0x1
+#define ITD_ATTRS_SPACE_SHIFT       1
+#define ITD_ATTRS_SPACE_MASK        0x3
+
+/* Helper macros for setting fields */
+#define ITD_ATTRS_SET_SECURE(attrs, val)                              \
+    (((attrs) & ~(ITD_ATTRS_SECURE_MASK << ITD_ATTRS_SECURE_SHIFT)) | \
+     (((val) & ITD_ATTRS_SECURE_MASK) << ITD_ATTRS_SECURE_SHIFT))
+
+#define ITD_ATTRS_SET_SPACE(attrs, val)                               \
+    (((attrs) & ~(ITD_ATTRS_SPACE_MASK << ITD_ATTRS_SPACE_SHIFT)) |   \
+     (((val) & ITD_ATTRS_SPACE_MASK) << ITD_ATTRS_SPACE_SHIFT))
+
+/* Helper macros for getting fields */
+#define ITD_ATTRS_GET_SECURE(attrs)                                   \
+    (((attrs) >> ITD_ATTRS_SECURE_SHIFT) & ITD_ATTRS_SECURE_MASK)
+
+#define ITD_ATTRS_GET_SPACE(attrs)                                    \
+    (((attrs) >> ITD_ATTRS_SPACE_SHIFT) & ITD_ATTRS_SPACE_MASK)
+
+/* DMA result/status values shared with tests */
+#define ITD_DMA_RESULT_IDLE   0xffffffffu
+#define ITD_DMA_RESULT_BUSY   0xfffffffeu
+#define ITD_DMA_ERR_BAD_LEN   0xdead0001u
+#define ITD_DMA_ERR_TX_FAIL   0xdead0002u
+#define ITD_DMA_ERR_RD_FAIL   0xdead0003u
+#define ITD_DMA_ERR_MISMATCH  0xdead0004u
+#define ITD_DMA_ERR_NOT_ARMED 0xdead0005u
+
+#define ITD_DMA_WRITE_VAL     0x88888888u
+
+/* DMA doorbell bits */
+#define ITD_DMA_DBELL_ARM    0x1u
+
+/* BAR0 layout of iommu-testdev */
+enum {
+    ITD_REG_DMA_TRIGGERING  = 0x00,
+    ITD_REG_DMA_GVA_LO      = 0x04,
+    ITD_REG_DMA_GVA_HI      = 0x08,
+    ITD_REG_DMA_LEN         = 0x0c,
+    ITD_REG_DMA_RESULT      = 0x10,
+    ITD_REG_DMA_DBELL       = 0x14,
+    ITD_REG_DMA_ATTRS       = 0x18, /* [0] secure,[2:1] space,[3] unspecified */
+    BAR0_SIZE               = 0x1000,
+};
+
+#endif /* HW_MISC_IOMMU_TESTDEV_H */
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library
  2025-11-26 15:45 [RFC RESEND v5 0/4] hw/misc: Introduce a generalized IOMMU test framework Tao Tang
  2025-11-26 15:45 ` [RFC RESEND v5 1/4] hw/arm/smmuv3: Extract common definitions to smmuv3-common.h Tao Tang
  2025-11-26 15:45 ` [RFC RESEND v5 2/4] hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing Tao Tang
@ 2025-11-26 15:45 ` Tao Tang
  2025-12-04 23:53   ` Pierrick Bouvier
  2025-11-26 15:45 ` [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev Tao Tang
  3 siblings, 1 reply; 23+ messages in thread
From: Tao Tang @ 2025-11-26 15:45 UTC (permalink / raw)
  To: Paolo Bonzini, Fabiano Rosas, Laurent Vivier, Eric Auger,
	Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Jean-Philippe Brucker, Mostafa Saleh,
	CLEMENT MATHIEU--DRIF, Tao Tang

Introduce qos-smmuv3, a reusable library for SMMUv3-related qtest
operations. This module encapsulates common tasks like:

- SMMUv3 initialization (enabling, configuring command/event queues)
- Stream Table Entry (STE) and Context Descriptor (CD) setup
- Multi-level page table construction (L0-L3 for 4KB granules)
- Support for Stage 1, Stage 2, and nested translation modes
- Could be easily extended to support multi-space testing infrastructure
    (Non-Secure, Secure, Root, Realm)

The library provides high-level abstractions that allow test code to
focus on IOMMU behavior validation rather than low-level register
manipulation and page table encoding. Key features include:

- Automatic memory allocation for translation structures with proper
    alignment
- Helper functions to build valid STEs/CDs for different translation
    scenarios
- Page table walkers that handle address offset calculations per
    security space
- Command queue management for SMMU configuration commands

This infrastructure is designed to be used by iommu-testdev-based tests
and future SMMUv3 test suites, reducing code duplication and improving
test maintainability.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 tests/qtest/libqos/meson.build  |   3 +
 tests/qtest/libqos/qos-smmuv3.c | 731 ++++++++++++++++++++++++++++++++
 tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
 3 files changed, 1001 insertions(+)
 create mode 100644 tests/qtest/libqos/qos-smmuv3.c
 create mode 100644 tests/qtest/libqos/qos-smmuv3.h

diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build
index 1ddaf7b095..8d6758ec2b 100644
--- a/tests/qtest/libqos/meson.build
+++ b/tests/qtest/libqos/meson.build
@@ -60,6 +60,9 @@ libqos_srcs = files(
         'x86_64_pc-machine.c',
         'riscv-virt-machine.c',
         'loongarch-virt-machine.c',
+
+        # SMMU:
+        'qos-smmuv3.c',
 )
 
 if have_virtfs
diff --git a/tests/qtest/libqos/qos-smmuv3.c b/tests/qtest/libqos/qos-smmuv3.c
new file mode 100644
index 0000000000..377ceb9e53
--- /dev/null
+++ b/tests/qtest/libqos/qos-smmuv3.c
@@ -0,0 +1,731 @@
+/*
+ * QOS SMMUv3 Module
+ *
+ * This module provides SMMUv3-specific helper functions for libqos tests,
+ * encapsulating SMMUv3 setup, assertion, and cleanup operations.
+ *
+ * Copyright (c) 2025 Phytium Technology
+ *
+ * Author:
+ *  Tao Tang <tangtao1634@phytium.com.cn>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/arm/smmuv3-common.h"
+#include "tests/qtest/libqos/pci.h"
+#include "hw/misc/iommu-testdev.h"
+#include "qos-smmuv3.h"
+
+#define QSMMU_STE_S2T0SZ_VAL 0x14
+
+/* Apply space offset to address */
+static inline uint64_t qsmmu_apply_space_offs(QSMMUSpace sp,
+                                              uint64_t address)
+{
+    return address + qsmmu_space_offset(sp);
+}
+
+uint32_t qsmmu_expected_dma_result(QSMMUTestContext *ctx)
+{
+    /* Currently only non-secure space is supported. */
+    if (ctx->tx_space != QSMMU_SPACE_NONSECURE) {
+        return ITD_DMA_ERR_TX_FAIL;
+    }
+    return ctx->config.expected_result;
+}
+
+uint32_t qsmmu_build_dma_attrs(QSMMUSpace space)
+{
+    uint32_t attrs = 0;
+    switch (space) {
+    case QSMMU_SPACE_NONSECURE:
+        /* Non-secure: secure=0, space=1 */
+        attrs = ITD_ATTRS_SET_SECURE(attrs, 0);
+        attrs = ITD_ATTRS_SET_SPACE(attrs, QSMMU_SPACE_NONSECURE);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    return attrs;
+}
+
+uint32_t qsmmu_setup_and_enable_translation(QSMMUTestContext *ctx)
+{
+    uint32_t build_result;
+
+    /* Build page tables and SMMU structures first */
+    build_result = qsmmu_build_translation(
+                       ctx->qts, ctx->config.trans_mode,
+                       ctx->tx_space, ctx->sid);
+    if (build_result != 0) {
+        g_test_message("Build failed: mode=%u sid=%u status=0x%x",
+                       ctx->config.trans_mode, ctx->sid, build_result);
+        ctx->trans_status = build_result;
+        return ctx->trans_status;
+    }
+
+    /* Program SMMU registers for the appropriate security space */
+    qsmmu_program_regs(ctx->qts, ctx->smmu_base, ctx->tx_space);
+
+    ctx->trans_status = 0;
+    return ctx->trans_status;
+}
+
+uint32_t qsmmu_trigger_dma(QSMMUTestContext *ctx)
+{
+    uint32_t result, attrs_val;
+
+    /* Program DMA parameters */
+    qpci_io_writel(ctx->dev, ctx->bar, ITD_REG_DMA_GVA_LO,
+                   (uint32_t)ctx->config.dma_iova);
+    qpci_io_writel(ctx->dev, ctx->bar, ITD_REG_DMA_GVA_HI,
+                   (uint32_t)(ctx->config.dma_iova >> 32));
+    qpci_io_writel(ctx->dev, ctx->bar, ITD_REG_DMA_LEN,
+                   ctx->config.dma_len);
+
+    /* Build and write DMA attributes based on device security state. */
+    attrs_val = qsmmu_build_dma_attrs(QSMMU_SPACE_NONSECURE);
+    qpci_io_writel(ctx->dev, ctx->bar, ITD_REG_DMA_ATTRS, attrs_val);
+
+    /* Flip status */
+    /* Arm iommu-testdev so the next read triggers DMA */
+    qpci_io_writel(ctx->dev, ctx->bar, ITD_REG_DMA_DBELL, ITD_DMA_DBELL_ARM);
+
+    /* Trigger DMA by reading ID register */
+    qpci_io_readl(ctx->dev, ctx->bar, ITD_REG_DMA_TRIGGERING);
+
+    /* Poll for DMA completion */
+    for (int i = 0; i < 1000; i++) {
+        result = qpci_io_readl(ctx->dev, ctx->bar, ITD_REG_DMA_RESULT);
+        if (result != ITD_DMA_RESULT_BUSY) {
+            ctx->dma_result = result;
+            break;
+        }
+        g_usleep(1000);
+    }
+
+    /* Fallback for timeout */
+    if (ctx->dma_result == ITD_DMA_RESULT_BUSY) {
+        ctx->dma_result = ITD_DMA_ERR_TX_FAIL;
+    }
+
+    return ctx->dma_result;
+}
+
+static void qsmmu_push_cfgi_cmd(QTestState *qts, uint64_t smmu_base,
+                                QSMMUSpace bank_sp, uint32_t type,
+                                uint32_t sid, bool ssec)
+{
+    hwaddr bank_off;
+    uint32_t new_prod, base_lo, base_hi, log2size, prod;
+    uint32_t index_mask, slot, words[4];
+    uint64_t base, qbase, entry_pa;
+
+    g_assert_false(ssec);
+
+    bank_off = 0;
+
+    /* Read CMDQ_BASE register */
+    base_lo = qtest_readl(qts, smmu_base + bank_off + A_CMDQ_BASE);
+    base_hi = qtest_readl(qts, smmu_base + bank_off + A_CMDQ_BASE + 4);
+    base = ((uint64_t)base_hi << 32) | base_lo;
+    log2size = base & 0x1f;
+    qbase = base & SMMU_BASE_ADDR_MASK;
+
+    /* Read CMDQ_PROD register */
+    prod = qtest_readl(qts, smmu_base + bank_off + A_CMDQ_PROD);
+    index_mask = (1u << log2size) - 1u;
+    slot = prod & index_mask;
+    entry_pa = qbase + (uint64_t)slot * 16u;
+
+    /* Prepare command words */
+    memset(words, 0, sizeof(words));
+    words[0] = (type & 0xff) | (ssec ? (1u << 10) : 0u);
+    words[1] = sid;
+
+    /* Write command to the command queue */
+    for (int i = 0; i < 4; i++) {
+        qtest_writel(qts, entry_pa + i * 4, words[i]);
+    }
+
+    /* Update PROD to trigger command handler */
+    new_prod = (prod + 1) & ((1u << (log2size + 1)) - 1u);
+    qtest_writel(qts, smmu_base + bank_off + A_CMDQ_PROD, new_prod);
+}
+
+void qsmmu_cleanup_translation(QSMMUTestContext *ctx)
+{
+    static const QSMMUSpace spaces[] = { QSMMU_SPACE_NONSECURE };
+    size_t ste_cd_entry_bytes = sizeof(STE); /* STE size == CD size */
+    uint32_t sid;
+    uint64_t ste_addr, ste_addr_real, cd_addr_real;
+    QSMMUSpace build_space;
+
+    sid = ctx->sid;
+    ste_addr = sid * ste_cd_entry_bytes + QSMMU_STR_TAB_BASE;
+
+    /* Clear page table entries and configuration structures */
+    for (int idx = 0; idx < ARRAY_SIZE(spaces); idx++) {
+        build_space = spaces[idx];
+
+        ste_addr_real = qsmmu_apply_space_offs(build_space, ste_addr);
+        cd_addr_real = qsmmu_apply_space_offs(build_space, QSMMU_CD_GPA);
+        for (int i = 0; i < ste_cd_entry_bytes / sizeof(uint32_t); i++) {
+            qtest_writel(ctx->qts, ste_addr_real + i * 4, 0);
+            g_assert_cmpint(qtest_readl(ctx->qts, ste_addr_real + i * 4),
+                            ==, 0);
+
+            qtest_writel(ctx->qts, cd_addr_real + i * 4, 0);
+            g_assert_cmpint(qtest_readl(ctx->qts, cd_addr_real + i * 4),
+                            ==, 0);
+        }
+    }
+
+    /* Invalidate SMMU caches via configuration invalidation commands */
+    if (ctx->smmu_base) {
+        /* Issue cache invalidation commands to SMMU */
+        qsmmu_push_cfgi_cmd(ctx->qts, ctx->smmu_base, QSMMU_SPACE_NONSECURE,
+                            SMMU_CMD_CFGI_STE, sid, false);
+        qsmmu_push_cfgi_cmd(ctx->qts, ctx->smmu_base, QSMMU_SPACE_NONSECURE,
+                            SMMU_CMD_CFGI_CD, sid, false);
+        qsmmu_push_cfgi_cmd(ctx->qts, ctx->smmu_base, QSMMU_SPACE_NONSECURE,
+                            SMMU_CMD_TLBI_NSNH_ALL, sid, false);
+    }
+}
+
+bool qsmmu_validate_test_result(QSMMUTestContext *ctx)
+{
+    uint32_t expected = qsmmu_expected_dma_result(ctx);
+    g_test_message("-> Validating result: expected=0x%x actual=0x%x",
+                   expected, ctx->dma_result);
+    return (ctx->dma_result == expected);
+}
+
+QSMMUSpace qsmmu_sec_sid_to_space(QSMMUSecSID sec_sid)
+{
+    switch (sec_sid) {
+    case QSMMU_SEC_SID_NONSECURE:
+        return QSMMU_SPACE_NONSECURE;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+uint64_t qsmmu_space_offset(QSMMUSpace sp)
+{
+    switch (sp) {
+    case QSMMU_SPACE_NONSECURE:
+        return QSMMU_SPACE_OFFS_NS;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+void qsmmu_single_translation(QSMMUTestContext *ctx)
+{
+    uint32_t config_result;
+    uint32_t dma_result;
+    bool test_passed;
+
+    /* Configure SMMU translation */
+    config_result = qsmmu_setup_and_enable_translation(ctx);
+    if (config_result != 0) {
+        g_test_message("Configuration failed: mode=%u status=0x%x",
+                       ctx->config.trans_mode, config_result);
+        return;
+    }
+
+    /* Trigger DMA operation */
+    dma_result = qsmmu_trigger_dma(ctx);
+    if (dma_result != 0) {
+        g_test_message("DMA failed: mode=%u result=0x%x",
+                       ctx->config.trans_mode, dma_result);
+    } else {
+        g_test_message("-> DMA succeeded: mode=%u", ctx->config.trans_mode);
+    }
+
+    /* Validate test result */
+    test_passed = qsmmu_validate_test_result(ctx);
+    g_assert_true(test_passed);
+
+    /* Clean up translation state to prepare for the next test */
+    qsmmu_cleanup_translation(ctx);
+}
+
+void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t count,
+                             QTestState *qts, QPCIDevice *dev,
+                             QPCIBar bar, uint64_t smmu_base)
+{
+    for (int i = 0; i < count; i++) {
+        /* Initialize test memory */
+        qtest_memset(qts, configs[i].dma_iova, 0x00, configs[i].dma_len);
+        /* Execute each test configuration */
+        QSMMUTestContext ctx = {
+            .qts = qts,
+            .dev = dev,
+            .bar = bar,
+            .smmu_base = smmu_base,
+            .config = configs[i],
+            .trans_status = 0,
+            .dma_result = 0,
+            .sid = dev->devfn,
+            .tx_space = qsmmu_sec_sid_to_space(configs[i].sec_sid),
+        };
+
+        qsmmu_single_translation(&ctx);
+        g_test_message("--> Test %d completed: mode=%u sec_sid=%u "
+                       "status=0x%x result=0x%x", i, configs[i].trans_mode,
+                       configs[i].sec_sid, ctx.trans_status, ctx.dma_result);
+    }
+}
+
+uint32_t qsmmu_build_translation(QTestState *qts, QSMMUTransMode mode,
+                                 QSMMUSpace tx_space, uint32_t sid)
+{
+    uint64_t ste_addr, ste_addr_real, cd_addr_real;
+    uint64_t cd_ttb, vttb, vttb_real;
+    uint8_t nscfg0, nscfg1;
+    QSMMUSpace build_space;
+    size_t ste_cd_entry_bytes = sizeof(STE);
+    STE ste;
+    CD cd;
+
+    build_space = tx_space;
+    if (build_space != QSMMU_SPACE_NONSECURE) {
+        return 0xdeadbeafu;
+    }
+
+    /* Build STE image */
+    memset(&ste, 0, sizeof(ste));
+    switch (mode) {
+    case QSMMU_TM_S1_ONLY:
+        STE_SET_CONFIG(&ste, 0x5);
+        break;
+    case QSMMU_TM_S2_ONLY:
+        STE_SET_CONFIG(&ste, 0x6);
+        break;
+    case QSMMU_TM_NESTED:
+    default:
+        STE_SET_CONFIG(&ste, 0x7);
+        break;
+    }
+
+    STE_SET_VALID(&ste, 1);
+    STE_SET_S2T0SZ(&ste, QSMMU_STE_S2T0SZ_VAL);
+    STE_SET_S2SL0(&ste, 0x2);
+    STE_SET_S2TG(&ste, 0);
+    STE_SET_S2PS(&ste, 0x5);
+    STE_SET_S2AA64(&ste, 1);
+    STE_SET_S2ENDI(&ste, 0);
+    STE_SET_S2AFFD(&ste, 0);
+
+    /*
+     * The consistent policy also extends to pointer fetches. For cases that
+     * require reading STE.S1ContextPtr or STE.S2TTB, we still follow the same
+     * policy:
+     * - The PA space security attribute of the address pointed to
+     *   (e.g., the CD or S2L1 table) must also match the input 'SEC_SID'.
+     */
+    cd_addr_real = qsmmu_apply_space_offs(build_space, QSMMU_CD_GPA);
+    STE_SET_CTXPTR(&ste, cd_addr_real);
+
+    vttb = QSMMU_VTTB;
+    vttb_real = qsmmu_apply_space_offs(build_space, vttb);
+    STE_SET_S2TTB(&ste, vttb_real);
+
+    ste_addr = sid * ste_cd_entry_bytes + QSMMU_STR_TAB_BASE;
+    ste_addr_real = qsmmu_apply_space_offs(build_space, ste_addr);
+
+    /* Write STE to memory */
+    for (int i = 0; i < ARRAY_SIZE(ste.word); i++) {
+        qtest_writel(qts, ste_addr_real + i * 4, ste.word[i]);
+    }
+
+    switch (tx_space) {
+    case QSMMU_SPACE_NONSECURE:
+        nscfg0 = 0x1;
+        nscfg1 = 0x1;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    /* Build CD image for S1 path if needed */
+    if (mode != QSMMU_TM_S2_ONLY) {
+        memset(&cd, 0, sizeof(cd));
+
+        CD_SET_ASID(&cd, 0x1e20);
+        CD_SET_AARCH64(&cd, 1);
+        CD_SET_VALID(&cd, 1);
+        CD_SET_A(&cd, 1);
+        CD_SET_S(&cd, 0);
+        CD_SET_HD(&cd, 0);
+        CD_SET_HA(&cd, 0);
+        CD_SET_IPS(&cd, 0x4);
+        CD_SET_TBI(&cd, 0x0);
+        CD_SET_AFFD(&cd, 0x0);
+        CD_SET_EPD(&cd, 0, 0x0);
+        CD_SET_EPD(&cd, 1, 0x1);
+        CD_SET_TSZ(&cd, 0, 0x10);
+        CD_SET_TG(&cd, 0, 0x0);
+        CD_SET_ENDI(&cd, 0x0);
+
+        CD_SET_NSCFG(&cd, 0, nscfg0);
+        CD_SET_NSCFG(&cd, 1, nscfg1);
+        CD_SET_R(&cd, 0x1);
+        cd_ttb = vttb_real;
+        CD_SET_TTB(&cd, 0, cd_ttb);
+
+        for (int i = 0; i < ARRAY_SIZE(cd.word); i++) {
+            /* TODO: Maybe need more work to write to secure RAM in future */
+            qtest_writel(qts, cd_addr_real + i * 4, cd.word[i]);
+            g_assert_cmpint(qtest_readl(qts, cd_addr_real + i * 4), ==,
+                            cd.word[i]);
+        }
+    }
+
+    qsmmu_setup_translation_tables(qts, QSMMU_IOVA_OR_IPA, build_space,
+                                   false, mode);
+    /* Nested extras: CD S2 tables */
+    if (mode == QSMMU_TM_NESTED) {
+        /*
+         * Extra Stage 2 page tables is needed if
+         *          SMMUTranslationClass == SMMU_CLASS_CD
+         * as smmuv3_do_translate would translate an IPA of the CD to the final
+         * output CD after a Stage 2 translation.
+         */
+        qsmmu_setup_translation_tables(qts, cd_addr_real, build_space,
+                                       true, mode);
+    }
+
+    return 0;
+}
+
+uint64_t qsmmu_bank_base(uint64_t base, QSMMUSpace sp)
+{
+    switch (sp) {
+    case QSMMU_SPACE_NONSECURE:
+        return base;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+void qsmmu_program_bank(QTestState *qts, uint64_t bank_base, QSMMUSpace sp)
+{
+    uint64_t cmdq_base, eventq_base, strtab_base;
+
+    qtest_writel(qts, bank_base + A_GBPA, 0x80000000);  /* UPDATE */
+    qtest_writel(qts, bank_base + A_CR0, 0x0);          /* Disable */
+    qtest_writel(qts, bank_base + A_CR1, 0x0d75);       /* Config */
+
+    /* CMDQ_BASE: add address-space offset*/
+    cmdq_base = qsmmu_apply_space_offs(sp, QSMMU_CMDQ_BASE_ADDR);
+    cmdq_base |= 0x0a;  /* Size and valid bits */
+    qtest_writeq(qts, bank_base + A_CMDQ_BASE, cmdq_base);
+
+    qtest_writel(qts, bank_base + A_CMDQ_CONS, 0x0);
+    qtest_writel(qts, bank_base + A_CMDQ_PROD, 0x0);
+
+    /* EVENTQ_BASE: add address-space offset */
+    eventq_base = qsmmu_apply_space_offs(sp, QSMMU_EVENTQ_BASE_ADDR);
+    eventq_base |= 0x0a;  /* Size and valid bits */
+    qtest_writeq(qts, bank_base + A_EVENTQ_BASE, eventq_base);
+
+    qtest_writel(qts, bank_base + A_EVENTQ_PROD, 0x0);
+    qtest_writel(qts, bank_base + A_EVENTQ_CONS, 0x0);
+
+    /* STRTAB_BASE_CFG: linear stream table, LOG2SIZE=5 */
+    qtest_writel(qts, bank_base + A_STRTAB_BASE_CFG, 0x5);
+
+    /* STRTAB_BASE: add address-space offset */
+    strtab_base = qsmmu_apply_space_offs(sp, QSMMU_STR_TAB_BASE);
+    qtest_writeq(qts, bank_base + A_STRTAB_BASE, strtab_base);
+
+    /* CR0: Enable SMMU with appropriate flags */
+    qtest_writel(qts, bank_base + A_CR0, 0xd);
+}
+
+void qsmmu_program_regs(QTestState *qts, uint64_t smmu_base, QSMMUSpace space)
+{
+    uint64_t sp_base;
+    /* Always program Non-Secure bank first */
+    uint64_t ns_base = qsmmu_bank_base(smmu_base, QSMMU_SPACE_NONSECURE);
+    qsmmu_program_bank(qts, ns_base, QSMMU_SPACE_NONSECURE);
+
+    /* Program the requested space if different from Non-Secure */
+    sp_base = qsmmu_bank_base(smmu_base, space);
+    if (sp_base != ns_base) {
+        qsmmu_program_bank(qts, sp_base, space);
+    }
+}
+
+static uint32_t qsmmu_get_table_index(uint64_t addr, int level)
+{
+    switch (level) {
+    case 0:
+        return (addr >> 39) & 0x1ff;
+    case 1:
+        return (addr >> 30) & 0x1ff;
+    case 2:
+        return (addr >> 21) & 0x1ff;
+    case 3:
+        return (addr >> 12) & 0x1ff;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static uint64_t qsmmu_get_table_addr(uint64_t base, int level, uint64_t iova)
+{
+    uint32_t index = qsmmu_get_table_index(iova, level);
+    return (base & QSMMU_PTE_MASK) + (index * 8);
+}
+
+/*
+ * qsmmu_get_pte_attrs - Calculate the S1 leaf PTE value
+ *
+ * IOMMU need to set different attributes for PTEs based on the translation mode
+ */
+static uint64_t qsmmu_get_pte_attrs(QSMMUTransMode mode, bool is_leaf,
+                                    QSMMUSpace space)
+{
+    uint64_t rw_mask = QSMMU_LEAF_PTE_RW_MASK;
+    uint64_t ro_mask = QSMMU_LEAF_PTE_RO_MASK;
+    uint64_t non_leaf_mask = QSMMU_NON_LEAF_PTE_MASK;
+
+    switch (space) {
+    case QSMMU_SPACE_NONSECURE:
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    if (!is_leaf) {
+        return non_leaf_mask;
+    }
+
+    /* For leaf PTE */
+    if (mode == QSMMU_TM_NESTED || mode == QSMMU_TM_S1_ONLY) {
+        return rw_mask;
+    }
+
+    return ro_mask;
+}
+
+/*
+ * qsmmu_setup_s2_walk_for_ipa - Setup Stage 2 page table walk for an IPA
+ *
+ * @qts: QTest state handle
+ * @space: Security space
+ * @ipa: Intermediate Physical Address to translate
+ * @s2_vttb: Stage 2 VTTB (page table base)
+ * @mode: Translation mode
+ * @is_final: Whether this is the final S2 walk (not nested within S1)
+ *
+ * Calculates and writes a 4-level Stage 2 page table walk for the given IPA.
+ * This function dynamically generates and writes all page table entries
+ * (L0-L3) to guest memory based on the input IPA and configuration.
+ */
+static void qsmmu_setup_s2_walk_for_ipa(QTestState *qts,
+                                        QSMMUSpace space,
+                                        uint64_t ipa,
+                                        uint64_t s2_vttb,
+                                        QSMMUTransMode mode,
+                                        bool is_final)
+{
+    uint64_t all_s2_l0_pte_val;
+    uint64_t all_s2_l1_pte_val;
+    uint64_t all_s2_l2_pte_val;
+    uint64_t all_s2_l3_pte_val;
+    uint64_t s2_l0_addr, s2_l1_addr, s2_l2_addr, s2_l3_addr;
+
+    /* Shared intermediate PTE values for all S2 walks */
+    all_s2_l0_pte_val = qsmmu_apply_space_offs(
+        space, QSMMU_L0_PTE_VAL | qsmmu_get_pte_attrs(mode, false, space));
+    all_s2_l1_pte_val = qsmmu_apply_space_offs(
+        space, QSMMU_L1_PTE_VAL | qsmmu_get_pte_attrs(mode, false, space));
+    all_s2_l2_pte_val = qsmmu_apply_space_offs(
+        space, QSMMU_L2_PTE_VAL | qsmmu_get_pte_attrs(mode, false, space));
+
+    /* Stage 2 Level 0 */
+    s2_l0_addr = qsmmu_get_table_addr(s2_vttb, 0, ipa);
+    qtest_writeq(qts, s2_l0_addr, all_s2_l0_pte_val);
+
+    /* Stage 2 Level 1 */
+    s2_l1_addr = qsmmu_get_table_addr(all_s2_l0_pte_val, 1, ipa);
+    qtest_writeq(qts, s2_l1_addr, all_s2_l1_pte_val);
+
+    /* Stage 2 Level 2 */
+    s2_l2_addr = qsmmu_get_table_addr(all_s2_l1_pte_val, 2, ipa);
+    qtest_writeq(qts, s2_l2_addr, all_s2_l2_pte_val);
+
+    /* Stage 2 Level 3 (leaf) */
+    s2_l3_addr = qsmmu_get_table_addr(all_s2_l2_pte_val, 3, ipa);
+
+    /*
+     * Stage 2 L3 PTE attributes depend on the context:
+     * - For nested S1 table address translations (!is_final):
+     *   Use LEAF attrs (0x763) because these PTEs map S1 table pages directly
+     * - For final S2 walk (is_final):
+     *   Use TABLE attrs (0x7e3) for the final IPA→PA mapping
+     */
+    if (!is_final) {
+        all_s2_l3_pte_val =
+            (ipa & QSMMU_PTE_MASK) |
+            qsmmu_get_pte_attrs(QSMMU_TM_NESTED, true, space);
+    } else {
+        all_s2_l3_pte_val =
+            (ipa & QSMMU_PTE_MASK) |
+            qsmmu_get_pte_attrs(QSMMU_TM_S2_ONLY, true, space);
+    }
+
+    qtest_writeq(qts, s2_l3_addr, all_s2_l3_pte_val);
+}
+
+/*
+ * qsmmu_setup_s1_level_with_nested_s2 - Setup S1 level with nested S2 walk
+ *
+ * @qts: QTest state handle
+ * @space: Security space
+ * @s1_level: Stage 1 level (0-3)
+ * @s1_pte_addr: Stage 1 PTE address (as IPA)
+ * @s1_pte_val: Stage 1 PTE value to write
+ * @s2_vttb: Stage 2 VTTB for nested translation
+ * @mode: Translation mode
+ *
+ * For nested translation, each S1 table access requires a full S2 walk
+ * to translate the S1 table's IPA to PA. This function performs the nested
+ * S2 walk and writes the S1 PTE value to guest memory.
+ */
+static void qsmmu_setup_s1_level_with_nested_s2(QTestState *qts,
+                                                QSMMUSpace space,
+                                                int s1_level,
+                                                uint64_t s1_pte_addr,
+                                                uint64_t s1_pte_val,
+                                                uint64_t s2_vttb,
+                                                QSMMUTransMode mode)
+{
+    /*
+     * Perform nested S2 walk to translate S1 table IPA to PA.
+     * This is always needed for S1_ONLY/S2_ONLY/NESTED modes because:
+     * - S1_ONLY: Needs S2 tables for "IPA as PA" mapping (for testing)
+     * - S2_ONLY: Needs S2 tables for direct translation
+     * - NESTED: Needs S2 tables for nested translation
+     */
+    qsmmu_setup_s2_walk_for_ipa(qts, space, s1_pte_addr,
+                                s2_vttb, mode, false);
+
+    /* Write the S1 PTE value */
+    qtest_writeq(qts, s1_pte_addr, s1_pte_val);
+}
+
+/*
+ * qsmmu_setup_translation_tables - Setup SMMU translation tables
+ *
+ * The 'SEC_SID' represents the input security state of the device/transaction,
+ * whether it's a static Secure state or a dynamically-switched Realm state.
+ * SEC_SID has been converted to the corresponding SEcurity Space (QSMMUSpace)
+ * before calling this function.
+ *
+ * In a real SMMU translation, this input security state does not unilaterally
+ * determine the output Physical Address (PA) space. The output PA space is
+ * ultimately determined by attributes encountered during the page table walk,
+ * such as NSCFG and NSTable.
+ *
+ * However, for the specific context of testing the SMMU with the iommu-testdev,
+ * and to simplify the future support for Secure and Realm states, we adopt a
+ * consistent policy:
+ *
+ * - We always ensure that the page table attributes (e.g., nscfg, nstable)
+ * *match* the input 'SEC_SID' of the test case.
+ *
+ * For example: If 'SEC_SID' is Non-Secure, the corresponding nscfg and nstable
+ * attributes in the translation tables will always be set to 1.
+ *
+ */
+void qsmmu_setup_translation_tables(QTestState *qts,
+                                    uint64_t iova,
+                                    QSMMUSpace space,
+                                    bool is_cd,
+                                    QSMMUTransMode mode)
+{
+    uint64_t all_s2_l0_pte_val, all_s2_l1_pte_val, all_s2_l2_pte_val;
+    uint64_t s1_vttb, s2_vttb, s1_leaf_pte_val;
+    uint64_t l0_addr, l1_addr, l2_addr, l3_addr;
+
+    g_test_message("Begin of construction: IOVA=0x%lx mode=%d is_building_CD=%s"
+                   " ===", iova, mode, is_cd ? "yes" : "no");
+
+    /* Initialize shared S2 PTE values used across all walks */
+    all_s2_l0_pte_val = qsmmu_apply_space_offs(
+        space, QSMMU_L0_PTE_VAL | qsmmu_get_pte_attrs(mode, false, space));
+    all_s2_l1_pte_val = qsmmu_apply_space_offs(
+        space, QSMMU_L1_PTE_VAL | qsmmu_get_pte_attrs(mode, false, space));
+    all_s2_l2_pte_val = qsmmu_apply_space_offs(
+        space, QSMMU_L2_PTE_VAL | qsmmu_get_pte_attrs(mode, false, space));
+
+    /* Both S1 and S2 share the same VTTB base */
+    s1_vttb = qsmmu_apply_space_offs(space, QSMMU_VTTB & QSMMU_PTE_MASK);
+    s2_vttb = s1_vttb;
+
+    if (!is_cd) {
+        /*
+         * Setup Stage 1 page tables with nested Stage 2 walks.
+         * For each S1 level (L0-L3), we need to:
+         * 1. Calculate S1 PTE address (as IPA)
+         * 2. Perform nested S2 walk to translate that IPA to PA
+         * 3. Write the S1 PTE value
+         */
+
+        /* Stage 1 Level 0 */
+        l0_addr = qsmmu_get_table_addr(s1_vttb, 0, iova);
+        qsmmu_setup_s1_level_with_nested_s2(qts, space, 0, l0_addr,
+                                            all_s2_l0_pte_val, s2_vttb, mode);
+
+        /* Stage 1 Level 1 */
+        l1_addr = qsmmu_get_table_addr(all_s2_l0_pte_val & QSMMU_PTE_MASK,
+                                       1, iova);
+        qsmmu_setup_s1_level_with_nested_s2(qts, space, 1, l1_addr,
+                                            all_s2_l1_pte_val, s2_vttb, mode);
+
+        /* Stage 1 Level 2 */
+        l2_addr = qsmmu_get_table_addr(all_s2_l1_pte_val & QSMMU_PTE_MASK,
+                                       2, iova);
+        qsmmu_setup_s1_level_with_nested_s2(qts, space, 2, l2_addr,
+                                            all_s2_l2_pte_val, s2_vttb, mode);
+
+        /* Stage 1 Level 3 (leaf) */
+        l3_addr = qsmmu_get_table_addr(all_s2_l2_pte_val & QSMMU_PTE_MASK,
+                                       3, iova);
+
+        s1_leaf_pte_val = qsmmu_apply_space_offs(
+            space, QSMMU_L3_PTE_VAL | qsmmu_get_pte_attrs(mode, true, space)
+        );
+
+        qsmmu_setup_s1_level_with_nested_s2(qts, space, 3, l3_addr,
+                                            s1_leaf_pte_val, s2_vttb, mode);
+    } else {
+        /*
+         * For CD address translation, we start directly with the IPA.
+         */
+        s1_leaf_pte_val = iova | qsmmu_get_pte_attrs(QSMMU_TM_NESTED,
+                                                     false, space);
+    }
+
+    /*
+     * Final Stage 2 walk: Translate the result from Stage 1.
+     * - For S1_ONLY: This is skipped in hardware but we set it up for testing
+     * - For S2_ONLY: This is the only walk
+     * - For NESTED: This translates the IPA from S1 to final PA
+     * - For CD address (is_cd=true): This is a table address, use !is_final
+     */
+    qsmmu_setup_s2_walk_for_ipa(qts, space, s1_leaf_pte_val, s2_vttb,
+                                mode, !is_cd);
+
+    /* Calculate and log final translated PA */
+    g_test_message("End of construction: PA=0x%llx ===",
+                   (s1_leaf_pte_val & QSMMU_PTE_MASK) + (iova & 0xfff));
+}
diff --git a/tests/qtest/libqos/qos-smmuv3.h b/tests/qtest/libqos/qos-smmuv3.h
new file mode 100644
index 0000000000..8f1dbfeb10
--- /dev/null
+++ b/tests/qtest/libqos/qos-smmuv3.h
@@ -0,0 +1,267 @@
+/*
+ * QOS SMMUv3 Module
+ *
+ * This module provides SMMUv3-specific helper functions for libqos tests,
+ * encapsulating SMMUv3 setup, assertion, and cleanup operations.
+ *
+ * Copyright (c) 2025 Phytium Technology
+ *
+ * Author:
+ *  Tao Tang <tangtao1634@phytium.com.cn>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef QTEST_LIBQOS_SMMUV3_H
+#define QTEST_LIBQOS_SMMUV3_H
+
+#include "hw/misc/iommu-testdev.h"
+
+/*
+ * SMMU MMIO register base. We hardcode this as base_memmap is 'static const'
+ * in hw/arm/virt.c and not directly accessible here.
+ */
+#define VIRT_SMMU_BASE            0x0000000009050000ull
+
+/* SMMU queue and table base addresses */
+#define QSMMU_CMDQ_BASE_ADDR      0x000000000e16b000ull
+#define QSMMU_EVENTQ_BASE_ADDR    0x000000000e170000ull
+
+/*
+ * Translation tables and descriptors for a mapping of
+ *   - IOVA(Stage 1 only)
+ *   - IPA (Stage 2 only)
+ * to GPA.
+ *
+ * The translation is based on the Arm architecture with the following
+ * prerequisites:
+ * - Granule size: 4KB pages.
+ * - Page table levels: 4 levels (L0, L1, L2, L3), starting at level 0.
+ * - IOVA size: The walk resolves a IOVA: 0x8080604567
+ * - Address space: The 4-level lookup with 4KB granules supports up to a
+ * 48-bit (256TB) virtual address space. Each level uses a 9-bit index
+ * (512 entries per table). The breakdown is:
+ * - L0 index: IOVA bits [47:39]
+ * - L1 index: IOVA bits [38:30]
+ * - L2 index: IOVA bits [29:21]
+ * - L3 index: IOVA bits [20:12]
+ * - Page offset: IOVA bits [11:0]
+ *
+ * NOTE: All physical addresses defined here (QSMMU_VTTB, table addresses, etc.)
+ * appear to be within a secure RAM region. In practice, an offset is added
+ * to these values to place them in non-secure RAM. For example, when running
+ * in a virt machine type, the RAM base address (e.g., 0x40000000) is added to
+ * these constants.
+ */
+#define QSMMU_IOVA_OR_IPA               0x0000008080604567ull
+#define QSMMU_VTTB                      0x000000000e4d0000ull
+#define QSMMU_STR_TAB_BASE              0x000000000e179000ull
+#define QSMMU_CD_GPA                    (QSMMU_STR_TAB_BASE - 0x40ull)
+
+
+#define QSMMU_L0_PTE_VAL                0x000000000e4d1000ull
+#define QSMMU_L1_PTE_VAL                0x000000000e4d2000ull
+#define QSMMU_L2_PTE_VAL                0x000000000e4d3000ull
+#define QSMMU_L3_PTE_VAL                0x000000000ecba000ull
+
+#define QSMMU_NON_LEAF_PTE_MASK         0x8000000000000003ull
+#define QSMMU_LEAF_PTE_RO_MASK          0x04000000000007e3ull
+#define QSMMU_LEAF_PTE_RW_MASK          0x0400000000000763ull
+#define QSMMU_PTE_MASK                  0x0000fffffffff000ull
+
+/*
+ * Address-space base offsets for test tables.
+ * - Non-Secure uses a fixed offset, keeping internal layout identical.
+ *
+ * Note: Future spaces (e.g. Secure/Realm/Root) are not implemented here.
+ * When needed, introduce new offsets and reuse the helpers below so relative
+ * layout stays identical across spaces.
+ */
+#define QSMMU_SPACE_OFFS_NS             0x0000000040000000ull
+
+typedef enum QSMMUSecSID {
+    QSMMU_SEC_SID_NONSECURE    = 0,
+} QSMMUSecSID;
+
+typedef enum QSMMUSpace {
+    QSMMU_SPACE_NONSECURE      = 1,
+} QSMMUSpace;
+
+typedef enum QSMMUTransMode {
+    QSMMU_TM_S1_ONLY           = 0,
+    QSMMU_TM_S2_ONLY           = 1,
+    QSMMU_TM_NESTED            = 2,
+} QSMMUTransMode;
+
+typedef struct QSMMUTestConfig {
+    QSMMUTransMode trans_mode;        /* Translation mode (S1, S2, Nested) */
+    QSMMUSecSID sec_sid;              /* SEC_SID of test device */
+    uint64_t dma_iova;                /* DMA IOVA address for testing */
+    uint32_t dma_len;                 /* DMA length for testing */
+    uint32_t expected_result;         /* Expected DMA result for validation */
+} QSMMUTestConfig;
+
+typedef struct QSMMUTestContext {
+    QTestState *qts;            /* QTest state handle */
+    QPCIDevice *dev;            /* PCI device handle */
+    QPCIBar bar;                /* PCI BAR for MMIO access */
+    QSMMUTestConfig config;     /* Test configuration */
+    uint64_t smmu_base;         /* SMMU base address */
+    uint32_t trans_status;      /* Translation configuration status */
+    uint32_t dma_result;        /* DMA operation result */
+    uint32_t sid;               /* Stream ID for the test */
+    QSMMUSpace tx_space;        /* Cached transaction space */
+} QSMMUTestContext;
+
+/* Convert SEC_SID to corresponding Security Space */
+QSMMUSpace qsmmu_sec_sid_to_space(QSMMUSecSID sec_sid);
+
+/* Get base offset of the specific Security space */
+uint64_t qsmmu_space_offset(QSMMUSpace sp);
+
+uint32_t qsmmu_build_dma_attrs(QSMMUSpace space);
+
+/*
+ * qsmmu_setup_and_enable_translation - Complete translation setup and enable
+ *
+ * @ctx: Test context containing configuration and device handles
+ *
+ * Returns: Translation status (0 = success, non-zero = error)
+ *
+ * This function performs the complete translation setup sequence:
+ * 1. Builds all required SMMU structures (STE, CD, page tables)
+ * 2. Programs SMMU registers for the appropriate security space
+ * 3. Returns configuration status
+ */
+uint32_t qsmmu_setup_and_enable_translation(QSMMUTestContext *ctx);
+
+/*
+ * qsmmu_build_translation - Build SMMU translation structures
+ *
+ * @qts: QTest state handle
+ * @mode: Translation mode (S1_ONLY, S2_ONLY, NESTED)
+ * @tx_space: Transaction security space
+ * @sid: Stream ID
+ *
+ * Returns: Build status (0 = success, non-zero = error)
+ *
+ * Constructs all necessary SMMU translation structures in guest memory:
+ * - Stream Table Entry (STE) for the given SID
+ * - Context Descriptor (CD) if Stage 1 translation is involved
+ * - Complete page table hierarchy based on translation mode
+ *
+ * The structures are written to security-space-specific memory regions.
+ */
+uint32_t qsmmu_build_translation(QTestState *qts, QSMMUTransMode mode,
+                                 QSMMUSpace tx_space, uint32_t sid);
+
+/*
+ * qsmmu_bank_base - Get SMMU control bank base address
+ *
+ * @base: SMMU base address
+ * @sp: Security space
+ *
+ * Returns: Bank base address for the given security space
+ *
+ * Maps security space to the corresponding SMMU control register bank.
+ * Currently only Non-Secure bank is supported.
+ */
+uint64_t qsmmu_bank_base(uint64_t base, QSMMUSpace sp);
+
+/*
+ * qsmmu_program_bank - Program SMMU control bank registers
+ *
+ * @qts: QTest state handle
+ * @bank_base: SMMU bank base address
+ * @sp: Security space
+ *
+ * Programs a specific SMMU control bank with minimal configuration:
+ * - Global Bypass Attribute (GBPA)
+ * - Control registers (CR0, CR1)
+ * - Command queue (base, producer, consumer)
+ * - Event queue (base, producer, consumer)
+ * - Stream table configuration (base, format)
+ *
+ * Addresses are adjusted based on security space offset.
+ */
+void qsmmu_program_bank(QTestState *qts, uint64_t bank_base, QSMMUSpace sp);
+
+/*
+ * qsmmu_program_regs - Program all required SMMU register banks
+ *
+ * @qts: QTest state handle
+ * @smmu_base: SMMU base address
+ * @space: Target security space
+ *
+ * Programs SMMU registers for the requested security space which is called in
+ * qsmmu_setup_and_enable_translation. Always programs Non-Secure bank first,
+ * then the target space if different.
+ */
+void qsmmu_program_regs(QTestState *qts, uint64_t smmu_base, QSMMUSpace space);
+
+uint32_t qsmmu_trigger_dma(QSMMUTestContext *ctx);
+
+/*
+ * qsmmu_cleanup_translation - Clean up translation configuration
+ *
+ * @ctx: Test context containing configuration and device handles
+ *
+ * Clears all translation structures and invalidates SMMU caches:
+ * - Clears STE and CD entries
+ * - Issues SMMU invalidation commands (CFGI_STE, CFGI_CD, TLBI_NSNH_ALL)
+ */
+void qsmmu_cleanup_translation(QSMMUTestContext *ctx);
+
+/* qsmmu_expected_dma_result - Calculate expected DMA result */
+uint32_t qsmmu_expected_dma_result(QSMMUTestContext *ctx);
+
+/*
+ * qsmmu_validate_test_result - Validate actual VS expected test result
+ *
+ * @ctx: Test context containing actual and expected results
+ *
+ * Returns: true if test passed (actual == expected), false otherwise
+ *
+ * Compares the actual DMA result with the expected result and logs
+ * the comparison for debugging purposes.
+ */
+bool qsmmu_validate_test_result(QSMMUTestContext *ctx);
+
+/*
+ * qsmmu_setup_translation_tables - Setup complete SMMU page table hierarchy
+ *
+ * @qts: QTest state handle
+ * @iova: Input Virtual Address or IPA to translate
+ * @space: Security space (NONSECURE, SECURE, REALM, ROOT)
+ * @is_cd: Whether translating CD address (vs regular IOVA)
+ * @mode: Translation mode (S1_ONLY, S2_ONLY, NESTED)
+ *
+ * This function builds the complete page table structure for translating
+ * the given IOVA through the SMMU. The structure varies based on mode:
+ *
+ * - S1_ONLY: Single Stage 1 walk (IOVA -> PA)
+ * - S2_ONLY: Single Stage 2 walk (IPA -> PA)
+ * - NESTED: Stage 1 walk (IOVA -> IPA) with nested S2 walks for each
+ *   S1 table access, plus final S2 walk for the result IPA
+ *
+ * For nested mode, this creates a complex hierarchy:
+ * - 4 Stage 1 levels (L0-L3), each requiring a 4-level Stage 2 walk
+ * - 1 final Stage 2 walk for the resulting IPA
+ *
+ * The function writes all necessary Page Table Entries (PTEs) to guest
+ * memory using qtest_writeq(), setting up the complete translation path
+ * that the SMMU hardware will traverse during DMA operations.
+ */
+void qsmmu_setup_translation_tables(QTestState *qts,
+                                    uint64_t iova,
+                                    QSMMUSpace space,
+                                    bool is_cd,
+                                    QSMMUTransMode mode);
+
+/* High-level test execution functions */
+void qsmmu_single_translation(QSMMUTestContext *ctx);
+void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t count,
+                             QTestState *qts, QPCIDevice *dev,
+                             QPCIBar bar, uint64_t smmu_base);
+
+#endif /* QTEST_LIBQOS_SMMUV3_H */
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev
  2025-11-26 15:45 [RFC RESEND v5 0/4] hw/misc: Introduce a generalized IOMMU test framework Tao Tang
                   ` (2 preceding siblings ...)
  2025-11-26 15:45 ` [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library Tao Tang
@ 2025-11-26 15:45 ` Tao Tang
  2025-12-04 18:42   ` Pierrick Bouvier
  2025-12-10 18:45   ` Eric Auger
  3 siblings, 2 replies; 23+ messages in thread
From: Tao Tang @ 2025-11-26 15:45 UTC (permalink / raw)
  To: Paolo Bonzini, Fabiano Rosas, Laurent Vivier, Eric Auger,
	Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Jean-Philippe Brucker, Mostafa Saleh,
	CLEMENT MATHIEU--DRIF, Tao Tang

Add a qtest suite that validates ARM SMMUv3 translation without guest
firmware or OS. The tests leverage iommu-testdev to trigger DMA
operations and the qos-smmuv3 library to configure IOMMU translation
structures.

This test suite targets the virt machine and covers:
- Stage 1 only translation (VA -> PA via CD page tables)
- Stage 2 only translation (IPA -> PA via STE S2 tables)
- Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)
- Design to extended to support multiple security spaces
    (Non-Secure, Secure, Root, Realm)

Each test case follows this sequence:
1. Initialize SMMUv3 with appropriate command/event queues
2. Build translation tables (STE/CD/PTE) for the target scenario
3. Configure iommu-testdev with IOVA and DMA attributes via MMIO
4. Trigger DMA and validate successful translation
5. Verify data integrity through a deterministic write-read pattern

This bare-metal approach provides deterministic IOMMU testing with
minimal dependencies, making failures directly attributable to the SMMU
translation path.

Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
---
 tests/qtest/iommu-smmuv3-test.c | 114 ++++++++++++++++++++++++++++++++
 tests/qtest/meson.build         |   1 +
 2 files changed, 115 insertions(+)
 create mode 100644 tests/qtest/iommu-smmuv3-test.c

diff --git a/tests/qtest/iommu-smmuv3-test.c b/tests/qtest/iommu-smmuv3-test.c
new file mode 100644
index 0000000000..af438ecce0
--- /dev/null
+++ b/tests/qtest/iommu-smmuv3-test.c
@@ -0,0 +1,114 @@
+/*
+ * QTest for SMMUv3 with iommu-testdev
+ *
+ * This QTest file is used to test the SMMUv3 with iommu-testdev so that we can
+ * test SMMUv3 without any guest kernel or firmware.
+ *
+ * Copyright (c) 2025 Phytium Technology
+ *
+ * Author:
+ *  Tao Tang <tangtao1634@phytium.com.cn>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/generic-pcihost.h"
+#include "hw/pci/pci_regs.h"
+#include "hw/misc/iommu-testdev.h"
+#include "libqos/qos-smmuv3.h"
+
+#define DMA_LEN           4
+
+/* Test configurations for different SMMU modes and spaces */
+static const QSMMUTestConfig base_test_configs[] = {
+    {
+        .trans_mode = QSMMU_TM_S1_ONLY,
+        .sec_sid = QSMMU_SEC_SID_NONSECURE,
+        .dma_iova = QSMMU_IOVA_OR_IPA,
+        .dma_len = DMA_LEN,
+        .expected_result = 0
+    },
+    {
+        .trans_mode = QSMMU_TM_S2_ONLY,
+        .sec_sid = QSMMU_SEC_SID_NONSECURE,
+        .dma_iova = QSMMU_IOVA_OR_IPA,
+        .dma_len = DMA_LEN,
+        .expected_result = 0
+    },
+    {
+        .trans_mode = QSMMU_TM_NESTED,
+        .sec_sid = QSMMU_SEC_SID_NONSECURE,
+        .dma_iova = QSMMU_IOVA_OR_IPA,
+        .dma_len = DMA_LEN,
+        .expected_result = 0
+    }
+};
+
+static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QGenericPCIBus *gbus,
+                                          QPCIBar *bar)
+{
+    uint16_t vid, did;
+    QPCIDevice *dev = NULL;
+
+    qpci_init_generic(gbus, qts, NULL, false);
+
+    /* Find device by vendor/device ID to avoid slot surprises. */
+    for (int s = 0; s < 32 && !dev; s++) {
+        for (int fn = 0; fn < 8 && !dev; fn++) {
+            QPCIDevice *cand = qpci_device_find(&gbus->bus, QPCI_DEVFN(s, fn));
+            if (!cand) {
+                continue;
+            }
+            vid = qpci_config_readw(cand, PCI_VENDOR_ID);
+            did = qpci_config_readw(cand, PCI_DEVICE_ID);
+            if (vid == IOMMU_TESTDEV_VENDOR_ID &&
+                did == IOMMU_TESTDEV_DEVICE_ID) {
+                dev = cand;
+                g_test_message("Found iommu-testdev! devfn: 0x%x", cand->devfn);
+            } else {
+                g_free(cand);
+            }
+        }
+    }
+    g_assert(dev);
+
+    qpci_device_enable(dev);
+    *bar = qpci_iomap(dev, 0, NULL);
+    g_assert_false(bar->is_io);
+
+    return dev;
+}
+
+static void test_smmuv3_translation(void)
+{
+    QTestState *qts;
+    QGenericPCIBus gbus;
+    QPCIDevice *dev;
+    QPCIBar bar;
+
+    /* Initialize QEMU environment for SMMU testing */
+    qts = qtest_init("-machine virt,acpi=off,gic-version=3,iommu=smmuv3 "
+                     "-smp 1 -m 512 -cpu max -net none "
+                     "-device iommu-testdev");
+
+    /* Setup and configure PCI device */
+    dev = setup_qtest_pci_device(qts, &gbus, &bar);
+    g_assert(dev);
+
+    /* Run the enhanced translation tests */
+    g_test_message("### Starting SMMUv3 translation tests...###");
+    qsmmu_translation_batch(base_test_configs, ARRAY_SIZE(base_test_configs),
+                            qts, dev, bar, VIRT_SMMU_BASE);
+    g_test_message("### SMMUv3 translation tests completed successfully! ###");
+    qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+    qtest_add_func("/iommu-testdev/translation", test_smmuv3_translation);
+    return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 669d07c06b..e2d2e68092 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -263,6 +263,7 @@ qtests_aarch64 = \
    config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \
   (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \
   (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \
+  (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-smmuv3-test'] : []) + \
   qtests_cxl +                                                                                  \
   ['arm-cpu-features',
    'numa-test',
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 1/4] hw/arm/smmuv3: Extract common definitions to smmuv3-common.h
  2025-11-26 15:45 ` [RFC RESEND v5 1/4] hw/arm/smmuv3: Extract common definitions to smmuv3-common.h Tao Tang
@ 2025-12-04 18:19   ` Pierrick Bouvier
  0 siblings, 0 replies; 23+ messages in thread
From: Pierrick Bouvier @ 2025-12-04 18:19 UTC (permalink / raw)
  To: Tao Tang, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

On 11/26/25 7:45 AM, Tao Tang wrote:
> Move register definitions, command enums, and Stream Table Entry (STE) /
> Context Descriptor (CD) structure definitions from the internal header
> hw/arm/smmuv3-internal.h to a new common header
> include/hw/arm/smmuv3-common.h.
> 
> This allows other components, such as generic SMMUv3 tests or test devices,
> to utilize these definitions without including the specific SMMUv3 device
> internal state.
> 
> In addition, refactor the STE and CD field definitions to consistently use
> the 'hw/registerfields.h' API and introduce corresponding setter macros to
> facilitate structure manipulation.
> 
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
>   hw/arm/smmuv3-internal.h       | 255 +-----------------
>   include/hw/arm/smmuv3-common.h | 461 +++++++++++++++++++++++++++++++++
>   2 files changed, 462 insertions(+), 254 deletions(-)
>   create mode 100644 include/hw/arm/smmuv3-common.h
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 2/4] hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing
  2025-11-26 15:45 ` [RFC RESEND v5 2/4] hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing Tao Tang
@ 2025-12-04 18:36   ` Pierrick Bouvier
  2025-12-10 18:35   ` Eric Auger
  1 sibling, 0 replies; 23+ messages in thread
From: Pierrick Bouvier @ 2025-12-04 18:36 UTC (permalink / raw)
  To: Tao Tang, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

On 11/26/25 7:45 AM, Tao Tang wrote:
> Add a minimal PCI test device designed to exercise IOMMU translation
> (such as ARM SMMUv3) without requiring guest firmware or OS. The device
> provides MMIO registers to configure and trigger DMA operations with
> controllable attributes (security state, address space), enabling
> deterministic IOMMU testing.
> 
> Key features:
> - Bare-metal IOMMU testing via simple MMIO interface
> - Configurable DMA attributes for security states and address spaces
> - Write-then-read verification pattern with automatic result checking
> 
> The device performs a deterministic DMA test pattern: write a known
> value (0x88888888) to a configured IOVA, read it back, and verify data
> integrity. Results are reported through a dedicated result register,
> eliminating the need for complex interrupt handling or driver
> infrastructure in tests.
> 
> This is purely a test device and not intended for production use or
> machine realism. It complements existing test infrastructure like
> pci-testdev but focuses specifically on IOMMU translation path
> validation.
> 
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
>   docs/specs/index.rst            |   1 +
>   docs/specs/iommu-testdev.rst    | 109 +++++++++++++
>   hw/misc/Kconfig                 |   5 +
>   hw/misc/iommu-testdev.c         | 278 ++++++++++++++++++++++++++++++++
>   hw/misc/meson.build             |   1 +
>   hw/misc/trace-events            |  10 ++
>   include/hw/misc/iommu-testdev.h |  70 ++++++++
>   7 files changed, 474 insertions(+)
>   create mode 100644 docs/specs/iommu-testdev.rst
>   create mode 100644 hw/misc/iommu-testdev.c
>   create mode 100644 include/hw/misc/iommu-testdev.h
Overall, the test device looks sane and has a clear interface, that can 
be used for several iommu implementations, beyond Arm smmu.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev
  2025-11-26 15:45 ` [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev Tao Tang
@ 2025-12-04 18:42   ` Pierrick Bouvier
  2025-12-05 14:19     ` Tao Tang
  2025-12-10 18:45   ` Eric Auger
  1 sibling, 1 reply; 23+ messages in thread
From: Pierrick Bouvier @ 2025-12-04 18:42 UTC (permalink / raw)
  To: Tao Tang, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

On 11/26/25 7:45 AM, Tao Tang wrote:
> Add a qtest suite that validates ARM SMMUv3 translation without guest
> firmware or OS. The tests leverage iommu-testdev to trigger DMA
> operations and the qos-smmuv3 library to configure IOMMU translation
> structures.
> 
> This test suite targets the virt machine and covers:
> - Stage 1 only translation (VA -> PA via CD page tables)
> - Stage 2 only translation (IPA -> PA via STE S2 tables)
> - Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)
> - Design to extended to support multiple security spaces
>      (Non-Secure, Secure, Root, Realm)
> 
> Each test case follows this sequence:
> 1. Initialize SMMUv3 with appropriate command/event queues
> 2. Build translation tables (STE/CD/PTE) for the target scenario
> 3. Configure iommu-testdev with IOVA and DMA attributes via MMIO
> 4. Trigger DMA and validate successful translation
> 5. Verify data integrity through a deterministic write-read pattern
> 
> This bare-metal approach provides deterministic IOMMU testing with
> minimal dependencies, making failures directly attributable to the SMMU
> translation path.
> 
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
>   tests/qtest/iommu-smmuv3-test.c | 114 ++++++++++++++++++++++++++++++++
>   tests/qtest/meson.build         |   1 +
>   2 files changed, 115 insertions(+)
>   create mode 100644 tests/qtest/iommu-smmuv3-test.c
> 
> diff --git a/tests/qtest/iommu-smmuv3-test.c b/tests/qtest/iommu-smmuv3-test.c
> new file mode 100644
> index 0000000000..af438ecce0
> --- /dev/null
> +++ b/tests/qtest/iommu-smmuv3-test.c
> @@ -0,0 +1,114 @@
> +/*
> + * QTest for SMMUv3 with iommu-testdev
> + *
> + * This QTest file is used to test the SMMUv3 with iommu-testdev so that we can
> + * test SMMUv3 without any guest kernel or firmware.
> + *
> + * Copyright (c) 2025 Phytium Technology
> + *
> + * Author:
> + *  Tao Tang <tangtao1634@phytium.com.cn>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "libqtest.h"
> +#include "libqos/pci.h"
> +#include "libqos/generic-pcihost.h"
> +#include "hw/pci/pci_regs.h"
> +#include "hw/misc/iommu-testdev.h"
> +#include "libqos/qos-smmuv3.h"
> +
> +#define DMA_LEN           4
> +
> +/* Test configurations for different SMMU modes and spaces */
> +static const QSMMUTestConfig base_test_configs[] = {
> +    {
> +        .trans_mode = QSMMU_TM_S1_ONLY,
> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
> +        .dma_iova = QSMMU_IOVA_OR_IPA,
> +        .dma_len = DMA_LEN,
> +        .expected_result = 0
> +    },
> +    {
> +        .trans_mode = QSMMU_TM_S2_ONLY,
> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
> +        .dma_iova = QSMMU_IOVA_OR_IPA,
> +        .dma_len = DMA_LEN,
> +        .expected_result = 0
> +    },
> +    {
> +        .trans_mode = QSMMU_TM_NESTED,
> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
> +        .dma_iova = QSMMU_IOVA_OR_IPA,
> +        .dma_len = DMA_LEN,
> +        .expected_result = 0
> +    }
> +};
> +
> +static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QGenericPCIBus *gbus,
> +                                          QPCIBar *bar)
> +{
> +    uint16_t vid, did;
> +    QPCIDevice *dev = NULL;
> +
> +    qpci_init_generic(gbus, qts, NULL, false);
> +
> +    /* Find device by vendor/device ID to avoid slot surprises. */
> +    for (int s = 0; s < 32 && !dev; s++) {
> +        for (int fn = 0; fn < 8 && !dev; fn++) {
> +            QPCIDevice *cand = qpci_device_find(&gbus->bus, QPCI_DEVFN(s, fn));
> +            if (!cand) {
> +                continue;
> +            }
> +            vid = qpci_config_readw(cand, PCI_VENDOR_ID);
> +            did = qpci_config_readw(cand, PCI_DEVICE_ID);
> +            if (vid == IOMMU_TESTDEV_VENDOR_ID &&
> +                did == IOMMU_TESTDEV_DEVICE_ID) {
> +                dev = cand;
> +                g_test_message("Found iommu-testdev! devfn: 0x%x", cand->devfn);
> +            } else {
> +                g_free(cand);
> +            }
> +        }
> +    }
> +    g_assert(dev);
> +
> +    qpci_device_enable(dev);
> +    *bar = qpci_iomap(dev, 0, NULL);
> +    g_assert_false(bar->is_io);
> +
> +    return dev;
> +}
> +
> +static void test_smmuv3_translation(void)
> +{
> +    QTestState *qts;
> +    QGenericPCIBus gbus;
> +    QPCIDevice *dev;
> +    QPCIBar bar;
> +
> +    /* Initialize QEMU environment for SMMU testing */
> +    qts = qtest_init("-machine virt,acpi=off,gic-version=3,iommu=smmuv3 "
> +                     "-smp 1 -m 512 -cpu max -net none "
> +                     "-device iommu-testdev");
> +
> +    /* Setup and configure PCI device */
> +    dev = setup_qtest_pci_device(qts, &gbus, &bar);
> +    g_assert(dev);
> +
> +    /* Run the enhanced translation tests */
> +    g_test_message("### Starting SMMUv3 translation tests...###");
> +    qsmmu_translation_batch(base_test_configs, ARRAY_SIZE(base_test_configs),
> +                            qts, dev, bar, VIRT_SMMU_BASE);
> +    g_test_message("### SMMUv3 translation tests completed successfully! ###");
> +    qtest_quit(qts);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    g_test_init(&argc, &argv, NULL);
> +    qtest_add_func("/iommu-testdev/translation", test_smmuv3_translation);

Just a simple organization remark, maybe it would be better to have 
separate tests for each translation setup. It's easier to review in case 
a failure is found.
test_smmuv3_translation could be modified to add a QSMMUTestConfig 
parameter, and new entry points could be used to define the three setup.
What do you think?

> +    return g_test_run();
> +}
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index 669d07c06b..e2d2e68092 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -263,6 +263,7 @@ qtests_aarch64 = \
>      config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \
>     (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \
>     (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \
> +  (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-smmuv3-test'] : []) + \
>     qtests_cxl +                                                                                  \
>     ['arm-cpu-features',
>      'numa-test',

I ran this qtest, and checked with a coverage enabled build that it was 
triggering associated code in smmuv3 implementation.

Tested-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>

For a first version that's great. Later, we might want to generate 
faults as well, to see that SMMU is correctly reporting an error on 
incorrect transactions.

I don't mind having a complex qos-smmuv3.c with gory details, since we 
have a clear test here, that is easy to understand and modify.

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library
  2025-11-26 15:45 ` [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library Tao Tang
@ 2025-12-04 23:53   ` Pierrick Bouvier
  2025-12-05 15:03     ` Tao Tang
  2025-12-10 18:43     ` Eric Auger
  0 siblings, 2 replies; 23+ messages in thread
From: Pierrick Bouvier @ 2025-12-04 23:53 UTC (permalink / raw)
  To: Tao Tang, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

On 11/26/25 7:45 AM, Tao Tang wrote:
> Introduce qos-smmuv3, a reusable library for SMMUv3-related qtest
> operations. This module encapsulates common tasks like:
> 
> - SMMUv3 initialization (enabling, configuring command/event queues)
> - Stream Table Entry (STE) and Context Descriptor (CD) setup
> - Multi-level page table construction (L0-L3 for 4KB granules)
> - Support for Stage 1, Stage 2, and nested translation modes
> - Could be easily extended to support multi-space testing infrastructure
>      (Non-Secure, Secure, Root, Realm)
> 
> The library provides high-level abstractions that allow test code to
> focus on IOMMU behavior validation rather than low-level register
> manipulation and page table encoding. Key features include:
> 
> - Automatic memory allocation for translation structures with proper
>      alignment
> - Helper functions to build valid STEs/CDs for different translation
>      scenarios
> - Page table walkers that handle address offset calculations per
>      security space
> - Command queue management for SMMU configuration commands
> 
> This infrastructure is designed to be used by iommu-testdev-based tests
> and future SMMUv3 test suites, reducing code duplication and improving
> test maintainability.
> 
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
>   tests/qtest/libqos/meson.build  |   3 +
>   tests/qtest/libqos/qos-smmuv3.c | 731 ++++++++++++++++++++++++++++++++
>   tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
>   3 files changed, 1001 insertions(+)
>   create mode 100644 tests/qtest/libqos/qos-smmuv3.c
>   create mode 100644 tests/qtest/libqos/qos-smmuv3.h
> 

...

> +
> +void qsmmu_single_translation(QSMMUTestContext *ctx)
> +{
> +    uint32_t config_result;
> +    uint32_t dma_result;
> +    bool test_passed;
> +
> +    /* Configure SMMU translation */
> +    config_result = qsmmu_setup_and_enable_translation(ctx);
> +    if (config_result != 0) {
> +        g_test_message("Configuration failed: mode=%u status=0x%x",
> +                       ctx->config.trans_mode, config_result);
> +        return;

Is that expected to silently return if we can't configure translation?

> +    }
> +
> +    /* Trigger DMA operation */
> +    dma_result = qsmmu_trigger_dma(ctx);
> +    if (dma_result != 0) {
> +        g_test_message("DMA failed: mode=%u result=0x%x",
> +                       ctx->config.trans_mode, dma_result);
> +    } else {
> +        g_test_message("-> DMA succeeded: mode=%u", ctx->config.trans_mode);
> +    }
> +
> +    /* Validate test result */
> +    test_passed = qsmmu_validate_test_result(ctx);
> +    g_assert_true(test_passed);
> +
> +    /* Clean up translation state to prepare for the next test */
> +    qsmmu_cleanup_translation(ctx);
> +}
> +
> +void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t count,
> +                             QTestState *qts, QPCIDevice *dev,
> +                             QPCIBar bar, uint64_t smmu_base)
> +{
> +    for (int i = 0; i < count; i++) {
> +        /* Initialize test memory */
> +        qtest_memset(qts, configs[i].dma_iova, 0x00, configs[i].dma_len);
> +        /* Execute each test configuration */
> +        QSMMUTestContext ctx = {
> +            .qts = qts,
> +            .dev = dev,
> +            .bar = bar,
> +            .smmu_base = smmu_base,
> +            .config = configs[i],
> +            .trans_status = 0,
> +            .dma_result = 0,
> +            .sid = dev->devfn,
> +            .tx_space = qsmmu_sec_sid_to_space(configs[i].sec_sid),
> +        };
> +
> +        qsmmu_single_translation(&ctx);
> +        g_test_message("--> Test %d completed: mode=%u sec_sid=%u "
> +                       "status=0x%x result=0x%x", i, configs[i].trans_mode,
> +                       configs[i].sec_sid, ctx.trans_status, ctx.dma_result);
> +    }
> +}

What is the reason for batching operations?
We are not in a performance critical scenario for running this test, so 
it's probably better to have distinct calls to single_translation.

...

For the rest of the patch, which is quite consequent, congrats. It's 
hard to review all the setup phase here, but knowing it works with the 
current smmuv3 implementation, that's a good proof that it's working as 
expected.


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev
  2025-12-04 18:42   ` Pierrick Bouvier
@ 2025-12-05 14:19     ` Tao Tang
  2025-12-05 17:06       ` Pierrick Bouvier
  0 siblings, 1 reply; 23+ messages in thread
From: Tao Tang @ 2025-12-05 14:19 UTC (permalink / raw)
  To: Pierrick Bouvier, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

Hi Pierrick,

On 2025/12/5 02:42, Pierrick Bouvier wrote:
> On 11/26/25 7:45 AM, Tao Tang wrote:
>> Add a qtest suite that validates ARM SMMUv3 translation without guest
>> firmware or OS. The tests leverage iommu-testdev to trigger DMA
>> operations and the qos-smmuv3 library to configure IOMMU translation
>> structures.
>>
>> This test suite targets the virt machine and covers:
>> - Stage 1 only translation (VA -> PA via CD page tables)
>> - Stage 2 only translation (IPA -> PA via STE S2 tables)
>> - Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)
>> - Design to extended to support multiple security spaces
>>      (Non-Secure, Secure, Root, Realm)
>>
>> Each test case follows this sequence:
>> 1. Initialize SMMUv3 with appropriate command/event queues
>> 2. Build translation tables (STE/CD/PTE) for the target scenario
>> 3. Configure iommu-testdev with IOVA and DMA attributes via MMIO
>> 4. Trigger DMA and validate successful translation
>> 5. Verify data integrity through a deterministic write-read pattern
>>
>> This bare-metal approach provides deterministic IOMMU testing with
>> minimal dependencies, making failures directly attributable to the SMMU
>> translation path.
>>
>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>> ---
>>   tests/qtest/iommu-smmuv3-test.c | 114 ++++++++++++++++++++++++++++++++
>>   tests/qtest/meson.build         |   1 +
>>   2 files changed, 115 insertions(+)
>>   create mode 100644 tests/qtest/iommu-smmuv3-test.c
>>
>> diff --git a/tests/qtest/iommu-smmuv3-test.c 
>> b/tests/qtest/iommu-smmuv3-test.c
>> new file mode 100644
>> index 0000000000..af438ecce0
>> --- /dev/null
>> +++ b/tests/qtest/iommu-smmuv3-test.c
>> @@ -0,0 +1,114 @@
>> +/*
>> + * QTest for SMMUv3 with iommu-testdev
>> + *
>> + * This QTest file is used to test the SMMUv3 with iommu-testdev so 
>> that we can
>> + * test SMMUv3 without any guest kernel or firmware.
>> + *
>> + * Copyright (c) 2025 Phytium Technology
>> + *
>> + * Author:
>> + *  Tao Tang <tangtao1634@phytium.com.cn>
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "libqtest.h"
>> +#include "libqos/pci.h"
>> +#include "libqos/generic-pcihost.h"
>> +#include "hw/pci/pci_regs.h"
>> +#include "hw/misc/iommu-testdev.h"
>> +#include "libqos/qos-smmuv3.h"
>> +
>> +#define DMA_LEN           4
>> +
>> +/* Test configurations for different SMMU modes and spaces */
>> +static const QSMMUTestConfig base_test_configs[] = {
>> +    {
>> +        .trans_mode = QSMMU_TM_S1_ONLY,
>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
>> +        .dma_len = DMA_LEN,
>> +        .expected_result = 0
>> +    },
>> +    {
>> +        .trans_mode = QSMMU_TM_S2_ONLY,
>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
>> +        .dma_len = DMA_LEN,
>> +        .expected_result = 0
>> +    },
>> +    {
>> +        .trans_mode = QSMMU_TM_NESTED,
>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
>> +        .dma_len = DMA_LEN,
>> +        .expected_result = 0
>> +    }
>> +};
>> +
>> +static QPCIDevice *setup_qtest_pci_device(QTestState *qts, 
>> QGenericPCIBus *gbus,
>> +                                          QPCIBar *bar)
>> +{
>> +    uint16_t vid, did;
>> +    QPCIDevice *dev = NULL;
>> +
>> +    qpci_init_generic(gbus, qts, NULL, false);
>> +
>> +    /* Find device by vendor/device ID to avoid slot surprises. */
>> +    for (int s = 0; s < 32 && !dev; s++) {
>> +        for (int fn = 0; fn < 8 && !dev; fn++) {
>> +            QPCIDevice *cand = qpci_device_find(&gbus->bus, 
>> QPCI_DEVFN(s, fn));
>> +            if (!cand) {
>> +                continue;
>> +            }
>> +            vid = qpci_config_readw(cand, PCI_VENDOR_ID);
>> +            did = qpci_config_readw(cand, PCI_DEVICE_ID);
>> +            if (vid == IOMMU_TESTDEV_VENDOR_ID &&
>> +                did == IOMMU_TESTDEV_DEVICE_ID) {
>> +                dev = cand;
>> +                g_test_message("Found iommu-testdev! devfn: 0x%x", 
>> cand->devfn);
>> +            } else {
>> +                g_free(cand);
>> +            }
>> +        }
>> +    }
>> +    g_assert(dev);
>> +
>> +    qpci_device_enable(dev);
>> +    *bar = qpci_iomap(dev, 0, NULL);
>> +    g_assert_false(bar->is_io);
>> +
>> +    return dev;
>> +}
>> +
>> +static void test_smmuv3_translation(void)
>> +{
>> +    QTestState *qts;
>> +    QGenericPCIBus gbus;
>> +    QPCIDevice *dev;
>> +    QPCIBar bar;
>> +
>> +    /* Initialize QEMU environment for SMMU testing */
>> +    qts = qtest_init("-machine 
>> virt,acpi=off,gic-version=3,iommu=smmuv3 "
>> +                     "-smp 1 -m 512 -cpu max -net none "
>> +                     "-device iommu-testdev");
>> +
>> +    /* Setup and configure PCI device */
>> +    dev = setup_qtest_pci_device(qts, &gbus, &bar);
>> +    g_assert(dev);
>> +
>> +    /* Run the enhanced translation tests */
>> +    g_test_message("### Starting SMMUv3 translation tests...###");
>> +    qsmmu_translation_batch(base_test_configs, 
>> ARRAY_SIZE(base_test_configs),
>> +                            qts, dev, bar, VIRT_SMMU_BASE);
>> +    g_test_message("### SMMUv3 translation tests completed 
>> successfully! ###");
>> +    qtest_quit(qts);
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +    g_test_init(&argc, &argv, NULL);
>> +    qtest_add_func("/iommu-testdev/translation", 
>> test_smmuv3_translation);
>
> Just a simple organization remark, maybe it would be better to have 
> separate tests for each translation setup. It's easier to review in 
> case a failure is found.
> test_smmuv3_translation could be modified to add a QSMMUTestConfig 
> parameter, and new entry points could be used to define the three setup.
> What do you think?
>
>> +    return g_test_run();
>> +}
>> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
>> index 669d07c06b..e2d2e68092 100644
>> --- a/tests/qtest/meson.build
>> +++ b/tests/qtest/meson.build
>> @@ -263,6 +263,7 @@ qtests_aarch64 = \
>>      config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? 
>> ['tpm-tis-i2c-test'] : []) + \
>>     (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? 
>> qtests_aspeed64 : []) + \
>>     (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : 
>> []) + \
>> +  (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? 
>> ['iommu-smmuv3-test'] : []) + \
>>     qtests_cxl + \
>>     ['arm-cpu-features',
>>      'numa-test',
>
> I ran this qtest, and checked with a coverage enabled build that it 
> was triggering associated code in smmuv3 implementation.
>
> Tested-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>
> For a first version that's great. Later, we might want to generate 
> faults as well, to see that SMMU is correctly reporting an error on 
> incorrect transactions.
>
> I don't mind having a complex qos-smmuv3.c with gory details, since we 
> have a clear test here, that is easy to understand and modify.
>
> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>


Thanks a lot for the review, the test, and the suggestion.


To be honest, when I wrote the initial version I didn’t really think 
about splitting the tests per mode. I was instead worrying about how to 
correctly call `qsmmu_cleanup_translation` after each translation mode 
to flush all SMMU caches so that the next mode wouldn’t be affected by 
leftover state. Your suggestion of separate test functions actually 
makes this a lot cleaner: each test starts from a fresh QEMU/SMMU state, 
and I don’t need to overthink the cleanup between different modes.


I'll refactor test_smmuv3_translation code a bit, but leave everything 
else the same. The refactoring code will be like:


static void test_smmuv3_ns_s1_only(void)
{
     run_smmuv3_translation(&base_test_configs[0]);
}

static void test_smmuv3_ns_s2_only(void)
{
     run_smmuv3_translation(&base_test_configs[1]);
}

static void test_smmuv3_ns_nested(void)
{
     run_smmuv3_translation(&base_test_configs[2]);
}

int main(int argc, char **argv)
{
     g_test_init(&argc, &argv, NULL);
     qtest_add_func("/iommu-testdev/translation/ns-s1-only",
                    test_smmuv3_ns_s1_only);
     qtest_add_func("/iommu-testdev/translation/ns-s2-only",
                    test_smmuv3_ns_s2_only);
     qtest_add_func("/iommu-testdev/translation/ns-nested",
                    test_smmuv3_ns_nested);
     return g_test_run();
}


Thanks again for running the coverage build and for the hint about 
adding fault-oriented tests; I’ll look into extending qos-smmuv3 in that 
direction as a follow-up.

Best regards,
Tao



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library
  2025-12-04 23:53   ` Pierrick Bouvier
@ 2025-12-05 15:03     ` Tao Tang
  2025-12-05 17:19       ` Pierrick Bouvier
  2025-12-10 18:43     ` Eric Auger
  1 sibling, 1 reply; 23+ messages in thread
From: Tao Tang @ 2025-12-05 15:03 UTC (permalink / raw)
  To: Pierrick Bouvier, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

Hi Pierrick,

On 2025/12/5 07:53, Pierrick Bouvier wrote:
> On 11/26/25 7:45 AM, Tao Tang wrote:
>> Introduce qos-smmuv3, a reusable library for SMMUv3-related qtest
>> operations. This module encapsulates common tasks like:
>>
>> - SMMUv3 initialization (enabling, configuring command/event queues)
>> - Stream Table Entry (STE) and Context Descriptor (CD) setup
>> - Multi-level page table construction (L0-L3 for 4KB granules)
>> - Support for Stage 1, Stage 2, and nested translation modes
>> - Could be easily extended to support multi-space testing infrastructure
>>      (Non-Secure, Secure, Root, Realm)
>>
>> The library provides high-level abstractions that allow test code to
>> focus on IOMMU behavior validation rather than low-level register
>> manipulation and page table encoding. Key features include:
>>
>> - Automatic memory allocation for translation structures with proper
>>      alignment
>> - Helper functions to build valid STEs/CDs for different translation
>>      scenarios
>> - Page table walkers that handle address offset calculations per
>>      security space
>> - Command queue management for SMMU configuration commands
>>
>> This infrastructure is designed to be used by iommu-testdev-based tests
>> and future SMMUv3 test suites, reducing code duplication and improving
>> test maintainability.
>>
>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>> ---
>>   tests/qtest/libqos/meson.build  |   3 +
>>   tests/qtest/libqos/qos-smmuv3.c | 731 ++++++++++++++++++++++++++++++++
>>   tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
>>   3 files changed, 1001 insertions(+)
>>   create mode 100644 tests/qtest/libqos/qos-smmuv3.c
>>   create mode 100644 tests/qtest/libqos/qos-smmuv3.h
>>
>
> ...
>
>> +
>> +void qsmmu_single_translation(QSMMUTestContext *ctx)
>> +{
>> +    uint32_t config_result;
>> +    uint32_t dma_result;
>> +    bool test_passed;
>> +
>> +    /* Configure SMMU translation */
>> +    config_result = qsmmu_setup_and_enable_translation(ctx);
>> +    if (config_result != 0) {
>> +        g_test_message("Configuration failed: mode=%u status=0x%x",
>> +                       ctx->config.trans_mode, config_result);
>> +        return;
>
> Is that expected to silently return if we can't configure translation?


No, it is not intended to silently return on a failed configuration. 
Maybe an assertion is a better choice:


config_result = qsmmu_setup_and_enable_translation(ctx);

g_assert_cmpuint(config_result, ==, 0);

>
>> +    }
>> +
>> +    /* Trigger DMA operation */
>> +    dma_result = qsmmu_trigger_dma(ctx);
>> +    if (dma_result != 0) {
>> +        g_test_message("DMA failed: mode=%u result=0x%x",
>> +                       ctx->config.trans_mode, dma_result);
>> +    } else {
>> +        g_test_message("-> DMA succeeded: mode=%u", 
>> ctx->config.trans_mode);
>> +    }
>> +
>> +    /* Validate test result */
>> +    test_passed = qsmmu_validate_test_result(ctx);
>> +    g_assert_true(test_passed);
>> +
>> +    /* Clean up translation state to prepare for the next test */
>> +    qsmmu_cleanup_translation(ctx);
>> +}
>> +
>> +void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t 
>> count,
>> +                             QTestState *qts, QPCIDevice *dev,
>> +                             QPCIBar bar, uint64_t smmu_base)
>> +{
>> +    for (int i = 0; i < count; i++) {
>> +        /* Initialize test memory */
>> +        qtest_memset(qts, configs[i].dma_iova, 0x00, 
>> configs[i].dma_len);
>> +        /* Execute each test configuration */
>> +        QSMMUTestContext ctx = {
>> +            .qts = qts,
>> +            .dev = dev,
>> +            .bar = bar,
>> +            .smmu_base = smmu_base,
>> +            .config = configs[i],
>> +            .trans_status = 0,
>> +            .dma_result = 0,
>> +            .sid = dev->devfn,
>> +            .tx_space = qsmmu_sec_sid_to_space(configs[i].sec_sid),
>> +        };
>> +
>> +        qsmmu_single_translation(&ctx);
>> +        g_test_message("--> Test %d completed: mode=%u sec_sid=%u "
>> +                       "status=0x%x result=0x%x", i, 
>> configs[i].trans_mode,
>> +                       configs[i].sec_sid, ctx.trans_status, 
>> ctx.dma_result);
>> +    }
>> +}
>
> What is the reason for batching operations?
> We are not in a performance critical scenario for running this test, 
> so it's probably better to have distinct calls to single_translation.


As described in the previous thread [1] , I plan to split the tests so 
that each translation mode is exercised by its own qtest. With that 
split in place, there is no real need for a qsmmu_translation_batch() 
helper anymore, so I refactor it into a qsmmu_run_translation_case 
function and drop the inside for-loop.


[1] 
https://lore.kernel.org/qemu-devel/7370070a-c569-4b77-bd1e-6fc749ba9c90@phytium.com.cn/

>
> ...
>
> For the rest of the patch, which is quite consequent, congrats. It's 
> hard to review all the setup phase here, but knowing it works with the 
> current smmuv3 implementation, that's a good proof that it's working 
> as expected.


Yes, setting up all this infrastructure did take some time, especially 
getting the nested mode page tables right (and Secure state-related 
configuration which is still in my local repo).

I really appreciate that you ran the tests yourself and even checked 
with a coverage-enabled build to confirm that it exercises the smmuv3 
implementation. Thanks again for the thorough review.


Best regards,

Tao



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev
  2025-12-05 14:19     ` Tao Tang
@ 2025-12-05 17:06       ` Pierrick Bouvier
  2025-12-06  4:54         ` Tao Tang
  0 siblings, 1 reply; 23+ messages in thread
From: Pierrick Bouvier @ 2025-12-05 17:06 UTC (permalink / raw)
  To: Tao Tang, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

On 12/5/25 6:19 AM, Tao Tang wrote:
> Hi Pierrick,
> 
> On 2025/12/5 02:42, Pierrick Bouvier wrote:
>> On 11/26/25 7:45 AM, Tao Tang wrote:
>>> Add a qtest suite that validates ARM SMMUv3 translation without guest
>>> firmware or OS. The tests leverage iommu-testdev to trigger DMA
>>> operations and the qos-smmuv3 library to configure IOMMU translation
>>> structures.
>>>
>>> This test suite targets the virt machine and covers:
>>> - Stage 1 only translation (VA -> PA via CD page tables)
>>> - Stage 2 only translation (IPA -> PA via STE S2 tables)
>>> - Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)
>>> - Design to extended to support multiple security spaces
>>>       (Non-Secure, Secure, Root, Realm)
>>>
>>> Each test case follows this sequence:
>>> 1. Initialize SMMUv3 with appropriate command/event queues
>>> 2. Build translation tables (STE/CD/PTE) for the target scenario
>>> 3. Configure iommu-testdev with IOVA and DMA attributes via MMIO
>>> 4. Trigger DMA and validate successful translation
>>> 5. Verify data integrity through a deterministic write-read pattern
>>>
>>> This bare-metal approach provides deterministic IOMMU testing with
>>> minimal dependencies, making failures directly attributable to the SMMU
>>> translation path.
>>>
>>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>>> ---
>>>    tests/qtest/iommu-smmuv3-test.c | 114 ++++++++++++++++++++++++++++++++
>>>    tests/qtest/meson.build         |   1 +
>>>    2 files changed, 115 insertions(+)
>>>    create mode 100644 tests/qtest/iommu-smmuv3-test.c
>>>
>>> diff --git a/tests/qtest/iommu-smmuv3-test.c
>>> b/tests/qtest/iommu-smmuv3-test.c
>>> new file mode 100644
>>> index 0000000000..af438ecce0
>>> --- /dev/null
>>> +++ b/tests/qtest/iommu-smmuv3-test.c
>>> @@ -0,0 +1,114 @@
>>> +/*
>>> + * QTest for SMMUv3 with iommu-testdev
>>> + *
>>> + * This QTest file is used to test the SMMUv3 with iommu-testdev so
>>> that we can
>>> + * test SMMUv3 without any guest kernel or firmware.
>>> + *
>>> + * Copyright (c) 2025 Phytium Technology
>>> + *
>>> + * Author:
>>> + *  Tao Tang <tangtao1634@phytium.com.cn>
>>> + *
>>> + * SPDX-License-Identifier: GPL-2.0-or-later
>>> + */
>>> +
>>> +#include "qemu/osdep.h"
>>> +#include "libqtest.h"
>>> +#include "libqos/pci.h"
>>> +#include "libqos/generic-pcihost.h"
>>> +#include "hw/pci/pci_regs.h"
>>> +#include "hw/misc/iommu-testdev.h"
>>> +#include "libqos/qos-smmuv3.h"
>>> +
>>> +#define DMA_LEN           4
>>> +
>>> +/* Test configurations for different SMMU modes and spaces */
>>> +static const QSMMUTestConfig base_test_configs[] = {
>>> +    {
>>> +        .trans_mode = QSMMU_TM_S1_ONLY,
>>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
>>> +        .dma_len = DMA_LEN,
>>> +        .expected_result = 0
>>> +    },
>>> +    {
>>> +        .trans_mode = QSMMU_TM_S2_ONLY,
>>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
>>> +        .dma_len = DMA_LEN,
>>> +        .expected_result = 0
>>> +    },
>>> +    {
>>> +        .trans_mode = QSMMU_TM_NESTED,
>>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
>>> +        .dma_len = DMA_LEN,
>>> +        .expected_result = 0
>>> +    }
>>> +};
>>> +
>>> +static QPCIDevice *setup_qtest_pci_device(QTestState *qts,
>>> QGenericPCIBus *gbus,
>>> +                                          QPCIBar *bar)
>>> +{
>>> +    uint16_t vid, did;
>>> +    QPCIDevice *dev = NULL;
>>> +
>>> +    qpci_init_generic(gbus, qts, NULL, false);
>>> +
>>> +    /* Find device by vendor/device ID to avoid slot surprises. */
>>> +    for (int s = 0; s < 32 && !dev; s++) {
>>> +        for (int fn = 0; fn < 8 && !dev; fn++) {
>>> +            QPCIDevice *cand = qpci_device_find(&gbus->bus,
>>> QPCI_DEVFN(s, fn));
>>> +            if (!cand) {
>>> +                continue;
>>> +            }
>>> +            vid = qpci_config_readw(cand, PCI_VENDOR_ID);
>>> +            did = qpci_config_readw(cand, PCI_DEVICE_ID);
>>> +            if (vid == IOMMU_TESTDEV_VENDOR_ID &&
>>> +                did == IOMMU_TESTDEV_DEVICE_ID) {
>>> +                dev = cand;
>>> +                g_test_message("Found iommu-testdev! devfn: 0x%x",
>>> cand->devfn);
>>> +            } else {
>>> +                g_free(cand);
>>> +            }
>>> +        }
>>> +    }
>>> +    g_assert(dev);
>>> +
>>> +    qpci_device_enable(dev);
>>> +    *bar = qpci_iomap(dev, 0, NULL);
>>> +    g_assert_false(bar->is_io);
>>> +
>>> +    return dev;
>>> +}
>>> +
>>> +static void test_smmuv3_translation(void)
>>> +{
>>> +    QTestState *qts;
>>> +    QGenericPCIBus gbus;
>>> +    QPCIDevice *dev;
>>> +    QPCIBar bar;
>>> +
>>> +    /* Initialize QEMU environment for SMMU testing */
>>> +    qts = qtest_init("-machine
>>> virt,acpi=off,gic-version=3,iommu=smmuv3 "
>>> +                     "-smp 1 -m 512 -cpu max -net none "
>>> +                     "-device iommu-testdev");
>>> +
>>> +    /* Setup and configure PCI device */
>>> +    dev = setup_qtest_pci_device(qts, &gbus, &bar);
>>> +    g_assert(dev);
>>> +
>>> +    /* Run the enhanced translation tests */
>>> +    g_test_message("### Starting SMMUv3 translation tests...###");
>>> +    qsmmu_translation_batch(base_test_configs,
>>> ARRAY_SIZE(base_test_configs),
>>> +                            qts, dev, bar, VIRT_SMMU_BASE);
>>> +    g_test_message("### SMMUv3 translation tests completed
>>> successfully! ###");
>>> +    qtest_quit(qts);
>>> +}
>>> +
>>> +int main(int argc, char **argv)
>>> +{
>>> +    g_test_init(&argc, &argv, NULL);
>>> +    qtest_add_func("/iommu-testdev/translation",
>>> test_smmuv3_translation);
>>
>> Just a simple organization remark, maybe it would be better to have
>> separate tests for each translation setup. It's easier to review in
>> case a failure is found.
>> test_smmuv3_translation could be modified to add a QSMMUTestConfig
>> parameter, and new entry points could be used to define the three setup.
>> What do you think?
>>
>>> +    return g_test_run();
>>> +}
>>> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
>>> index 669d07c06b..e2d2e68092 100644
>>> --- a/tests/qtest/meson.build
>>> +++ b/tests/qtest/meson.build
>>> @@ -263,6 +263,7 @@ qtests_aarch64 = \
>>>       config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ?
>>> ['tpm-tis-i2c-test'] : []) + \
>>>      (config_all_devices.has_key('CONFIG_ASPEED_SOC') ?
>>> qtests_aspeed64 : []) + \
>>>      (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx :
>>> []) + \
>>> +  (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ?
>>> ['iommu-smmuv3-test'] : []) + \
>>>      qtests_cxl + \
>>>      ['arm-cpu-features',
>>>       'numa-test',
>>
>> I ran this qtest, and checked with a coverage enabled build that it
>> was triggering associated code in smmuv3 implementation.
>>
>> Tested-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>
>> For a first version that's great. Later, we might want to generate
>> faults as well, to see that SMMU is correctly reporting an error on
>> incorrect transactions.
>>
>> I don't mind having a complex qos-smmuv3.c with gory details, since we
>> have a clear test here, that is easy to understand and modify.
>>
>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
> 
> 
> Thanks a lot for the review, the test, and the suggestion.
> 
> 
> To be honest, when I wrote the initial version I didn’t really think
> about splitting the tests per mode. I was instead worrying about how to
> correctly call `qsmmu_cleanup_translation` after each translation mode
> to flush all SMMU caches so that the next mode wouldn’t be affected by
> leftover state. Your suggestion of separate test functions actually
> makes this a lot cleaner: each test starts from a fresh QEMU/SMMU state,
> and I don’t need to overthink the cleanup between different modes.
>

I agree with you. The last thing we want is to debug a transient state 
that was not correctly reset. As well, we can remove all the cleanup code.

> 
> I'll refactor test_smmuv3_translation code a bit, but leave everything
> else the same. The refactoring code will be like:
> 
> 
> static void test_smmuv3_ns_s1_only(void)
> {
>       run_smmuv3_translation(&base_test_configs[0]);
> }
> 
> static void test_smmuv3_ns_s2_only(void)
> {
>       run_smmuv3_translation(&base_test_configs[1]);
> }
> 
> static void test_smmuv3_ns_nested(void)
> {
>       run_smmuv3_translation(&base_test_configs[2]);
> }

At this point, you can probably put the config directly in each 
function, having a global array (base_test_configs) does not bring any 
specific value.

> 
> int main(int argc, char **argv)
> {
>       g_test_init(&argc, &argv, NULL);
>       qtest_add_func("/iommu-testdev/translation/ns-s1-only",
>                      test_smmuv3_ns_s1_only);
>       qtest_add_func("/iommu-testdev/translation/ns-s2-only",
>                      test_smmuv3_ns_s2_only);
>       qtest_add_func("/iommu-testdev/translation/ns-nested",
>                      test_smmuv3_ns_nested);
>       return g_test_run();
> }
> 

Looks great like this.

> 
> Thanks again for running the coverage build and for the hint about
> adding fault-oriented tests; I’ll look into extending qos-smmuv3 in that
> direction as a follow-up.
>

You're welcome. To be honest, that's the only way to prove we correctly 
exercise the code. As the setup is quite complicated and needs specific 
SMMU magic, observe coverage is the simplest way to make sure it 
exercise what we want.
As well, coverage can be used in the future to check which part of the 
smmu implementation is not covered by unit tests, so it will be easy to 
enhance them.

Overall, the direction you took with this is good and it will be a 
useful addition for SMMU related work.

> Best regards,
> Tao
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library
  2025-12-05 15:03     ` Tao Tang
@ 2025-12-05 17:19       ` Pierrick Bouvier
  2025-12-06  5:27         ` Tao Tang
  0 siblings, 1 reply; 23+ messages in thread
From: Pierrick Bouvier @ 2025-12-05 17:19 UTC (permalink / raw)
  To: Tao Tang, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

On 12/5/25 7:03 AM, Tao Tang wrote:
> Hi Pierrick,
> 
> On 2025/12/5 07:53, Pierrick Bouvier wrote:
>> On 11/26/25 7:45 AM, Tao Tang wrote:
>>> Introduce qos-smmuv3, a reusable library for SMMUv3-related qtest
>>> operations. This module encapsulates common tasks like:
>>>
>>> - SMMUv3 initialization (enabling, configuring command/event queues)
>>> - Stream Table Entry (STE) and Context Descriptor (CD) setup
>>> - Multi-level page table construction (L0-L3 for 4KB granules)
>>> - Support for Stage 1, Stage 2, and nested translation modes
>>> - Could be easily extended to support multi-space testing infrastructure
>>>       (Non-Secure, Secure, Root, Realm)
>>>
>>> The library provides high-level abstractions that allow test code to
>>> focus on IOMMU behavior validation rather than low-level register
>>> manipulation and page table encoding. Key features include:
>>>
>>> - Automatic memory allocation for translation structures with proper
>>>       alignment
>>> - Helper functions to build valid STEs/CDs for different translation
>>>       scenarios
>>> - Page table walkers that handle address offset calculations per
>>>       security space
>>> - Command queue management for SMMU configuration commands
>>>
>>> This infrastructure is designed to be used by iommu-testdev-based tests
>>> and future SMMUv3 test suites, reducing code duplication and improving
>>> test maintainability.
>>>
>>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>>> ---
>>>    tests/qtest/libqos/meson.build  |   3 +
>>>    tests/qtest/libqos/qos-smmuv3.c | 731 ++++++++++++++++++++++++++++++++
>>>    tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
>>>    3 files changed, 1001 insertions(+)
>>>    create mode 100644 tests/qtest/libqos/qos-smmuv3.c
>>>    create mode 100644 tests/qtest/libqos/qos-smmuv3.h
>>>
>>
>> ...
>>
>>> +
>>> +void qsmmu_single_translation(QSMMUTestContext *ctx)
>>> +{
>>> +    uint32_t config_result;
>>> +    uint32_t dma_result;
>>> +    bool test_passed;
>>> +
>>> +    /* Configure SMMU translation */
>>> +    config_result = qsmmu_setup_and_enable_translation(ctx);
>>> +    if (config_result != 0) {
>>> +        g_test_message("Configuration failed: mode=%u status=0x%x",
>>> +                       ctx->config.trans_mode, config_result);
>>> +        return;
>>
>> Is that expected to silently return if we can't configure translation?
> 
> 
> No, it is not intended to silently return on a failed configuration.
> Maybe an assertion is a better choice:
> 
> 
> config_result = qsmmu_setup_and_enable_translation(ctx);
> 
> g_assert_cmpuint(config_result, ==, 0);
> 

Looks good. We should rely on exit code first, and then on verbose log 
to find what is the problem.

>>
>>> +    }
>>> +
>>> +    /* Trigger DMA operation */
>>> +    dma_result = qsmmu_trigger_dma(ctx);
>>> +    if (dma_result != 0) {
>>> +        g_test_message("DMA failed: mode=%u result=0x%x",
>>> +                       ctx->config.trans_mode, dma_result);
>>> +    } else {
>>> +        g_test_message("-> DMA succeeded: mode=%u",
>>> ctx->config.trans_mode);
>>> +    }
>>> +
>>> +    /* Validate test result */
>>> +    test_passed = qsmmu_validate_test_result(ctx);
>>> +    g_assert_true(test_passed);
>>> +
>>> +    /* Clean up translation state to prepare for the next test */
>>> +    qsmmu_cleanup_translation(ctx);
>>> +}
>>> +
>>> +void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t
>>> count,
>>> +                             QTestState *qts, QPCIDevice *dev,
>>> +                             QPCIBar bar, uint64_t smmu_base)
>>> +{
>>> +    for (int i = 0; i < count; i++) {
>>> +        /* Initialize test memory */
>>> +        qtest_memset(qts, configs[i].dma_iova, 0x00,
>>> configs[i].dma_len);
>>> +        /* Execute each test configuration */
>>> +        QSMMUTestContext ctx = {
>>> +            .qts = qts,
>>> +            .dev = dev,
>>> +            .bar = bar,
>>> +            .smmu_base = smmu_base,
>>> +            .config = configs[i],
>>> +            .trans_status = 0,
>>> +            .dma_result = 0,
>>> +            .sid = dev->devfn,
>>> +            .tx_space = qsmmu_sec_sid_to_space(configs[i].sec_sid),
>>> +        };
>>> +
>>> +        qsmmu_single_translation(&ctx);
>>> +        g_test_message("--> Test %d completed: mode=%u sec_sid=%u "
>>> +                       "status=0x%x result=0x%x", i,
>>> configs[i].trans_mode,
>>> +                       configs[i].sec_sid, ctx.trans_status,
>>> ctx.dma_result);
>>> +    }
>>> +}
>>
>> What is the reason for batching operations?
>> We are not in a performance critical scenario for running this test,
>> so it's probably better to have distinct calls to single_translation.
> 
> 
> As described in the previous thread [1] , I plan to split the tests so
> that each translation mode is exercised by its own qtest. With that
> split in place, there is no real need for a qsmmu_translation_batch()
> helper anymore, so I refactor it into a qsmmu_run_translation_case
> function and drop the inside for-loop.
>

All good, indeed removes the need for translation_batch.

> 
> [1]
> https://lore.kernel.org/qemu-devel/7370070a-c569-4b77-bd1e-6fc749ba9c90@phytium.com.cn/
> 
>>
>> ...
>>
>> For the rest of the patch, which is quite consequent, congrats. It's
>> hard to review all the setup phase here, but knowing it works with the
>> current smmuv3 implementation, that's a good proof that it's working
>> as expected.
> 
> 
> Yes, setting up all this infrastructure did take some time, especially
> getting the nested mode page tables right (and Secure state-related
> configuration which is still in my local repo).
>

Feel free to start with the current version, and then you'll add secure 
state related changes as part of your other series.

> I really appreciate that you ran the tests yourself and even checked
> with a coverage-enabled build to confirm that it exercises the smmuv3
> implementation. Thanks again for the thorough review.
>

In case someone else wants to reproduce:
$ export CFLAGS="--coverage"
$ ./configure --target-list=aarch64-softmmu
$ ninja -C build
$ QTEST_QEMU_BINARY=./build/qemu-system-aarch64 \
   ./build/tests/qtest/iommu-smmuv3-test
$ rm -rf build/coverage_html
$ mkdir build/coverage_html
$ gcovr \
       --gcov-ignore-parse-errors suspicious_hits.warn \
       --gcov-ignore-parse-errors negative_hits.warn \
       --merge-mode-functions=separate \
       --html-details build/coverage_html/index.html \
       --filter 'hw/arm/smmu*'
$ echo file://$(pwd)/build/coverage_html/index.html
# open this in browser by clicking on your terminal

If useful for you, you can attach those instructions in your next cover 
letter, so people can easily reproduce.

> 
> Best regards,
> 
> Tao
> 



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev
  2025-12-05 17:06       ` Pierrick Bouvier
@ 2025-12-06  4:54         ` Tao Tang
  0 siblings, 0 replies; 23+ messages in thread
From: Tao Tang @ 2025-12-06  4:54 UTC (permalink / raw)
  To: Pierrick Bouvier, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF


On 2025/12/6 01:06, Pierrick Bouvier wrote:
> On 12/5/25 6:19 AM, Tao Tang wrote:
>> Hi Pierrick,
>>
>> On 2025/12/5 02:42, Pierrick Bouvier wrote:
>>> On 11/26/25 7:45 AM, Tao Tang wrote:
>>>> Add a qtest suite that validates ARM SMMUv3 translation without guest
>>>> firmware or OS. The tests leverage iommu-testdev to trigger DMA
>>>> operations and the qos-smmuv3 library to configure IOMMU translation
>>>> structures.
>>>>
>>>> This test suite targets the virt machine and covers:
>>>> - Stage 1 only translation (VA -> PA via CD page tables)
>>>> - Stage 2 only translation (IPA -> PA via STE S2 tables)
>>>> - Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)
>>>> - Design to extended to support multiple security spaces
>>>>       (Non-Secure, Secure, Root, Realm)
>>>>
>>>> Each test case follows this sequence:
>>>> 1. Initialize SMMUv3 with appropriate command/event queues
>>>> 2. Build translation tables (STE/CD/PTE) for the target scenario
>>>> 3. Configure iommu-testdev with IOVA and DMA attributes via MMIO
>>>> 4. Trigger DMA and validate successful translation
>>>> 5. Verify data integrity through a deterministic write-read pattern
>>>>
>>>> This bare-metal approach provides deterministic IOMMU testing with
>>>> minimal dependencies, making failures directly attributable to the 
>>>> SMMU
>>>> translation path.
>>>>
>>>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>>>> ---
>>>>    tests/qtest/iommu-smmuv3-test.c | 114 
>>>> ++++++++++++++++++++++++++++++++
>>>>    tests/qtest/meson.build         |   1 +
>>>>    2 files changed, 115 insertions(+)
>>>>    create mode 100644 tests/qtest/iommu-smmuv3-test.c
>>>>
>>>> diff --git a/tests/qtest/iommu-smmuv3-test.c
>>>> b/tests/qtest/iommu-smmuv3-test.c
>>>> new file mode 100644
>>>> index 0000000000..af438ecce0
>>>> --- /dev/null
>>>> +++ b/tests/qtest/iommu-smmuv3-test.c
>>>> @@ -0,0 +1,114 @@
>>>> +/*
>>>> + * QTest for SMMUv3 with iommu-testdev
>>>> + *
>>>> + * This QTest file is used to test the SMMUv3 with iommu-testdev so
>>>> that we can
>>>> + * test SMMUv3 without any guest kernel or firmware.
>>>> + *
>>>> + * Copyright (c) 2025 Phytium Technology
>>>> + *
>>>> + * Author:
>>>> + *  Tao Tang <tangtao1634@phytium.com.cn>
>>>> + *
>>>> + * SPDX-License-Identifier: GPL-2.0-or-later
>>>> + */
>>>> +
>>>> +#include "qemu/osdep.h"
>>>> +#include "libqtest.h"
>>>> +#include "libqos/pci.h"
>>>> +#include "libqos/generic-pcihost.h"
>>>> +#include "hw/pci/pci_regs.h"
>>>> +#include "hw/misc/iommu-testdev.h"
>>>> +#include "libqos/qos-smmuv3.h"
>>>> +
>>>> +#define DMA_LEN           4
>>>> +
>>>> +/* Test configurations for different SMMU modes and spaces */
>>>> +static const QSMMUTestConfig base_test_configs[] = {
>>>> +    {
>>>> +        .trans_mode = QSMMU_TM_S1_ONLY,
>>>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>>>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
>>>> +        .dma_len = DMA_LEN,
>>>> +        .expected_result = 0
>>>> +    },
>>>> +    {
>>>> +        .trans_mode = QSMMU_TM_S2_ONLY,
>>>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>>>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
>>>> +        .dma_len = DMA_LEN,
>>>> +        .expected_result = 0
>>>> +    },
>>>> +    {
>>>> +        .trans_mode = QSMMU_TM_NESTED,
>>>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>>>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
>>>> +        .dma_len = DMA_LEN,
>>>> +        .expected_result = 0
>>>> +    }
>>>> +};
>>>> +
>>>> +static QPCIDevice *setup_qtest_pci_device(QTestState *qts,
>>>> QGenericPCIBus *gbus,
>>>> +                                          QPCIBar *bar)
>>>> +{
>>>> +    uint16_t vid, did;
>>>> +    QPCIDevice *dev = NULL;
>>>> +
>>>> +    qpci_init_generic(gbus, qts, NULL, false);
>>>> +
>>>> +    /* Find device by vendor/device ID to avoid slot surprises. */
>>>> +    for (int s = 0; s < 32 && !dev; s++) {
>>>> +        for (int fn = 0; fn < 8 && !dev; fn++) {
>>>> +            QPCIDevice *cand = qpci_device_find(&gbus->bus,
>>>> QPCI_DEVFN(s, fn));
>>>> +            if (!cand) {
>>>> +                continue;
>>>> +            }
>>>> +            vid = qpci_config_readw(cand, PCI_VENDOR_ID);
>>>> +            did = qpci_config_readw(cand, PCI_DEVICE_ID);
>>>> +            if (vid == IOMMU_TESTDEV_VENDOR_ID &&
>>>> +                did == IOMMU_TESTDEV_DEVICE_ID) {
>>>> +                dev = cand;
>>>> +                g_test_message("Found iommu-testdev! devfn: 0x%x",
>>>> cand->devfn);
>>>> +            } else {
>>>> +                g_free(cand);
>>>> +            }
>>>> +        }
>>>> +    }
>>>> +    g_assert(dev);
>>>> +
>>>> +    qpci_device_enable(dev);
>>>> +    *bar = qpci_iomap(dev, 0, NULL);
>>>> +    g_assert_false(bar->is_io);
>>>> +
>>>> +    return dev;
>>>> +}
>>>> +
>>>> +static void test_smmuv3_translation(void)
>>>> +{
>>>> +    QTestState *qts;
>>>> +    QGenericPCIBus gbus;
>>>> +    QPCIDevice *dev;
>>>> +    QPCIBar bar;
>>>> +
>>>> +    /* Initialize QEMU environment for SMMU testing */
>>>> +    qts = qtest_init("-machine
>>>> virt,acpi=off,gic-version=3,iommu=smmuv3 "
>>>> +                     "-smp 1 -m 512 -cpu max -net none "
>>>> +                     "-device iommu-testdev");
>>>> +
>>>> +    /* Setup and configure PCI device */
>>>> +    dev = setup_qtest_pci_device(qts, &gbus, &bar);
>>>> +    g_assert(dev);
>>>> +
>>>> +    /* Run the enhanced translation tests */
>>>> +    g_test_message("### Starting SMMUv3 translation tests...###");
>>>> +    qsmmu_translation_batch(base_test_configs,
>>>> ARRAY_SIZE(base_test_configs),
>>>> +                            qts, dev, bar, VIRT_SMMU_BASE);
>>>> +    g_test_message("### SMMUv3 translation tests completed
>>>> successfully! ###");
>>>> +    qtest_quit(qts);
>>>> +}
>>>> +
>>>> +int main(int argc, char **argv)
>>>> +{
>>>> +    g_test_init(&argc, &argv, NULL);
>>>> +    qtest_add_func("/iommu-testdev/translation",
>>>> test_smmuv3_translation);
>>>
>>> Just a simple organization remark, maybe it would be better to have
>>> separate tests for each translation setup. It's easier to review in
>>> case a failure is found.
>>> test_smmuv3_translation could be modified to add a QSMMUTestConfig
>>> parameter, and new entry points could be used to define the three 
>>> setup.
>>> What do you think?
>>>
>>>> +    return g_test_run();
>>>> +}
>>>> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
>>>> index 669d07c06b..e2d2e68092 100644
>>>> --- a/tests/qtest/meson.build
>>>> +++ b/tests/qtest/meson.build
>>>> @@ -263,6 +263,7 @@ qtests_aarch64 = \
>>>>       config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ?
>>>> ['tpm-tis-i2c-test'] : []) + \
>>>>      (config_all_devices.has_key('CONFIG_ASPEED_SOC') ?
>>>> qtests_aspeed64 : []) + \
>>>>      (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx :
>>>> []) + \
>>>> +  (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ?
>>>> ['iommu-smmuv3-test'] : []) + \
>>>>      qtests_cxl + \
>>>>      ['arm-cpu-features',
>>>>       'numa-test',
>>>
>>> I ran this qtest, and checked with a coverage enabled build that it
>>> was triggering associated code in smmuv3 implementation.
>>>
>>> Tested-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>>
>>> For a first version that's great. Later, we might want to generate
>>> faults as well, to see that SMMU is correctly reporting an error on
>>> incorrect transactions.
>>>
>>> I don't mind having a complex qos-smmuv3.c with gory details, since we
>>> have a clear test here, that is easy to understand and modify.
>>>
>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>
>>
>> Thanks a lot for the review, the test, and the suggestion.
>>
>>
>> To be honest, when I wrote the initial version I didn’t really think
>> about splitting the tests per mode. I was instead worrying about how to
>> correctly call `qsmmu_cleanup_translation` after each translation mode
>> to flush all SMMU caches so that the next mode wouldn’t be affected by
>> leftover state. Your suggestion of separate test functions actually
>> makes this a lot cleaner: each test starts from a fresh QEMU/SMMU state,
>> and I don’t need to overthink the cleanup between different modes.
>>
>
> I agree with you. The last thing we want is to debug a transient state 
> that was not correctly reset. As well, we can remove all the cleanup 
> code.
>
>>
>> I'll refactor test_smmuv3_translation code a bit, but leave everything
>> else the same. The refactoring code will be like:
>>
>>
>> static void test_smmuv3_ns_s1_only(void)
>> {
>>       run_smmuv3_translation(&base_test_configs[0]);
>> }
>>
>> static void test_smmuv3_ns_s2_only(void)
>> {
>>       run_smmuv3_translation(&base_test_configs[1]);
>> }
>>
>> static void test_smmuv3_ns_nested(void)
>> {
>>       run_smmuv3_translation(&base_test_configs[2]);
>> }
>
> At this point, you can probably put the config directly in each 
> function, having a global array (base_test_configs) does not bring any 
> specific value.
>
>>
>> int main(int argc, char **argv)
>> {
>>       g_test_init(&argc, &argv, NULL);
>>       qtest_add_func("/iommu-testdev/translation/ns-s1-only",
>>                      test_smmuv3_ns_s1_only);
>>       qtest_add_func("/iommu-testdev/translation/ns-s2-only",
>>                      test_smmuv3_ns_s2_only);
>>       qtest_add_func("/iommu-testdev/translation/ns-nested",
>>                      test_smmuv3_ns_nested);
>>       return g_test_run();
>> }
>>
>
> Looks great like this.
>
>>
>> Thanks again for running the coverage build and for the hint about
>> adding fault-oriented tests; I’ll look into extending qos-smmuv3 in that
>> direction as a follow-up.
>>
>
> You're welcome. To be honest, that's the only way to prove we 
> correctly exercise the code. As the setup is quite complicated and 
> needs specific SMMU magic, observe coverage is the simplest way to 
> make sure it exercise what we want.
> As well, coverage can be used in the future to check which part of the 
> smmu implementation is not covered by unit tests, so it will be easy 
> to enhance them.
>
> Overall, the direction you took with this is good and it will be a 
> useful addition for SMMU related work. 


Hi Pierrick,


Good point about the configs. I’ll drop the global `base_test_configs[]` 
array and the leftover cleanup code, as you suggested.

Thanks again for the guidance and for confirming the overall direction.

Best regards,
Tao



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library
  2025-12-05 17:19       ` Pierrick Bouvier
@ 2025-12-06  5:27         ` Tao Tang
  2025-12-10 18:40           ` Eric Auger
  0 siblings, 1 reply; 23+ messages in thread
From: Tao Tang @ 2025-12-06  5:27 UTC (permalink / raw)
  To: Pierrick Bouvier, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Eric Auger, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF


On 2025/12/6 01:19, Pierrick Bouvier wrote:
> On 12/5/25 7:03 AM, Tao Tang wrote:
>> Hi Pierrick,
>>
>> On 2025/12/5 07:53, Pierrick Bouvier wrote:
>>> On 11/26/25 7:45 AM, Tao Tang wrote:
>>>> Introduce qos-smmuv3, a reusable library for SMMUv3-related qtest
>>>> operations. This module encapsulates common tasks like:
>>>>
>>>> - SMMUv3 initialization (enabling, configuring command/event queues)
>>>> - Stream Table Entry (STE) and Context Descriptor (CD) setup
>>>> - Multi-level page table construction (L0-L3 for 4KB granules)
>>>> - Support for Stage 1, Stage 2, and nested translation modes
>>>> - Could be easily extended to support multi-space testing 
>>>> infrastructure
>>>>       (Non-Secure, Secure, Root, Realm)
>>>>
>>>> The library provides high-level abstractions that allow test code to
>>>> focus on IOMMU behavior validation rather than low-level register
>>>> manipulation and page table encoding. Key features include:
>>>>
>>>> - Automatic memory allocation for translation structures with proper
>>>>       alignment
>>>> - Helper functions to build valid STEs/CDs for different translation
>>>>       scenarios
>>>> - Page table walkers that handle address offset calculations per
>>>>       security space
>>>> - Command queue management for SMMU configuration commands
>>>>
>>>> This infrastructure is designed to be used by iommu-testdev-based 
>>>> tests
>>>> and future SMMUv3 test suites, reducing code duplication and improving
>>>> test maintainability.
>>>>
>>>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>>>> ---
>>>>    tests/qtest/libqos/meson.build  |   3 +
>>>>    tests/qtest/libqos/qos-smmuv3.c | 731 
>>>> ++++++++++++++++++++++++++++++++
>>>>    tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
>>>>    3 files changed, 1001 insertions(+)
>>>>    create mode 100644 tests/qtest/libqos/qos-smmuv3.c
>>>>    create mode 100644 tests/qtest/libqos/qos-smmuv3.h
>>>>
>>>
>>> ...
>>>
>>>> +
>>>> +void qsmmu_single_translation(QSMMUTestContext *ctx)
>>>> +{
>>>> +    uint32_t config_result;
>>>> +    uint32_t dma_result;
>>>> +    bool test_passed;
>>>> +
>>>> +    /* Configure SMMU translation */
>>>> +    config_result = qsmmu_setup_and_enable_translation(ctx);
>>>> +    if (config_result != 0) {
>>>> +        g_test_message("Configuration failed: mode=%u status=0x%x",
>>>> +                       ctx->config.trans_mode, config_result);
>>>> +        return;
>>>
>>> Is that expected to silently return if we can't configure translation?
>>
>>
>> No, it is not intended to silently return on a failed configuration.
>> Maybe an assertion is a better choice:
>>
>>
>> config_result = qsmmu_setup_and_enable_translation(ctx);
>>
>> g_assert_cmpuint(config_result, ==, 0);
>>
>
> Looks good. We should rely on exit code first, and then on verbose log 
> to find what is the problem.
>
>>>
>>>> +    }
>>>> +
>>>> +    /* Trigger DMA operation */
>>>> +    dma_result = qsmmu_trigger_dma(ctx);
>>>> +    if (dma_result != 0) {
>>>> +        g_test_message("DMA failed: mode=%u result=0x%x",
>>>> +                       ctx->config.trans_mode, dma_result);
>>>> +    } else {
>>>> +        g_test_message("-> DMA succeeded: mode=%u",
>>>> ctx->config.trans_mode);
>>>> +    }
>>>> +
>>>> +    /* Validate test result */
>>>> +    test_passed = qsmmu_validate_test_result(ctx);
>>>> +    g_assert_true(test_passed);
>>>> +
>>>> +    /* Clean up translation state to prepare for the next test */
>>>> +    qsmmu_cleanup_translation(ctx);
>>>> +}
>>>> +
>>>> +void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t
>>>> count,
>>>> +                             QTestState *qts, QPCIDevice *dev,
>>>> +                             QPCIBar bar, uint64_t smmu_base)
>>>> +{
>>>> +    for (int i = 0; i < count; i++) {
>>>> +        /* Initialize test memory */
>>>> +        qtest_memset(qts, configs[i].dma_iova, 0x00,
>>>> configs[i].dma_len);
>>>> +        /* Execute each test configuration */
>>>> +        QSMMUTestContext ctx = {
>>>> +            .qts = qts,
>>>> +            .dev = dev,
>>>> +            .bar = bar,
>>>> +            .smmu_base = smmu_base,
>>>> +            .config = configs[i],
>>>> +            .trans_status = 0,
>>>> +            .dma_result = 0,
>>>> +            .sid = dev->devfn,
>>>> +            .tx_space = qsmmu_sec_sid_to_space(configs[i].sec_sid),
>>>> +        };
>>>> +
>>>> +        qsmmu_single_translation(&ctx);
>>>> +        g_test_message("--> Test %d completed: mode=%u sec_sid=%u "
>>>> +                       "status=0x%x result=0x%x", i,
>>>> configs[i].trans_mode,
>>>> +                       configs[i].sec_sid, ctx.trans_status,
>>>> ctx.dma_result);
>>>> +    }
>>>> +}
>>>
>>> What is the reason for batching operations?
>>> We are not in a performance critical scenario for running this test,
>>> so it's probably better to have distinct calls to single_translation.
>>
>>
>> As described in the previous thread [1] , I plan to split the tests so
>> that each translation mode is exercised by its own qtest. With that
>> split in place, there is no real need for a qsmmu_translation_batch()
>> helper anymore, so I refactor it into a qsmmu_run_translation_case
>> function and drop the inside for-loop.
>>
>
> All good, indeed removes the need for translation_batch.
>
>>
>> [1]
>> https://lore.kernel.org/qemu-devel/7370070a-c569-4b77-bd1e-6fc749ba9c90@phytium.com.cn/ 
>>
>>
>>>
>>> ...
>>>
>>> For the rest of the patch, which is quite consequent, congrats. It's
>>> hard to review all the setup phase here, but knowing it works with the
>>> current smmuv3 implementation, that's a good proof that it's working
>>> as expected.
>>
>>
>> Yes, setting up all this infrastructure did take some time, especially
>> getting the nested mode page tables right (and Secure state-related
>> configuration which is still in my local repo).
>>
>
> Feel free to start with the current version, and then you'll add 
> secure state related changes as part of your other series.
>
>> I really appreciate that you ran the tests yourself and even checked
>> with a coverage-enabled build to confirm that it exercises the smmuv3
>> implementation. Thanks again for the thorough review.
>>
>
> In case someone else wants to reproduce:
> $ export CFLAGS="--coverage"
> $ ./configure --target-list=aarch64-softmmu
> $ ninja -C build
> $ QTEST_QEMU_BINARY=./build/qemu-system-aarch64 \
>   ./build/tests/qtest/iommu-smmuv3-test
> $ rm -rf build/coverage_html
> $ mkdir build/coverage_html
> $ gcovr \
>       --gcov-ignore-parse-errors suspicious_hits.warn \
>       --gcov-ignore-parse-errors negative_hits.warn \
>       --merge-mode-functions=separate \
>       --html-details build/coverage_html/index.html \
>       --filter 'hw/arm/smmu*'
> $ echo file://$(pwd)/build/coverage_html/index.html
> # open this in browser by clicking on your terminal
>
> If useful for you, you can attach those instructions in your next 
> cover letter, so people can easily reproduce.
>

Hi Pierrick,

Got it, I’ll keep this series focused on the current non-secure smmuv3 
implementation, and the coverage instructions you shared are very 
helpful — I’ll add them to the next cover letter so that other reviewers 
can easily reproduce the results.

Thanks again for all the detailed review and guidance.

Best regards,
Tao



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 2/4] hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing
  2025-11-26 15:45 ` [RFC RESEND v5 2/4] hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing Tao Tang
  2025-12-04 18:36   ` Pierrick Bouvier
@ 2025-12-10 18:35   ` Eric Auger
  2025-12-11  7:27     ` Tao Tang
  1 sibling, 1 reply; 23+ messages in thread
From: Eric Auger @ 2025-12-10 18:35 UTC (permalink / raw)
  To: Tao Tang, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Jean-Philippe Brucker, Mostafa Saleh,
	CLEMENT MATHIEU--DRIF



On 11/26/25 4:45 PM, Tao Tang wrote:
> Add a minimal PCI test device designed to exercise IOMMU translation
> (such as ARM SMMUv3) without requiring guest firmware or OS. The device
> provides MMIO registers to configure and trigger DMA operations with
> controllable attributes (security state, address space), enabling
> deterministic IOMMU testing.
>
> Key features:
> - Bare-metal IOMMU testing via simple MMIO interface
> - Configurable DMA attributes for security states and address spaces
> - Write-then-read verification pattern with automatic result checking
>
> The device performs a deterministic DMA test pattern: write a known
> value (0x88888888) to a configured IOVA, read it back, and verify data
> integrity. Results are reported through a dedicated result register,
> eliminating the need for complex interrupt handling or driver
> infrastructure in tests.
>
> This is purely a test device and not intended for production use or
> machine realism. It complements existing test infrastructure like
> pci-testdev but focuses specifically on IOMMU translation path
> validation.
>
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
>  docs/specs/index.rst            |   1 +
>  docs/specs/iommu-testdev.rst    | 109 +++++++++++++
>  hw/misc/Kconfig                 |   5 +
>  hw/misc/iommu-testdev.c         | 278 ++++++++++++++++++++++++++++++++
>  hw/misc/meson.build             |   1 +
>  hw/misc/trace-events            |  10 ++
>  include/hw/misc/iommu-testdev.h |  70 ++++++++
>  7 files changed, 474 insertions(+)
>  create mode 100644 docs/specs/iommu-testdev.rst
>  create mode 100644 hw/misc/iommu-testdev.c
>  create mode 100644 include/hw/misc/iommu-testdev.h
>
> diff --git a/docs/specs/index.rst b/docs/specs/index.rst
> index f19d73c9f6..1fc7fae6bb 100644
> --- a/docs/specs/index.rst
> +++ b/docs/specs/index.rst
> @@ -39,3 +39,4 @@ guest hardware that is specific to QEMU.
>     riscv-iommu
>     riscv-aia
>     aspeed-intc
> +   iommu-testdev
> \ No newline at end of file
> diff --git a/docs/specs/iommu-testdev.rst b/docs/specs/iommu-testdev.rst
> new file mode 100644
> index 0000000000..fdc7f2ee89
> --- /dev/null
> +++ b/docs/specs/iommu-testdev.rst
> @@ -0,0 +1,109 @@
> +iommu-testdev — IOMMU test device for bare-metal testing
> +=========================================================
> +
> +Overview
> +--------
> +``iommu-testdev`` is a minimal, test-only PCI device designed to exercise
> +IOMMU translation (such as ARM SMMUv3) without requiring firmware or a guest
> +OS. Tests can populate IOMMU translation tables with known values and trigger
> +DMA operations that flow through the IOMMU translation path. It is **not** a
> +faithful PCIe endpoint and must be considered a QEMU-internal test vehicle.
> +
> +Key Features
> +------------
> +* **Bare-metal IOMMU testing**: No guest kernel or firmware required
> +* **Configurable DMA attributes**: Supports address space  configuration via
double space
> +  MMIO registers
> +* **Deterministic verification**: Write-then-read DMA pattern with automatic
> +  result checking
> +
> +Status
> +------
> +* Location: ``hw/misc/iommu-testdev.c``
> +* Header: ``include/hw/misc/iommu-testdev.h``
> +* Build guard: ``CONFIG_IOMMU_TESTDEV``
> +
> +Device Interface
> +----------------
> +The device exposes a single PCI BAR0 with MMIO registers:
all those regs are 32b?
> +
> +* ``ITD_REG_DMA_TRIGGERING`` (0x00): Reading triggers DMA execution
> +* ``ITD_REG_DMA_GVA_LO`` (0x04): IOVA/GVA bits [31:0]
> +* ``ITD_REG_DMA_GVA_HI`` (0x08): IOVA/GVA bits [63:32]
> +* ``ITD_REG_DMA_LEN`` (0x0C): DMA transfer length
> +* ``ITD_REG_DMA_RESULT`` (0x10): DMA operation result (0=success)
> +* ``ITD_REG_DMA_DBELL`` (0x14): Write 1 to arm DMA
can you elaborate on this DMA_DBELL? It is not crystal clear to me.
> +* ``ITD_REG_DMA_ATTRS`` (0x18): DMA attributes
> +
> +  - bit[0]: secure (1=Secure, 0=Non-Secure)
> +  - bits[2:1]: address space (0=Non-Secure, 1=Secure, 2=Root, 3=Realm)
I would simply remove Root and Realm for now. Are those bits part of 

ITD_REG_DMA_ATTR? 

> +    Only these MemTxAttrs fields (``secure`` and ``space``) are consumed today;
> +    other bits are reserved but can be wired up easily if future tests need
> +    to pass extra attributes.
> +
> +Translation Setup Workflow
> +--------------------------
> +``iommu-testdev`` never builds SMMU/AMD-Vi/RISC-V IOMMU structures on its own.
> +Architecture-specific construction lives entirely in qtest/libqos helpers.
> +Those helpers populate guest memory with page tables/architecture-specific
> +structures and program the emulated IOMMU registers directly. See the
> +``qsmmu_setup_and_enable_translation()`` function in
> +``tests/qtest/libqos/qos-smmuv3.c`` for an example of how SMMUv3 translation
> +is set up for this device.
> +
> +DMA Operation Flow
> +------------------
> +1. Test programs IOMMU translation tables
> +2. Test configures DMA address (GVA_LO/HI), length, and attributes
> +3. Test writes 1 to DMA_DBELL to arm the operation
> +4. Test reads DMA_TRIGGERING to execute DMA
why do we need 3 and 4?
> +5. Test polls DMA_RESULT:
> +
> +   - 0x00000000: Success
> +   - 0xFFFFFFFE: Busy (still in progress)
> +   - 0xDEAD000X: Various error codes
> +
> +The device performs a write-then-read sequence using a known pattern
> +(0x88888888) and verifies data integrity automatically.
> +
> +Running the qtest
> +-----------------
> +The SMMUv3 test suite uses this device and covers multiple translation modes::
> +
> +    cd build-debug
> +    QTEST_QEMU_BINARY=./qemu-system-aarch64 \\
> +        ./tests/qtest/iommu-smmuv3-test --tap -k
> +
> +This test suite exercises:
> +
> +* Stage 1 only translation
> +* Stage 2 only translation
> +* Nested (Stage 1 + Stage 2) translation
> +* Multiple security spaces (Non-Secure, Secure, Root, Realm)
ditto
> +
> +Instantiation
> +-------------
> +The device is not wired into any board by default. Tests instantiate it
> +via QEMU command line::
> +
> +    -device iommu-testdev
> +
> +For ARM platforms with SMMUv3::
> +
> +    -M virt,iommu=smmuv3 -device iommu-testdev
does it work -device arm-smmuv3 multi instantiation too?
> +
> +The device will be placed behind the IOMMU automatically.
> +
> +Limitations
> +-----------
> +* No realistic PCIe enumeration, MSI/MSI-X, or interrupt handling
> +* No ATS/PRI support
> +* No actual device functionality beyond DMA test pattern
> +* Test-only; not suitable for production or machine realism
> +* Address space support (Secure/Root/Realm) is architecture-dependent
> +
> +See also
> +--------
> +* ``tests/qtest/iommu-smmuv3-test.c`` — SMMUv3 test suite
> +* ``tests/qtest/libqos/qos-smmuv3.{c,h}`` — SMMUv3 test library
> +* SMMUv3 emulation: ``hw/arm/smmu*``
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index fccd735c24..b5f6fdbd9c 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -25,6 +25,11 @@ config PCI_TESTDEV
>      default y if TEST_DEVICES
>      depends on PCI
>  
> +config IOMMU_TESTDEV
> +    bool
> +    default y if TEST_DEVICES
> +    depends on PCI
> +
>  config EDU
>      bool
>      default y if TEST_DEVICES
> diff --git a/hw/misc/iommu-testdev.c b/hw/misc/iommu-testdev.c
> new file mode 100644
> index 0000000000..3182ccea4d
> --- /dev/null
> +++ b/hw/misc/iommu-testdev.c
> @@ -0,0 +1,278 @@
> +/*
> + * A test device for IOMMU
> + *
> + * This test device is a minimal IOMMU-aware device used to test the IOMMU.
how is it IOMMU aware? Isn't it "simply" a kinda PCIe DMA device

it behaves as other PCIe devices with s->dma_as = pci_device_iommu_address_space(pdev);
no?


> + *
> + * Copyright (c) 2025 Phytium Technology
> + *
> + * Author:
> + *  Tao Tang <tangtao1634@phytium.com.cn>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "system/address-spaces.h"
> +#include "trace.h"
> +#include "hw/pci/pci_device.h"
> +#include "hw/qdev-properties.h"
> +#include "qom/object.h"
> +#include "hw/misc/iommu-testdev.h"
> +
> +#define TYPE_IOMMU_TESTDEV "iommu-testdev"
> +OBJECT_DECLARE_SIMPLE_TYPE(IOMMUTestDevState, IOMMU_TESTDEV)
> +
> +struct IOMMUTestDevState {
> +    PCIDevice parent_obj;
> +    MemoryRegion bar0;
> +    uint64_t dma_vaddr;
> +    uint32_t dma_len;
> +    uint32_t dma_result;
> +    bool dma_pending;
> +
> +    AddressSpace *dma_as;   /* IOMMU-mediated DMA AS for this device */
> +    uint32_t dma_attrs_cfg; /* bit0 secure, bits[2:1] space, bit3 unspecified */
> +};
> +
> +static void iommu_testdev_maybe_run_dma(IOMMUTestDevState *s)
> +{
> +    uint32_t expected_val, actual_val;
> +    g_autofree uint8_t *write_buf = NULL;
> +    g_autofree uint8_t *read_buf = NULL;
> +    MemTxResult write_res, read_res;
> +    MemTxAttrs attrs;
> +    AddressSpace *as;
> +
> +    if (!s->dma_pending) {
> +        s->dma_result = ITD_DMA_ERR_NOT_ARMED;
> +        trace_iommu_testdev_dma_result(s->dma_result);
> +        return;
> +    }
> +    trace_iommu_testdev_dma_start();
> +
> +    s->dma_pending = false;
> +
> +    if (!s->dma_len) {
> +        s->dma_result = ITD_DMA_ERR_BAD_LEN;
> +        return;
> +    }
> +
> +    write_buf = g_malloc(s->dma_len);
> +    read_buf = g_malloc(s->dma_len);
> +
> +    /* Initialize MemTxAttrs from generic register */
> +    attrs = MEMTXATTRS_UNSPECIFIED;
> +    attrs.secure = ITD_ATTRS_GET_SECURE(s->dma_attrs_cfg);
> +
> +    /*
> +     * The 'space' field in MemTxAttrs is ARM-specific.
> +     * On other architectures where this field doesn't exist.
> +     */
> +    attrs.space = ITD_ATTRS_GET_SPACE(s->dma_attrs_cfg);
> +
> +    as = s->dma_as;
> +
> +    /* Step 1: Write ITD_DMA_WRITE_VAL to DMA address */
> +    trace_iommu_testdev_dma_write(s->dma_vaddr, s->dma_len);
> +
> +    for (int i = 0; i < s->dma_len; i++) {
> +        /* Data is written in little-endian order */
> +        write_buf[i] = (ITD_DMA_WRITE_VAL >> ((i % 4) * 8)) & 0xff;
> +    }
> +    write_res = dma_memory_write(as, s->dma_vaddr, write_buf, s->dma_len,
> +                                 attrs);
> +
> +    if (write_res != MEMTX_OK) {
> +        s->dma_result = ITD_DMA_ERR_TX_FAIL;
> +        trace_iommu_testdev_dma_result(s->dma_result);
> +        return;
> +    }
> +
> +    /* Step 2: Read back from the same DMA address */
> +    trace_iommu_testdev_dma_read(s->dma_vaddr, s->dma_len);
> +
> +    read_res = dma_memory_read(as, s->dma_vaddr, read_buf, s->dma_len, attrs);
> +
> +    if (read_res != MEMTX_OK) {
> +        s->dma_result = ITD_DMA_ERR_RD_FAIL;
> +        trace_iommu_testdev_dma_result(s->dma_result);
> +        return;
> +    }
> +
> +    /* Step 3: Verify the read data matches what we wrote */
> +    for (int i = 0; i < s->dma_len; i += 4) {
> +        int remaining_bytes = MIN(4, s->dma_len - i);
> +
> +        expected_val = 0;
> +        actual_val = 0;
> +
> +        for (int j = 0; j < remaining_bytes; j++) {
> +            expected_val |= ((uint32_t)write_buf[i + j]) << (j * 8);
> +            actual_val |= ((uint32_t)read_buf[i + j]) << (j * 8);
> +        }
> +
> +        trace_iommu_testdev_dma_verify(expected_val, actual_val);
> +
> +        if (expected_val != actual_val) {
> +            s->dma_result = ITD_DMA_ERR_MISMATCH;
> +            trace_iommu_testdev_dma_result(s->dma_result);
> +            return;
> +        }
> +    }
> +
> +    /* All checks passed */
> +    s->dma_result = 0;
> +    trace_iommu_testdev_dma_result(s->dma_result);
> +}
> +
> +static uint64_t iommu_testdev_mmio_read(void *opaque, hwaddr addr,
> +                                        unsigned size)
> +{
> +    IOMMUTestDevState *s = opaque;
> +    uint64_t value = 0;
> +
> +    switch (addr) {
> +    case ITD_REG_DMA_TRIGGERING:
> +        /*
> +         * This lets tests poll ITD_REG_DMA_RESULT to observe BUSY before
> +         * consuming the DMA.
> +         */
> +        iommu_testdev_maybe_run_dma(s);
> +        value = 0;
> +        break;
> +    case ITD_REG_DMA_GVA_LO:
> +        value = (uint32_t)(s->dma_vaddr & 0xffffffffu);
> +        break;
> +    case ITD_REG_DMA_GVA_HI:
> +        value = (uint32_t)(s->dma_vaddr >> 32);
> +        break;
> +    case ITD_REG_DMA_LEN:
> +        value = s->dma_len;
> +        break;
> +    case ITD_REG_DMA_RESULT:
> +        value = s->dma_result;
> +        break;
> +    case ITD_REG_DMA_ATTRS:
> +        value = s->dma_attrs_cfg;
> +        break;
> +    default:
> +        value = 0;
> +        break;
> +    }
> +
> +    trace_iommu_testdev_mmio_read(addr, value, size);
> +    return value;
> +}
> +
> +static void iommu_testdev_mmio_write(void *opaque, hwaddr addr, uint64_t val,
> +                                     unsigned size)
> +{
> +    IOMMUTestDevState *s = opaque;
> +    uint32_t data = val;
> +
> +    trace_iommu_testdev_mmio_write(addr, val, size);
> +
> +    switch (addr) {
> +    case ITD_REG_DMA_GVA_LO:
> +        s->dma_vaddr = (s->dma_vaddr & ~0xffffffffull) | data;
> +        break;
> +    case ITD_REG_DMA_GVA_HI:
> +        s->dma_vaddr = (s->dma_vaddr & 0xffffffffull) |
> +                       ((uint64_t)data << 32);
> +        break;
> +    case ITD_REG_DMA_LEN:
> +        s->dma_len = data;
> +        break;
> +    case ITD_REG_DMA_RESULT:
> +        s->dma_result = data;
> +        break;
> +    case ITD_REG_DMA_DBELL:
> +        if (data & ITD_DMA_DBELL_ARM) {
> +            /* Arm the DMA operation */
> +            s->dma_pending = true;
> +            s->dma_result = ITD_DMA_RESULT_BUSY;
> +            trace_iommu_testdev_dma_pending(true);
> +        } else {
> +            /* Disarm the DMA operation */
> +            s->dma_pending = false;
> +            s->dma_result = ITD_DMA_RESULT_IDLE;
> +            trace_iommu_testdev_dma_pending(false);
> +        }
> +        break;
> +    case ITD_REG_DMA_ATTRS:
> +        s->dma_attrs_cfg = data;
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps iommu_testdev_mmio_ops = {
> +    .read = iommu_testdev_mmio_read,
> +    .write = iommu_testdev_mmio_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void iommu_testdev_realize(PCIDevice *pdev, Error **errp)
> +{
> +    IOMMUTestDevState *s = IOMMU_TESTDEV(pdev);
> +
> +    s->dma_vaddr = 0;
> +    s->dma_len = 0;
> +    s->dma_result = ITD_DMA_RESULT_IDLE;
> +    s->dma_pending = false;
> +    s->dma_attrs_cfg = 0;
> +    s->dma_as = pci_device_iommu_address_space(pdev);
> +
> +    memory_region_init_io(&s->bar0, OBJECT(pdev), &iommu_testdev_mmio_ops, s,
> +                          TYPE_IOMMU_TESTDEV ".bar0", BAR0_SIZE);
> +    pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0);
> +}
> +
> +static void iommu_testdev_reset(DeviceState *dev)
> +{
> +    IOMMUTestDevState *s = IOMMU_TESTDEV(dev);
> +
> +    s->dma_vaddr = 0;
> +    s->dma_len = 0;
> +    s->dma_result = ITD_DMA_RESULT_IDLE;
> +    s->dma_pending = false;
> +    s->dma_attrs_cfg = 0;
> +}
> +
> +static void iommu_testdev_class_init(ObjectClass *klass, const void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
> +
> +    pc->realize = iommu_testdev_realize;
> +    pc->vendor_id = IOMMU_TESTDEV_VENDOR_ID;
> +    pc->device_id = IOMMU_TESTDEV_DEVICE_ID;
> +    pc->revision = 0;
> +    pc->class_id = PCI_CLASS_OTHERS;
> +    dc->desc = "A test device for IOMMU";
> +    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
> +    device_class_set_legacy_reset(dc, iommu_testdev_reset);
> +}
> +
> +static const TypeInfo iommu_testdev_info = {
> +    .name          = TYPE_IOMMU_TESTDEV,
> +    .parent        = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(IOMMUTestDevState),
> +    .class_init    = iommu_testdev_class_init,
> +    .interfaces    = (const InterfaceInfo[]) {
> +        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
> +        { }
> +    },
> +};
> +
> +static void iommu_testdev_register_types(void)
> +{
> +    type_register_static(&iommu_testdev_info);
> +}
> +
> +type_init(iommu_testdev_register_types);
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index b1d8d8e5d2..6f9bb9bb0f 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c'))
>  system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
>  system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
>  system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
> +system_ss.add(when: 'CONFIG_IOMMU_TESTDEV', if_true: files('iommu-testdev.c'))
>  system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
>  system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
>  system_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
> diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> index eeb9243898..84fd349fb8 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -409,3 +409,13 @@ ivshmem_flat_interrupt_peer(uint16_t peer_id, uint16_t vector_id) "Interrupting
>  i2c_echo_event(const char *id, const char *event) "%s: %s"
>  i2c_echo_recv(const char *id, uint8_t data) "%s: recv 0x%02" PRIx8
>  i2c_echo_send(const char *id, uint8_t data) "%s: send 0x%02" PRIx8
> +
> +# iommu-testdev.c
> +iommu_testdev_mmio_read(uint64_t addr, uint64_t value, unsigned size) "addr=0x%" PRIx64 " value=0x%" PRIx64 " size=%u"
> +iommu_testdev_mmio_write(uint64_t addr, uint64_t value, unsigned size) "addr=0x%" PRIx64 " value=0x%" PRIx64 " size=%u"
> +iommu_testdev_dma_start(void) "DMA operation started"
> +iommu_testdev_dma_write(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u"
> +iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u"
> +iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x"
> +iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x"
> +iommu_testdev_dma_pending(bool pending) "pending=%d"
> diff --git a/include/hw/misc/iommu-testdev.h b/include/hw/misc/iommu-testdev.h
> new file mode 100644
> index 0000000000..06924e737c
> --- /dev/null
> +++ b/include/hw/misc/iommu-testdev.h
> @@ -0,0 +1,70 @@
> +/*
> + * A test device for IOMMU
> + *
> + * This test device is a minimal IOMMU-aware device used to test the IOMMU.
> + *
> + * Copyright (c) 2025 Phytium Technology
> + *
> + * Author:
> + *  Tao Tang <tangtao1634@phytium.com.cn>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_MISC_IOMMU_TESTDEV_H
> +#define HW_MISC_IOMMU_TESTDEV_H
> +
> +#include "hw/pci/pci.h"
> +
> +#define IOMMU_TESTDEV_VENDOR_ID     PCI_VENDOR_ID_REDHAT
> +#define IOMMU_TESTDEV_DEVICE_ID     PCI_DEVICE_ID_REDHAT_TEST
> +
> +/* DMA_ATTRS register bit definitions (architecture-agnostic) */
> +#define ITD_ATTRS_SECURE_SHIFT      0
> +#define ITD_ATTRS_SECURE_MASK       0x1
> +#define ITD_ATTRS_SPACE_SHIFT       1
> +#define ITD_ATTRS_SPACE_MASK        0x3
> +
> +/* Helper macros for setting fields */
> +#define ITD_ATTRS_SET_SECURE(attrs, val)                              \
> +    (((attrs) & ~(ITD_ATTRS_SECURE_MASK << ITD_ATTRS_SECURE_SHIFT)) | \
> +     (((val) & ITD_ATTRS_SECURE_MASK) << ITD_ATTRS_SECURE_SHIFT))
> +
> +#define ITD_ATTRS_SET_SPACE(attrs, val)                               \
> +    (((attrs) & ~(ITD_ATTRS_SPACE_MASK << ITD_ATTRS_SPACE_SHIFT)) |   \
> +     (((val) & ITD_ATTRS_SPACE_MASK) << ITD_ATTRS_SPACE_SHIFT))
> +
> +/* Helper macros for getting fields */
> +#define ITD_ATTRS_GET_SECURE(attrs)                                   \
> +    (((attrs) >> ITD_ATTRS_SECURE_SHIFT) & ITD_ATTRS_SECURE_MASK)
> +
> +#define ITD_ATTRS_GET_SPACE(attrs)                                    \
> +    (((attrs) >> ITD_ATTRS_SPACE_SHIFT) & ITD_ATTRS_SPACE_MASK)
> +
> +/* DMA result/status values shared with tests */
> +#define ITD_DMA_RESULT_IDLE   0xffffffffu
> +#define ITD_DMA_RESULT_BUSY   0xfffffffeu
> +#define ITD_DMA_ERR_BAD_LEN   0xdead0001u
> +#define ITD_DMA_ERR_TX_FAIL   0xdead0002u
> +#define ITD_DMA_ERR_RD_FAIL   0xdead0003u
> +#define ITD_DMA_ERR_MISMATCH  0xdead0004u
> +#define ITD_DMA_ERR_NOT_ARMED 0xdead0005u
> +
> +#define ITD_DMA_WRITE_VAL     0x88888888u
> +
> +/* DMA doorbell bits */
> +#define ITD_DMA_DBELL_ARM    0x1u
> +
> +/* BAR0 layout of iommu-testdev */
> +enum {
> +    ITD_REG_DMA_TRIGGERING  = 0x00,
> +    ITD_REG_DMA_GVA_LO      = 0x04,
> +    ITD_REG_DMA_GVA_HI      = 0x08,
> +    ITD_REG_DMA_LEN         = 0x0c,
> +    ITD_REG_DMA_RESULT      = 0x10,
> +    ITD_REG_DMA_DBELL       = 0x14,
> +    ITD_REG_DMA_ATTRS       = 0x18, /* [0] secure,[2:1] space,[3] unspecified */
> +    BAR0_SIZE               = 0x1000,
> +};
> +
> +#endif /* HW_MISC_IOMMU_TESTDEV_H */
Eric



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library
  2025-12-06  5:27         ` Tao Tang
@ 2025-12-10 18:40           ` Eric Auger
  2025-12-11  8:53             ` Tao Tang
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Auger @ 2025-12-10 18:40 UTC (permalink / raw)
  To: Tao Tang, Pierrick Bouvier, Paolo Bonzini, Fabiano Rosas,
	Laurent Vivier, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

Hi Tao,

On 12/6/25 6:27 AM, Tao Tang wrote:
>
> On 2025/12/6 01:19, Pierrick Bouvier wrote:
>> On 12/5/25 7:03 AM, Tao Tang wrote:
>>> Hi Pierrick,
>>>
>>> On 2025/12/5 07:53, Pierrick Bouvier wrote:
>>>> On 11/26/25 7:45 AM, Tao Tang wrote:
>>>>> Introduce qos-smmuv3, a reusable library for SMMUv3-related qtest
>>>>> operations. This module encapsulates common tasks like:
>>>>>
>>>>> - SMMUv3 initialization (enabling, configuring command/event queues)
>>>>> - Stream Table Entry (STE) and Context Descriptor (CD) setup
>>>>> - Multi-level page table construction (L0-L3 for 4KB granules)
>>>>> - Support for Stage 1, Stage 2, and nested translation modes
>>>>> - Could be easily extended to support multi-space testing
>>>>> infrastructure
>>>>>       (Non-Secure, Secure, Root, Realm)
>>>>>
>>>>> The library provides high-level abstractions that allow test code to
>>>>> focus on IOMMU behavior validation rather than low-level register
>>>>> manipulation and page table encoding. Key features include:
>>>>>
>>>>> - Automatic memory allocation for translation structures with proper
>>>>>       alignment
>>>>> - Helper functions to build valid STEs/CDs for different translation
>>>>>       scenarios
>>>>> - Page table walkers that handle address offset calculations per
>>>>>       security space
>>>>> - Command queue management for SMMU configuration commands
>>>>>
>>>>> This infrastructure is designed to be used by iommu-testdev-based
>>>>> tests
>>>>> and future SMMUv3 test suites, reducing code duplication and
>>>>> improving
>>>>> test maintainability.
>>>>>
>>>>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>>>>> ---
>>>>>    tests/qtest/libqos/meson.build  |   3 +
>>>>>    tests/qtest/libqos/qos-smmuv3.c | 731
>>>>> ++++++++++++++++++++++++++++++++
>>>>>    tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
>>>>>    3 files changed, 1001 insertions(+)
>>>>>    create mode 100644 tests/qtest/libqos/qos-smmuv3.c
>>>>>    create mode 100644 tests/qtest/libqos/qos-smmuv3.h
>>>>>
>>>>
>>>> ...
>>>>
>>>>> +
>>>>> +void qsmmu_single_translation(QSMMUTestContext *ctx)
>>>>> +{
>>>>> +    uint32_t config_result;
>>>>> +    uint32_t dma_result;
>>>>> +    bool test_passed;
>>>>> +
>>>>> +    /* Configure SMMU translation */
>>>>> +    config_result = qsmmu_setup_and_enable_translation(ctx);
>>>>> +    if (config_result != 0) {
>>>>> +        g_test_message("Configuration failed: mode=%u status=0x%x",
>>>>> +                       ctx->config.trans_mode, config_result);
>>>>> +        return;
>>>>
>>>> Is that expected to silently return if we can't configure translation?
>>>
>>>
>>> No, it is not intended to silently return on a failed configuration.
>>> Maybe an assertion is a better choice:
>>>
>>>
>>> config_result = qsmmu_setup_and_enable_translation(ctx);
>>>
>>> g_assert_cmpuint(config_result, ==, 0);
>>>
>>
>> Looks good. We should rely on exit code first, and then on verbose
>> log to find what is the problem.
>>
>>>>
>>>>> +    }
>>>>> +
>>>>> +    /* Trigger DMA operation */
>>>>> +    dma_result = qsmmu_trigger_dma(ctx);
>>>>> +    if (dma_result != 0) {
>>>>> +        g_test_message("DMA failed: mode=%u result=0x%x",
>>>>> +                       ctx->config.trans_mode, dma_result);
>>>>> +    } else {
>>>>> +        g_test_message("-> DMA succeeded: mode=%u",
>>>>> ctx->config.trans_mode);
>>>>> +    }
>>>>> +
>>>>> +    /* Validate test result */
>>>>> +    test_passed = qsmmu_validate_test_result(ctx);
>>>>> +    g_assert_true(test_passed);
>>>>> +
>>>>> +    /* Clean up translation state to prepare for the next test */
>>>>> +    qsmmu_cleanup_translation(ctx);
>>>>> +}
>>>>> +
>>>>> +void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t
>>>>> count,
>>>>> +                             QTestState *qts, QPCIDevice *dev,
>>>>> +                             QPCIBar bar, uint64_t smmu_base)
>>>>> +{
>>>>> +    for (int i = 0; i < count; i++) {
>>>>> +        /* Initialize test memory */
>>>>> +        qtest_memset(qts, configs[i].dma_iova, 0x00,
>>>>> configs[i].dma_len);
>>>>> +        /* Execute each test configuration */
>>>>> +        QSMMUTestContext ctx = {
>>>>> +            .qts = qts,
>>>>> +            .dev = dev,
>>>>> +            .bar = bar,
>>>>> +            .smmu_base = smmu_base,
>>>>> +            .config = configs[i],
>>>>> +            .trans_status = 0,
>>>>> +            .dma_result = 0,
>>>>> +            .sid = dev->devfn,
>>>>> +            .tx_space = qsmmu_sec_sid_to_space(configs[i].sec_sid),
>>>>> +        };
>>>>> +
>>>>> +        qsmmu_single_translation(&ctx);
>>>>> +        g_test_message("--> Test %d completed: mode=%u sec_sid=%u "
>>>>> +                       "status=0x%x result=0x%x", i,
>>>>> configs[i].trans_mode,
>>>>> +                       configs[i].sec_sid, ctx.trans_status,
>>>>> ctx.dma_result);
>>>>> +    }
>>>>> +}
>>>>
>>>> What is the reason for batching operations?
>>>> We are not in a performance critical scenario for running this test,
>>>> so it's probably better to have distinct calls to single_translation.
>>>
>>>
>>> As described in the previous thread [1] , I plan to split the tests so
>>> that each translation mode is exercised by its own qtest. With that
>>> split in place, there is no real need for a qsmmu_translation_batch()
>>> helper anymore, so I refactor it into a qsmmu_run_translation_case
>>> function and drop the inside for-loop.
>>>
>>
>> All good, indeed removes the need for translation_batch.
>>
>>>
>>> [1]
>>> https://lore.kernel.org/qemu-devel/7370070a-c569-4b77-bd1e-6fc749ba9c90@phytium.com.cn/
>>>
>>>
>>>>
>>>> ...
>>>>
>>>> For the rest of the patch, which is quite consequent, congrats. It's
>>>> hard to review all the setup phase here, but knowing it works with the
>>>> current smmuv3 implementation, that's a good proof that it's working
>>>> as expected.
>>>
>>>
>>> Yes, setting up all this infrastructure did take some time, especially
>>> getting the nested mode page tables right (and Secure state-related
>>> configuration which is still in my local repo).
>>>
>>
>> Feel free to start with the current version, and then you'll add
>> secure state related changes as part of your other series.
>>
>>> I really appreciate that you ran the tests yourself and even checked
>>> with a coverage-enabled build to confirm that it exercises the smmuv3
>>> implementation. Thanks again for the thorough review.
>>>
>>
>> In case someone else wants to reproduce:
>> $ export CFLAGS="--coverage"
>> $ ./configure --target-list=aarch64-softmmu
>> $ ninja -C build
>> $ QTEST_QEMU_BINARY=./build/qemu-system-aarch64 \
>>   ./build/tests/qtest/iommu-smmuv3-test
>> $ rm -rf build/coverage_html
>> $ mkdir build/coverage_html
>> $ gcovr \
>>       --gcov-ignore-parse-errors suspicious_hits.warn \
>>       --gcov-ignore-parse-errors negative_hits.warn \
>>       --merge-mode-functions=separate \
>>       --html-details build/coverage_html/index.html \
>>       --filter 'hw/arm/smmu*'
>> $ echo file://$(pwd)/build/coverage_html/index.html
>> # open this in browser by clicking on your terminal
>>
>> If useful for you, you can attach those instructions in your next
>> cover letter, so people can easily reproduce. 

are you ready to maintain that code (esp the lib)? You shall add an
entry in the MAINTAINERS file for those new files I guess.

Eric
>>
>
> Hi Pierrick,
>
> Got it, I’ll keep this series focused on the current non-secure smmuv3
> implementation, and the coverage instructions you shared are very
> helpful — I’ll add them to the next cover letter so that other
> reviewers can easily reproduce the results.
>
> Thanks again for all the detailed review and guidance.
>
> Best regards,
> Tao
>



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library
  2025-12-04 23:53   ` Pierrick Bouvier
  2025-12-05 15:03     ` Tao Tang
@ 2025-12-10 18:43     ` Eric Auger
  2025-12-11  9:39       ` Tao Tang
  1 sibling, 1 reply; 23+ messages in thread
From: Eric Auger @ 2025-12-10 18:43 UTC (permalink / raw)
  To: Pierrick Bouvier, Tao Tang, Paolo Bonzini, Fabiano Rosas,
	Laurent Vivier, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF



On 12/5/25 12:53 AM, Pierrick Bouvier wrote:
> On 11/26/25 7:45 AM, Tao Tang wrote:
>> Introduce qos-smmuv3, a reusable library for SMMUv3-related qtest
>> operations. This module encapsulates common tasks like:
>>
>> - SMMUv3 initialization (enabling, configuring command/event queues)
>> - Stream Table Entry (STE) and Context Descriptor (CD) setup
>> - Multi-level page table construction (L0-L3 for 4KB granules)
>> - Support for Stage 1, Stage 2, and nested translation modes
>> - Could be easily extended to support multi-space testing infrastructure
>>      (Non-Secure, Secure, Root, Realm)
>>
>> The library provides high-level abstractions that allow test code to
>> focus on IOMMU behavior validation rather than low-level register
>> manipulation and page table encoding. Key features include:
>>
>> - Automatic memory allocation for translation structures with proper
>>      alignment
>> - Helper functions to build valid STEs/CDs for different translation
>>      scenarios
>> - Page table walkers that handle address offset calculations per
>>      security space
>> - Command queue management for SMMU configuration commands
>>
>> This infrastructure is designed to be used by iommu-testdev-based tests
>> and future SMMUv3 test suites, reducing code duplication and improving
>> test maintainability.
>>
>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>> ---
>>   tests/qtest/libqos/meson.build  |   3 +
>>   tests/qtest/libqos/qos-smmuv3.c | 731 ++++++++++++++++++++++++++++++++
>>   tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
>>   3 files changed, 1001 insertions(+)
>>   create mode 100644 tests/qtest/libqos/qos-smmuv3.c
>>   create mode 100644 tests/qtest/libqos/qos-smmuv3.h
>>
>
> ...
>
>> +
>> +void qsmmu_single_translation(QSMMUTestContext *ctx)
>> +{
>> +    uint32_t config_result;
>> +    uint32_t dma_result;
>> +    bool test_passed;
>> +
>> +    /* Configure SMMU translation */
>> +    config_result = qsmmu_setup_and_enable_translation(ctx);
>> +    if (config_result != 0) {
>> +        g_test_message("Configuration failed: mode=%u status=0x%x",
>> +                       ctx->config.trans_mode, config_result);
>> +        return;
>
> Is that expected to silently return if we can't configure translation?
>
>> +    }
>> +
>> +    /* Trigger DMA operation */
>> +    dma_result = qsmmu_trigger_dma(ctx);
>> +    if (dma_result != 0) {
>> +        g_test_message("DMA failed: mode=%u result=0x%x",
>> +                       ctx->config.trans_mode, dma_result);
>> +    } else {
>> +        g_test_message("-> DMA succeeded: mode=%u",
>> ctx->config.trans_mode);
>> +    }
>> +
>> +    /* Validate test result */
>> +    test_passed = qsmmu_validate_test_result(ctx);
>> +    g_assert_true(test_passed);
>> +
>> +    /* Clean up translation state to prepare for the next test */
>> +    qsmmu_cleanup_translation(ctx);
>> +}
>> +
>> +void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t
>> count,
>> +                             QTestState *qts, QPCIDevice *dev,
>> +                             QPCIBar bar, uint64_t smmu_base)
>> +{
>> +    for (int i = 0; i < count; i++) {
>> +        /* Initialize test memory */
>> +        qtest_memset(qts, configs[i].dma_iova, 0x00,
>> configs[i].dma_len);
>> +        /* Execute each test configuration */
>> +        QSMMUTestContext ctx = {
>> +            .qts = qts,
>> +            .dev = dev,
>> +            .bar = bar,
>> +            .smmu_base = smmu_base,
>> +            .config = configs[i],
>> +            .trans_status = 0,
>> +            .dma_result = 0,
>> +            .sid = dev->devfn,
>> +            .tx_space = qsmmu_sec_sid_to_space(configs[i].sec_sid),
>> +        };
>> +
>> +        qsmmu_single_translation(&ctx);
>> +        g_test_message("--> Test %d completed: mode=%u sec_sid=%u "
>> +                       "status=0x%x result=0x%x", i,
>> configs[i].trans_mode,
>> +                       configs[i].sec_sid, ctx.trans_status,
>> ctx.dma_result);
>> +    }
>> +}
>
> What is the reason for batching operations?
> We are not in a performance critical scenario for running this test,
> so it's probably better to have distinct calls to single_translation.
>
> ...
>
> For the rest of the patch, which is quite consequent, congrats. It's
> hard to review all the setup phase here, but knowing it works with the
> current smmuv3 implementation, that's a good proof that it's working
> as expected. 

That's a huge amount of code indeed. I don't how much we shall review
this test lib but if it needs std review the patch needs to be split to
ease the review. Maybe qtest maintainers can give some guidelines here...

Eric



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev
  2025-11-26 15:45 ` [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev Tao Tang
  2025-12-04 18:42   ` Pierrick Bouvier
@ 2025-12-10 18:45   ` Eric Auger
  2025-12-11  8:06     ` Tao Tang
  1 sibling, 1 reply; 23+ messages in thread
From: Eric Auger @ 2025-12-10 18:45 UTC (permalink / raw)
  To: Tao Tang, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Jean-Philippe Brucker, Mostafa Saleh,
	CLEMENT MATHIEU--DRIF



On 11/26/25 4:45 PM, Tao Tang wrote:
> Add a qtest suite that validates ARM SMMUv3 translation without guest
> firmware or OS. The tests leverage iommu-testdev to trigger DMA
> operations and the qos-smmuv3 library to configure IOMMU translation
> structures.
>
> This test suite targets the virt machine and covers:
> - Stage 1 only translation (VA -> PA via CD page tables)
> - Stage 2 only translation (IPA -> PA via STE S2 tables)
> - Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)
> - Design to extended to support multiple security spaces
>     (Non-Secure, Secure, Root, Realm)
>
> Each test case follows this sequence:
> 1. Initialize SMMUv3 with appropriate command/event queues
> 2. Build translation tables (STE/CD/PTE) for the target scenario
> 3. Configure iommu-testdev with IOVA and DMA attributes via MMIO
> 4. Trigger DMA and validate successful translation
> 5. Verify data integrity through a deterministic write-read pattern
>
> This bare-metal approach provides deterministic IOMMU testing with
> minimal dependencies, making failures directly attributable to the SMMU
> translation path.
>
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
>  tests/qtest/iommu-smmuv3-test.c | 114 ++++++++++++++++++++++++++++++++
>  tests/qtest/meson.build         |   1 +
>  2 files changed, 115 insertions(+)
>  create mode 100644 tests/qtest/iommu-smmuv3-test.c
>
> diff --git a/tests/qtest/iommu-smmuv3-test.c b/tests/qtest/iommu-smmuv3-test.c
> new file mode 100644
> index 0000000000..af438ecce0
> --- /dev/null
> +++ b/tests/qtest/iommu-smmuv3-test.c
> @@ -0,0 +1,114 @@
> +/*
> + * QTest for SMMUv3 with iommu-testdev
> + *
> + * This QTest file is used to test the SMMUv3 with iommu-testdev so that we can
> + * test SMMUv3 without any guest kernel or firmware.
> + *
> + * Copyright (c) 2025 Phytium Technology
> + *
> + * Author:
> + *  Tao Tang <tangtao1634@phytium.com.cn>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "libqtest.h"
> +#include "libqos/pci.h"
> +#include "libqos/generic-pcihost.h"
> +#include "hw/pci/pci_regs.h"
> +#include "hw/misc/iommu-testdev.h"
> +#include "libqos/qos-smmuv3.h"
> +
> +#define DMA_LEN           4
> +
> +/* Test configurations for different SMMU modes and spaces */
> +static const QSMMUTestConfig base_test_configs[] = {
> +    {
> +        .trans_mode = QSMMU_TM_S1_ONLY,
> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
> +        .dma_iova = QSMMU_IOVA_OR_IPA,
what does this mean? QSMMU_IOVA_OR_IPA

Eric
> +        .dma_len = DMA_LEN,
> +        .expected_result = 0
> +    },
> +    {
> +        .trans_mode = QSMMU_TM_S2_ONLY,
> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
> +        .dma_iova = QSMMU_IOVA_OR_IPA,
> +        .dma_len = DMA_LEN,
> +        .expected_result = 0
> +    },
> +    {
> +        .trans_mode = QSMMU_TM_NESTED,
> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
> +        .dma_iova = QSMMU_IOVA_OR_IPA,
> +        .dma_len = DMA_LEN,
> +        .expected_result = 0
> +    }
> +};
> +
> +static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QGenericPCIBus *gbus,
> +                                          QPCIBar *bar)
> +{
> +    uint16_t vid, did;
> +    QPCIDevice *dev = NULL;
> +
> +    qpci_init_generic(gbus, qts, NULL, false);
> +
> +    /* Find device by vendor/device ID to avoid slot surprises. */
> +    for (int s = 0; s < 32 && !dev; s++) {
> +        for (int fn = 0; fn < 8 && !dev; fn++) {
> +            QPCIDevice *cand = qpci_device_find(&gbus->bus, QPCI_DEVFN(s, fn));
> +            if (!cand) {
> +                continue;
> +            }
> +            vid = qpci_config_readw(cand, PCI_VENDOR_ID);
> +            did = qpci_config_readw(cand, PCI_DEVICE_ID);
> +            if (vid == IOMMU_TESTDEV_VENDOR_ID &&
> +                did == IOMMU_TESTDEV_DEVICE_ID) {
> +                dev = cand;
> +                g_test_message("Found iommu-testdev! devfn: 0x%x", cand->devfn);
> +            } else {
> +                g_free(cand);
> +            }
> +        }
> +    }
> +    g_assert(dev);
> +
> +    qpci_device_enable(dev);
> +    *bar = qpci_iomap(dev, 0, NULL);
> +    g_assert_false(bar->is_io);
> +
> +    return dev;
> +}
> +
> +static void test_smmuv3_translation(void)
> +{
> +    QTestState *qts;
> +    QGenericPCIBus gbus;
> +    QPCIDevice *dev;
> +    QPCIBar bar;
> +
> +    /* Initialize QEMU environment for SMMU testing */
> +    qts = qtest_init("-machine virt,acpi=off,gic-version=3,iommu=smmuv3 "
> +                     "-smp 1 -m 512 -cpu max -net none "
> +                     "-device iommu-testdev");
> +
> +    /* Setup and configure PCI device */
> +    dev = setup_qtest_pci_device(qts, &gbus, &bar);
> +    g_assert(dev);
> +
> +    /* Run the enhanced translation tests */
> +    g_test_message("### Starting SMMUv3 translation tests...###");
> +    qsmmu_translation_batch(base_test_configs, ARRAY_SIZE(base_test_configs),
> +                            qts, dev, bar, VIRT_SMMU_BASE);
> +    g_test_message("### SMMUv3 translation tests completed successfully! ###");
> +    qtest_quit(qts);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    g_test_init(&argc, &argv, NULL);
> +    qtest_add_func("/iommu-testdev/translation", test_smmuv3_translation);
> +    return g_test_run();
> +}
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index 669d07c06b..e2d2e68092 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -263,6 +263,7 @@ qtests_aarch64 = \
>     config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \
>    (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \
>    (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \
> +  (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') ? ['iommu-smmuv3-test'] : []) + \
>    qtests_cxl +                                                                                  \
>    ['arm-cpu-features',
>     'numa-test',



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 2/4] hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing
  2025-12-10 18:35   ` Eric Auger
@ 2025-12-11  7:27     ` Tao Tang
  0 siblings, 0 replies; 23+ messages in thread
From: Tao Tang @ 2025-12-11  7:27 UTC (permalink / raw)
  To: eric.auger, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Jean-Philippe Brucker, Mostafa Saleh,
	CLEMENT MATHIEU--DRIF

Hi Eric,

On 2025/12/11 02:35, Eric Auger wrote:
>
> On 11/26/25 4:45 PM, Tao Tang wrote:
>> Add a minimal PCI test device designed to exercise IOMMU translation
>> (such as ARM SMMUv3) without requiring guest firmware or OS. The device
>> provides MMIO registers to configure and trigger DMA operations with
>> controllable attributes (security state, address space), enabling
>> deterministic IOMMU testing.
>>
>> Key features:
>> - Bare-metal IOMMU testing via simple MMIO interface
>> - Configurable DMA attributes for security states and address spaces
>> - Write-then-read verification pattern with automatic result checking
>>
>> The device performs a deterministic DMA test pattern: write a known
>> value (0x88888888) to a configured IOVA, read it back, and verify data
>> integrity. Results are reported through a dedicated result register,
>> eliminating the need for complex interrupt handling or driver
>> infrastructure in tests.
>>
>> This is purely a test device and not intended for production use or
>> machine realism. It complements existing test infrastructure like
>> pci-testdev but focuses specifically on IOMMU translation path
>> validation.
>>
>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>> ---
>>   docs/specs/index.rst            |   1 +
>>   docs/specs/iommu-testdev.rst    | 109 +++++++++++++
>>   hw/misc/Kconfig                 |   5 +
>>   hw/misc/iommu-testdev.c         | 278 ++++++++++++++++++++++++++++++++
>>   hw/misc/meson.build             |   1 +
>>   hw/misc/trace-events            |  10 ++
>>   include/hw/misc/iommu-testdev.h |  70 ++++++++
>>   7 files changed, 474 insertions(+)
>>   create mode 100644 docs/specs/iommu-testdev.rst
>>   create mode 100644 hw/misc/iommu-testdev.c
>>   create mode 100644 include/hw/misc/iommu-testdev.h
>>
>> diff --git a/docs/specs/index.rst b/docs/specs/index.rst
>> index f19d73c9f6..1fc7fae6bb 100644
>> --- a/docs/specs/index.rst
>> +++ b/docs/specs/index.rst
>> @@ -39,3 +39,4 @@ guest hardware that is specific to QEMU.
>>      riscv-iommu
>>      riscv-aia
>>      aspeed-intc
>> +   iommu-testdev
>> \ No newline at end of file
>> diff --git a/docs/specs/iommu-testdev.rst b/docs/specs/iommu-testdev.rst
>> new file mode 100644
>> index 0000000000..fdc7f2ee89
>> --- /dev/null
>> +++ b/docs/specs/iommu-testdev.rst
>> @@ -0,0 +1,109 @@
>> +iommu-testdev — IOMMU test device for bare-metal testing
>> +=========================================================
>> +
>> +Overview
>> +--------
>> +``iommu-testdev`` is a minimal, test-only PCI device designed to exercise
>> +IOMMU translation (such as ARM SMMUv3) without requiring firmware or a guest
>> +OS. Tests can populate IOMMU translation tables with known values and trigger
>> +DMA operations that flow through the IOMMU translation path. It is **not** a
>> +faithful PCIe endpoint and must be considered a QEMU-internal test vehicle.
>> +
>> +Key Features
>> +------------
>> +* **Bare-metal IOMMU testing**: No guest kernel or firmware required
>> +* **Configurable DMA attributes**: Supports address space  configuration via
> double space


Thanks for catching it.

>> +  MMIO registers
>> +* **Deterministic verification**: Write-then-read DMA pattern with automatic
>> +  result checking
>> +
>> +Status
>> +------
>> +* Location: ``hw/misc/iommu-testdev.c``
>> +* Header: ``include/hw/misc/iommu-testdev.h``
>> +* Build guard: ``CONFIG_IOMMU_TESTDEV``
>> +
>> +Device Interface
>> +----------------
>> +The device exposes a single PCI BAR0 with MMIO registers:
> all those regs are 32b?


Yes. All registers are 32-bit, little-endian, and only support 4-byte 
accesses, as enforced by the MemoryRegionOps valid range. I’ll make this 
explicit in  the docs and in the register table to avoid any ambiguity.

>> +
>> +* ``ITD_REG_DMA_TRIGGERING`` (0x00): Reading triggers DMA execution
>> +* ``ITD_REG_DMA_GVA_LO`` (0x04): IOVA/GVA bits [31:0]
>> +* ``ITD_REG_DMA_GVA_HI`` (0x08): IOVA/GVA bits [63:32]
>> +* ``ITD_REG_DMA_LEN`` (0x0C): DMA transfer length
>> +* ``ITD_REG_DMA_RESULT`` (0x10): DMA operation result (0=success)
>> +* ``ITD_REG_DMA_DBELL`` (0x14): Write 1 to arm DMA
> can you elaborate on this DMA_DBELL? It is not crystal clear to me.


The idea is to model a tiny, explicit DMA state machine instead of 
having a single MMIO operation that implicitly does “configure + start + 
complete”.

In this model:

   * DMA_DBELL is just an “arm / doorbell” register.
   * DMA_TRIGGERING is the “consume / execute” action.
   * DMA_RESULT exposes the state/outcome.

This split is mainly for timing control and debuggability: qtests can 
easily exercise and assert distinct paths (NOT_ARMED, BAD_LEN, TX/RD 
failures, mismatch) instead of having all side effects hidden behind a 
single write.


If you feel this is overkill for a qtest-only device, I'm open to 
simplifying the interface (e.g. "write a register and immediately 
execute DMA").

>> +* ``ITD_REG_DMA_ATTRS`` (0x18): DMA attributes
>> +
>> +  - bit[0]: secure (1=Secure, 0=Non-Secure)
>> +  - bits[2:1]: address space (0=Non-Secure, 1=Secure, 2=Root, 3=Realm)
> I would simply remove Root and Realm for now. Are those bits part of
>
> ITD_REG_DMA_ATTR?


Yes I just shadowed the MemTxAttrs. I'll remove the unused Root/Realm 
definition.

>
>> +    Only these MemTxAttrs fields (``secure`` and ``space``) are consumed today;
>> +    other bits are reserved but can be wired up easily if future tests need
>> +    to pass extra attributes.
>> +
>> +Translation Setup Workflow
>> +--------------------------
>> +``iommu-testdev`` never builds SMMU/AMD-Vi/RISC-V IOMMU structures on its own.
>> +Architecture-specific construction lives entirely in qtest/libqos helpers.
>> +Those helpers populate guest memory with page tables/architecture-specific
>> +structures and program the emulated IOMMU registers directly. See the
>> +``qsmmu_setup_and_enable_translation()`` function in
>> +``tests/qtest/libqos/qos-smmuv3.c`` for an example of how SMMUv3 translation
>> +is set up for this device.
>> +
>> +DMA Operation Flow
>> +------------------
>> +1. Test programs IOMMU translation tables
>> +2. Test configures DMA address (GVA_LO/HI), length, and attributes
>> +3. Test writes 1 to DMA_DBELL to arm the operation
>> +4. Test reads DMA_TRIGGERING to execute DMA
> why do we need 3 and 4?


As previously described.

>> +5. Test polls DMA_RESULT:
>> +
>> +   - 0x00000000: Success
>> +   - 0xFFFFFFFE: Busy (still in progress)
>> +   - 0xDEAD000X: Various error codes
>> +
>> +The device performs a write-then-read sequence using a known pattern
>> +(0x88888888) and verifies data integrity automatically.
>> +
>> +Running the qtest
>> +-----------------
>> +The SMMUv3 test suite uses this device and covers multiple translation modes::
>> +
>> +    cd build-debug
>> +    QTEST_QEMU_BINARY=./qemu-system-aarch64 \\
>> +        ./tests/qtest/iommu-smmuv3-test --tap -k
>> +
>> +This test suite exercises:
>> +
>> +* Stage 1 only translation
>> +* Stage 2 only translation
>> +* Nested (Stage 1 + Stage 2) translation
>> +* Multiple security spaces (Non-Secure, Secure, Root, Realm)
> ditto


I'll remove it.

>> +
>> +Instantiation
>> +-------------
>> +The device is not wired into any board by default. Tests instantiate it
>> +via QEMU command line::
>> +
>> +    -device iommu-testdev
>> +
>> +For ARM platforms with SMMUv3::
>> +
>> +    -M virt,iommu=smmuv3 -device iommu-testdev
> does it work -device arm-smmuv3 multi instantiation too?


No. It only supports machine-wide SMMU (`iommu=smmuv3`) as SMMU base 
address is hardcoded with 0x9050000 in tests/qtest/libqos/qos-smmuv3.h. 
Should we support user-creatable SMMUv3 in this testdev?

>> +
>> +The device will be placed behind the IOMMU automatically.
>> +
>> +Limitations
>> +-----------
>> +* No realistic PCIe enumeration, MSI/MSI-X, or interrupt handling
>> +* No ATS/PRI support
>> +* No actual device functionality beyond DMA test pattern
>> +* Test-only; not suitable for production or machine realism
>> +* Address space support (Secure/Root/Realm) is architecture-dependent
>> +
>> +See also
>> +--------
>> +* ``tests/qtest/iommu-smmuv3-test.c`` — SMMUv3 test suite
>> +* ``tests/qtest/libqos/qos-smmuv3.{c,h}`` — SMMUv3 test library
>> +* SMMUv3 emulation: ``hw/arm/smmu*``
>> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
>> index fccd735c24..b5f6fdbd9c 100644
>> --- a/hw/misc/Kconfig
>> +++ b/hw/misc/Kconfig
>> @@ -25,6 +25,11 @@ config PCI_TESTDEV
>>       default y if TEST_DEVICES
>>       depends on PCI
>>   
>> +config IOMMU_TESTDEV
>> +    bool
>> +    default y if TEST_DEVICES
>> +    depends on PCI
>> +
>>   config EDU
>>       bool
>>       default y if TEST_DEVICES
>> diff --git a/hw/misc/iommu-testdev.c b/hw/misc/iommu-testdev.c
>> new file mode 100644
>> index 0000000000..3182ccea4d
>> --- /dev/null
>> +++ b/hw/misc/iommu-testdev.c
>> @@ -0,0 +1,278 @@
>> +/*
>> + * A test device for IOMMU
>> + *
>> + * This test device is a minimal IOMMU-aware device used to test the IOMMU.
> how is it IOMMU aware? Isn't it "simply" a kinda PCIe DMA device
>
> it behaves as other PCIe devices with s->dma_as = pci_device_iommu_address_space(pdev);
> no?


Sorry for the confusing wording. The device is not really "IOMMU-aware": 
it is just a PCI DMA engine, and the tests are responsible 
for programming the emulated IOMMU and wiring the device behind it, not 
the iommu-testdev itself.


Thanks again for the review,
Tao



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev
  2025-12-10 18:45   ` Eric Auger
@ 2025-12-11  8:06     ` Tao Tang
  0 siblings, 0 replies; 23+ messages in thread
From: Tao Tang @ 2025-12-11  8:06 UTC (permalink / raw)
  To: eric.auger, Paolo Bonzini, Fabiano Rosas, Laurent Vivier,
	Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Pierrick Bouvier,
	Philippe Mathieu-Daudé, Jean-Philippe Brucker, Mostafa Saleh,
	CLEMENT MATHIEU--DRIF

Hi Eric,

On 2025/12/11 02:45, Eric Auger wrote:
>
> On 11/26/25 4:45 PM, Tao Tang wrote:
>> Add a qtest suite that validates ARM SMMUv3 translation without guest
>> firmware or OS. The tests leverage iommu-testdev to trigger DMA
>> operations and the qos-smmuv3 library to configure IOMMU translation
>> structures.
>>
>> This test suite targets the virt machine and covers:
>> - Stage 1 only translation (VA -> PA via CD page tables)
>> - Stage 2 only translation (IPA -> PA via STE S2 tables)
>> - Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)
>> - Design to extended to support multiple security spaces
>>      (Non-Secure, Secure, Root, Realm)
>>
>> Each test case follows this sequence:
>> 1. Initialize SMMUv3 with appropriate command/event queues
>> 2. Build translation tables (STE/CD/PTE) for the target scenario
>> 3. Configure iommu-testdev with IOVA and DMA attributes via MMIO
>> 4. Trigger DMA and validate successful translation
>> 5. Verify data integrity through a deterministic write-read pattern
>>
>> This bare-metal approach provides deterministic IOMMU testing with
>> minimal dependencies, making failures directly attributable to the SMMU
>> translation path.
>>
>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>> ---
>>   tests/qtest/iommu-smmuv3-test.c | 114 ++++++++++++++++++++++++++++++++
>>   tests/qtest/meson.build         |   1 +
>>   2 files changed, 115 insertions(+)
>>   create mode 100644 tests/qtest/iommu-smmuv3-test.c
>>
>> diff --git a/tests/qtest/iommu-smmuv3-test.c b/tests/qtest/iommu-smmuv3-test.c
>> new file mode 100644
>> index 0000000000..af438ecce0
>> --- /dev/null
>> +++ b/tests/qtest/iommu-smmuv3-test.c
>> @@ -0,0 +1,114 @@
>> +/*
>> + * QTest for SMMUv3 with iommu-testdev
>> + *
>> + * This QTest file is used to test the SMMUv3 with iommu-testdev so that we can
>> + * test SMMUv3 without any guest kernel or firmware.
>> + *
>> + * Copyright (c) 2025 Phytium Technology
>> + *
>> + * Author:
>> + *  Tao Tang <tangtao1634@phytium.com.cn>
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "libqtest.h"
>> +#include "libqos/pci.h"
>> +#include "libqos/generic-pcihost.h"
>> +#include "hw/pci/pci_regs.h"
>> +#include "hw/misc/iommu-testdev.h"
>> +#include "libqos/qos-smmuv3.h"
>> +
>> +#define DMA_LEN           4
>> +
>> +/* Test configurations for different SMMU modes and spaces */
>> +static const QSMMUTestConfig base_test_configs[] = {
>> +    {
>> +        .trans_mode = QSMMU_TM_S1_ONLY,
>> +        .sec_sid = QSMMU_SEC_SID_NONSECURE,
>> +        .dma_iova = QSMMU_IOVA_OR_IPA,
> what does this mean? QSMMU_IOVA_OR_IPA
>
> Eric


`QSMMU_IOVA_OR_IPA` was intended to represent the address that is seen 
as the *input* to the SMMU in the test. In other words:

     - for the S1-only and nested cases, this value is used as the IOVA, and
     - for the S2-only case, the same numeric value is used as the IPA.

So conceptually it is “the address fed into the IOMMU”, and the actual 
interpretation depends on `trans_mode`.

I agree the name `IOVA_OR_IPA` is confusing and does not make this model 
very clear.


Maybe we could use `QSMMU_DMA_INPUT_ADDR`  instead?


Thanks for catching this.

Tao




^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library
  2025-12-10 18:40           ` Eric Auger
@ 2025-12-11  8:53             ` Tao Tang
  0 siblings, 0 replies; 23+ messages in thread
From: Tao Tang @ 2025-12-11  8:53 UTC (permalink / raw)
  To: eric.auger, Pierrick Bouvier, Paolo Bonzini, Fabiano Rosas,
	Laurent Vivier, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF


On 2025/12/11 02:40, Eric Auger wrote:
> Hi Tao,
>
> On 12/6/25 6:27 AM, Tao Tang wrote:
>> On 2025/12/6 01:19, Pierrick Bouvier wrote:
>>> On 12/5/25 7:03 AM, Tao Tang wrote:
>>>> Hi Pierrick,
>>>>
>>>> On 2025/12/5 07:53, Pierrick Bouvier wrote:
>>>>> On 11/26/25 7:45 AM, Tao Tang wrote:
>>>>>> Introduce qos-smmuv3, a reusable library for SMMUv3-related qtest
>>>>>> operations. This module encapsulates common tasks like:
>>>>>>
>>>>>> - SMMUv3 initialization (enabling, configuring command/event queues)
>>>>>> - Stream Table Entry (STE) and Context Descriptor (CD) setup
>>>>>> - Multi-level page table construction (L0-L3 for 4KB granules)
>>>>>> - Support for Stage 1, Stage 2, and nested translation modes
>>>>>> - Could be easily extended to support multi-space testing
>>>>>> infrastructure
>>>>>>        (Non-Secure, Secure, Root, Realm)
>>>>>>
>>>>>> The library provides high-level abstractions that allow test code to
>>>>>> focus on IOMMU behavior validation rather than low-level register
>>>>>> manipulation and page table encoding. Key features include:
>>>>>>
>>>>>> - Automatic memory allocation for translation structures with proper
>>>>>>        alignment
>>>>>> - Helper functions to build valid STEs/CDs for different translation
>>>>>>        scenarios
>>>>>> - Page table walkers that handle address offset calculations per
>>>>>>        security space
>>>>>> - Command queue management for SMMU configuration commands
>>>>>>
>>>>>> This infrastructure is designed to be used by iommu-testdev-based
>>>>>> tests
>>>>>> and future SMMUv3 test suites, reducing code duplication and
>>>>>> improving
>>>>>> test maintainability.
>>>>>>
>>>>>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>>>>>> ---
>>>>>>     tests/qtest/libqos/meson.build  |   3 +
>>>>>>     tests/qtest/libqos/qos-smmuv3.c | 731
>>>>>> ++++++++++++++++++++++++++++++++
>>>>>>     tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
>>>>>>     3 files changed, 1001 insertions(+)
>>>>>>     create mode 100644 tests/qtest/libqos/qos-smmuv3.c
>>>>>>     create mode 100644 tests/qtest/libqos/qos-smmuv3.h
>>>>>>
>>>>> ...
>>>>>
>>>>>> +
>>>>>> +void qsmmu_single_translation(QSMMUTestContext *ctx)
>>>>>> +{
>>>>>> +    uint32_t config_result;
>>>>>> +    uint32_t dma_result;
>>>>>> +    bool test_passed;
>>>>>> +
>>>>>> +    /* Configure SMMU translation */
>>>>>> +    config_result = qsmmu_setup_and_enable_translation(ctx);
>>>>>> +    if (config_result != 0) {
>>>>>> +        g_test_message("Configuration failed: mode=%u status=0x%x",
>>>>>> +                       ctx->config.trans_mode, config_result);
>>>>>> +        return;
>>>>> Is that expected to silently return if we can't configure translation?
>>>>
>>>> No, it is not intended to silently return on a failed configuration.
>>>> Maybe an assertion is a better choice:
>>>>
>>>>
>>>> config_result = qsmmu_setup_and_enable_translation(ctx);
>>>>
>>>> g_assert_cmpuint(config_result, ==, 0);
>>>>
>>> Looks good. We should rely on exit code first, and then on verbose
>>> log to find what is the problem.
>>>
>>>>>> +    }
>>>>>> +
>>>>>> +    /* Trigger DMA operation */
>>>>>> +    dma_result = qsmmu_trigger_dma(ctx);
>>>>>> +    if (dma_result != 0) {
>>>>>> +        g_test_message("DMA failed: mode=%u result=0x%x",
>>>>>> +                       ctx->config.trans_mode, dma_result);
>>>>>> +    } else {
>>>>>> +        g_test_message("-> DMA succeeded: mode=%u",
>>>>>> ctx->config.trans_mode);
>>>>>> +    }
>>>>>> +
>>>>>> +    /* Validate test result */
>>>>>> +    test_passed = qsmmu_validate_test_result(ctx);
>>>>>> +    g_assert_true(test_passed);
>>>>>> +
>>>>>> +    /* Clean up translation state to prepare for the next test */
>>>>>> +    qsmmu_cleanup_translation(ctx);
>>>>>> +}
>>>>>> +
>>>>>> +void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t
>>>>>> count,
>>>>>> +                             QTestState *qts, QPCIDevice *dev,
>>>>>> +                             QPCIBar bar, uint64_t smmu_base)
>>>>>> +{
>>>>>> +    for (int i = 0; i < count; i++) {
>>>>>> +        /* Initialize test memory */
>>>>>> +        qtest_memset(qts, configs[i].dma_iova, 0x00,
>>>>>> configs[i].dma_len);
>>>>>> +        /* Execute each test configuration */
>>>>>> +        QSMMUTestContext ctx = {
>>>>>> +            .qts = qts,
>>>>>> +            .dev = dev,
>>>>>> +            .bar = bar,
>>>>>> +            .smmu_base = smmu_base,
>>>>>> +            .config = configs[i],
>>>>>> +            .trans_status = 0,
>>>>>> +            .dma_result = 0,
>>>>>> +            .sid = dev->devfn,
>>>>>> +            .tx_space = qsmmu_sec_sid_to_space(configs[i].sec_sid),
>>>>>> +        };
>>>>>> +
>>>>>> +        qsmmu_single_translation(&ctx);
>>>>>> +        g_test_message("--> Test %d completed: mode=%u sec_sid=%u "
>>>>>> +                       "status=0x%x result=0x%x", i,
>>>>>> configs[i].trans_mode,
>>>>>> +                       configs[i].sec_sid, ctx.trans_status,
>>>>>> ctx.dma_result);
>>>>>> +    }
>>>>>> +}
>>>>> What is the reason for batching operations?
>>>>> We are not in a performance critical scenario for running this test,
>>>>> so it's probably better to have distinct calls to single_translation.
>>>>
>>>> As described in the previous thread [1] , I plan to split the tests so
>>>> that each translation mode is exercised by its own qtest. With that
>>>> split in place, there is no real need for a qsmmu_translation_batch()
>>>> helper anymore, so I refactor it into a qsmmu_run_translation_case
>>>> function and drop the inside for-loop.
>>>>
>>> All good, indeed removes the need for translation_batch.
>>>
>>>> [1]
>>>> https://lore.kernel.org/qemu-devel/7370070a-c569-4b77-bd1e-6fc749ba9c90@phytium.com.cn/
>>>>
>>>>
>>>>> ...
>>>>>
>>>>> For the rest of the patch, which is quite consequent, congrats. It's
>>>>> hard to review all the setup phase here, but knowing it works with the
>>>>> current smmuv3 implementation, that's a good proof that it's working
>>>>> as expected.
>>>>
>>>> Yes, setting up all this infrastructure did take some time, especially
>>>> getting the nested mode page tables right (and Secure state-related
>>>> configuration which is still in my local repo).
>>>>
>>> Feel free to start with the current version, and then you'll add
>>> secure state related changes as part of your other series.
>>>
>>>> I really appreciate that you ran the tests yourself and even checked
>>>> with a coverage-enabled build to confirm that it exercises the smmuv3
>>>> implementation. Thanks again for the thorough review.
>>>>
>>> In case someone else wants to reproduce:
>>> $ export CFLAGS="--coverage"
>>> $ ./configure --target-list=aarch64-softmmu
>>> $ ninja -C build
>>> $ QTEST_QEMU_BINARY=./build/qemu-system-aarch64 \
>>>    ./build/tests/qtest/iommu-smmuv3-test
>>> $ rm -rf build/coverage_html
>>> $ mkdir build/coverage_html
>>> $ gcovr \
>>>        --gcov-ignore-parse-errors suspicious_hits.warn \
>>>        --gcov-ignore-parse-errors negative_hits.warn \
>>>        --merge-mode-functions=separate \
>>>        --html-details build/coverage_html/index.html \
>>>        --filter 'hw/arm/smmu*'
>>> $ echo file://$(pwd)/build/coverage_html/index.html
>>> # open this in browser by clicking on your terminal
>>>
>>> If useful for you, you can attach those instructions in your next
>>> cover letter, so people can easily reproduce.
> are you ready to maintain that code (esp the lib)? You shall add an
> entry in the MAINTAINERS file for those new files I guess.
>
> Eric


Yes, I'm ready to maintain this code, especially the new libqos helper. 
Just to make sure I understood you correctly: were you thinking about
something along the lines of the following?


qtest

.....

.....

QTest SMMUv3 helpers
M: Eric Auger <eric.auger@redhat.com>
M: Tao Tang <tangtao1634@phytium.com.cn>
L: qemu-arm@nongnu.org
S: Maintained
F: hw/misc/iommu-testdev.c
F: include/hw/misc/iommu-testdev.h
F: tests/qtest/libqos/qos-smmuv3.h
F: tests/qtest/iommu-smmuv3-test.c
F: tests/qtest/libqos/qos-smmuv3.c

Device Fuzzing

.....

.....



Alternatively, we could also keep iommu-testdev under your existing 
SMMUv3 section and add a smaller "SMMUv3 qtest helpers" section with 
only the qtest/libqos files and myself as maintainer.

I'm happy to go with whichever layout you prefer.

Best regards,
Tao



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library
  2025-12-10 18:43     ` Eric Auger
@ 2025-12-11  9:39       ` Tao Tang
  0 siblings, 0 replies; 23+ messages in thread
From: Tao Tang @ 2025-12-11  9:39 UTC (permalink / raw)
  To: eric.auger, Pierrick Bouvier, Paolo Bonzini, Fabiano Rosas,
	Laurent Vivier, Peter Maydell, Alex Bennée
  Cc: qemu-devel, qemu-arm, Chen Baozi, Philippe Mathieu-Daudé,
	Jean-Philippe Brucker, Mostafa Saleh, CLEMENT MATHIEU--DRIF

Hi Eric,

On 2025/12/11 02:43, Eric Auger wrote:
>
> On 12/5/25 12:53 AM, Pierrick Bouvier wrote:
>> On 11/26/25 7:45 AM, Tao Tang wrote:
>>> Introduce qos-smmuv3, a reusable library for SMMUv3-related qtest
>>> operations. This module encapsulates common tasks like:
>>>
>>> - SMMUv3 initialization (enabling, configuring command/event queues)
>>> - Stream Table Entry (STE) and Context Descriptor (CD) setup
>>> - Multi-level page table construction (L0-L3 for 4KB granules)
>>> - Support for Stage 1, Stage 2, and nested translation modes
>>> - Could be easily extended to support multi-space testing infrastructure
>>>       (Non-Secure, Secure, Root, Realm)
>>>
>>> The library provides high-level abstractions that allow test code to
>>> focus on IOMMU behavior validation rather than low-level register
>>> manipulation and page table encoding. Key features include:
>>>
>>> - Automatic memory allocation for translation structures with proper
>>>       alignment
>>> - Helper functions to build valid STEs/CDs for different translation
>>>       scenarios
>>> - Page table walkers that handle address offset calculations per
>>>       security space
>>> - Command queue management for SMMU configuration commands
>>>
>>> This infrastructure is designed to be used by iommu-testdev-based tests
>>> and future SMMUv3 test suites, reducing code duplication and improving
>>> test maintainability.
>>>
>>> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
>>> ---
>>>    tests/qtest/libqos/meson.build  |   3 +
>>>    tests/qtest/libqos/qos-smmuv3.c | 731 ++++++++++++++++++++++++++++++++
>>>    tests/qtest/libqos/qos-smmuv3.h | 267 ++++++++++++
>>>    3 files changed, 1001 insertions(+)
>>>    create mode 100644 tests/qtest/libqos/qos-smmuv3.c
>>>    create mode 100644 tests/qtest/libqos/qos-smmuv3.h
>>>
>> ...
>>
>>> +
>>> +void qsmmu_single_translation(QSMMUTestContext *ctx)
>>> +{
>>> +    uint32_t config_result;
>>> +    uint32_t dma_result;
>>> +    bool test_passed;
>>> +
>>> +    /* Configure SMMU translation */
>>> +    config_result = qsmmu_setup_and_enable_translation(ctx);
>>> +    if (config_result != 0) {
>>> +        g_test_message("Configuration failed: mode=%u status=0x%x",
>>> +                       ctx->config.trans_mode, config_result);
>>> +        return;
>> Is that expected to silently return if we can't configure translation?
>>
>>> +    }
>>> +
>>> +    /* Trigger DMA operation */
>>> +    dma_result = qsmmu_trigger_dma(ctx);
>>> +    if (dma_result != 0) {
>>> +        g_test_message("DMA failed: mode=%u result=0x%x",
>>> +                       ctx->config.trans_mode, dma_result);
>>> +    } else {
>>> +        g_test_message("-> DMA succeeded: mode=%u",
>>> ctx->config.trans_mode);
>>> +    }
>>> +
>>> +    /* Validate test result */
>>> +    test_passed = qsmmu_validate_test_result(ctx);
>>> +    g_assert_true(test_passed);
>>> +
>>> +    /* Clean up translation state to prepare for the next test */
>>> +    qsmmu_cleanup_translation(ctx);
>>> +}
>>> +
>>> +void qsmmu_translation_batch(const QSMMUTestConfig *configs, size_t
>>> count,
>>> +                             QTestState *qts, QPCIDevice *dev,
>>> +                             QPCIBar bar, uint64_t smmu_base)
>>> +{
>>> +    for (int i = 0; i < count; i++) {
>>> +        /* Initialize test memory */
>>> +        qtest_memset(qts, configs[i].dma_iova, 0x00,
>>> configs[i].dma_len);
>>> +        /* Execute each test configuration */
>>> +        QSMMUTestContext ctx = {
>>> +            .qts = qts,
>>> +            .dev = dev,
>>> +            .bar = bar,
>>> +            .smmu_base = smmu_base,
>>> +            .config = configs[i],
>>> +            .trans_status = 0,
>>> +            .dma_result = 0,
>>> +            .sid = dev->devfn,
>>> +            .tx_space = qsmmu_sec_sid_to_space(configs[i].sec_sid),
>>> +        };
>>> +
>>> +        qsmmu_single_translation(&ctx);
>>> +        g_test_message("--> Test %d completed: mode=%u sec_sid=%u "
>>> +                       "status=0x%x result=0x%x", i,
>>> configs[i].trans_mode,
>>> +                       configs[i].sec_sid, ctx.trans_status,
>>> ctx.dma_result);
>>> +    }
>>> +}
>> What is the reason for batching operations?
>> We are not in a performance critical scenario for running this test,
>> so it's probably better to have distinct calls to single_translation.
>>
>> ...
>>
>> For the rest of the patch, which is quite consequent, congrats. It's
>> hard to review all the setup phase here, but knowing it works with the
>> current smmuv3 implementation, that's a good proof that it's working
>> as expected.
> That's a huge amount of code indeed. I don't how much we shall review
> this test lib but if it needs std review the patch needs to be split to
> ease the review. Maybe qtest maintainers can give some guidelines here...
>
> Eric


I agree that the current qos-smmuv3 patch is too big for a single 
review. When implementing this library I actually spent quite some time 
debugging it, especially getting the page tables right, which is part of 
why it initially came as one large chunk.

I plan to split it into several smaller patches along functional boundaries:

- one for the basic structures and common helpers, then separate patches 
for
- Stage-1,
- Stage-2,
- nested translation support,
- for the more complex nested case I can further split the changes if 
that makes the review easier.

I would very much appreciate qtest maintainers' professional advices on 
the overall shape and layering of this test library before I start 
splitting it up.


Best regards,

Tao



^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2025-12-11  9:40 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-26 15:45 [RFC RESEND v5 0/4] hw/misc: Introduce a generalized IOMMU test framework Tao Tang
2025-11-26 15:45 ` [RFC RESEND v5 1/4] hw/arm/smmuv3: Extract common definitions to smmuv3-common.h Tao Tang
2025-12-04 18:19   ` Pierrick Bouvier
2025-11-26 15:45 ` [RFC RESEND v5 2/4] hw/misc: Introduce iommu-testdev for bare-metal IOMMU testing Tao Tang
2025-12-04 18:36   ` Pierrick Bouvier
2025-12-10 18:35   ` Eric Auger
2025-12-11  7:27     ` Tao Tang
2025-11-26 15:45 ` [RFC RESEND v5 3/4] tests/qtest/libqos: Add SMMUv3 helper library Tao Tang
2025-12-04 23:53   ` Pierrick Bouvier
2025-12-05 15:03     ` Tao Tang
2025-12-05 17:19       ` Pierrick Bouvier
2025-12-06  5:27         ` Tao Tang
2025-12-10 18:40           ` Eric Auger
2025-12-11  8:53             ` Tao Tang
2025-12-10 18:43     ` Eric Auger
2025-12-11  9:39       ` Tao Tang
2025-11-26 15:45 ` [RFC RESEND v5 4/4] tests/qtest: Add SMMUv3 bare-metal test using iommu-testdev Tao Tang
2025-12-04 18:42   ` Pierrick Bouvier
2025-12-05 14:19     ` Tao Tang
2025-12-05 17:06       ` Pierrick Bouvier
2025-12-06  4:54         ` Tao Tang
2025-12-10 18:45   ` Eric Auger
2025-12-11  8:06     ` Tao Tang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).