iommu-testdev — IOMMU test device for bare-metal testing — QEMU documentation

# 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 32-bit MMIO registers:

- `ITD_REG_DMA_TRIGGERING` (0x00): Read triggers DMA and consumes the armed request

- `ITD_REG_DMA_GVA_LO` (0x04): DMA IOVA bits [31:0]

- `ITD_REG_DMA_GVA_HI` (0x08): DMA IOVA bits [63:32]

- `ITD_REG_DMA_GPA_LO` (0x1C): DMA GPA bits [31:0] for readback validation

- `ITD_REG_DMA_GPA_HI` (0x20): DMA GPA bits [63:32] for readback validation

- `ITD_REG_DMA_LEN` (0x0C): DMA transfer length

- `ITD_REG_DMA_RESULT` (0x10): DMA result (0=success, 0xffffffff=idle, 0xfffffffe=armed)

- `ITD_REG_DMA_DBELL` (0x14): Write 1 to arm DMA, write 0 to disarm. Arming only marks the request and sets BUSY (no latch/check), but it provides an explicit gate for qtests and leaves room for async/latching.

- `ITD_REG_DMA_ATTRS` (0x18): DMA attributes which shadow some fields in MemTxAttrs:

  - bit[0]: secure (1=Secure, 0=Non-Secure)

  - bits[2:1]: ArmSecuritySpace (0=Secure, 1=Non-Secure)

  - bit[3]: space_valid (1=space is valid, 0=ignore space and default to Non-Secure) `space` field in MemTxAttrs is consumed only when `space_valid` is set. For Secure/Non-Secure, `secure` and `space` must match; mismatches return `ITD_DMA_ERR_BAD_ATTRS`. 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

Arming semantics:

- Writing `DMA_DBELL` with bit0=1 marks the request armed and sets `DMA_RESULT` to BUSY. It does not latch GVA/LEN/ATTRS; values are sampled when `DMA_TRIGGERING` is read.

- Writing `DMA_DBELL` with bit0=0 disarms the request and sets `DMA_RESULT` to IDLE.

- Reading `DMA_TRIGGERING` consumes the armed request and clears the armed state, even on error.

The flow would be split into these steps, 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 step: 1. Test programs IOMMU translation tables 2. Test configures DMA IOVA (GVA_LO/HI), GPA for readback, 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: Armed (waiting for trigger). DMA runs synchronously, so BUSY is not observed once the trigger read completes.

> - 0xDEAD0006: Bad attrs (secure/space mismatch for S/NS)

> - 0xDEAD000X: Various error codes

The device performs a write-then-read sequence using a known pattern (0x12345678) and verifies data integrity automatically.

## Running the qtest

The SMMUv3 test suite uses this device and covers multiple translation modes:

    cd build
    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

## 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

When the IOMMU sits on the same PCI root complex (`pci.0`), the device is placed behind it automatically. For other PCI topologies, specify the bus explicitly.

## 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 and gated by `space_valid`

- Readback uses the programmed GPA and reads via system memory, avoiding a second IOMMU access for the readback step

## 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*`