ASAravind Sivakumaran
Theme

Theme and appearance

Appearance
Palette

Pick any colour. The displayed accent adjusts for readable contrast in light or dark mode.

← IOMMU star map

IOMMU / INTERACTIVE WORKING MODEL

IOMMU translation & DMA validation.

Trace a device request from its address space to memory—or to a fault.

AMD-style semantic model · up to four guest levels · configurable host levels · 4 KiB / 2 MiB / 1 GiB leaves · a synthetic Ethernet NIC and selected raw entry encodings.

Start with the translation path New to this? Follow one prepared request first →
What claims does this page make? 3 points
New to IOMMU? Start here

An IOMMU controls which memory a device may access. Begin with the default mapping, select Translate, then step through DMA. Compare the two fault examples to see why an invalid page copies no bytes.

DMA
Direct Memory Access: a device reads or writes system memory.
PASID
Process Address Space ID: selects an address space for a device request; it is not itself a memory address or a VM identifier.
GVA / GPA / SPA
Guest Virtual / Guest Physical / System Physical Address. Guest Only ends at GPA = SPA; nested translation adds a host mapping.
DTE / GCR3
The Device Table Entry holds translation controls. The GCR3 tables select a guest page-table root using the PASID.
Leaf / offset
A leaf entry names the final page. The offset selects a byte within that page. Larger pages end the walk earlier.
ATS / IRTE
Address Translation Services queries a translation. An Interrupt Remapping Table Entry routes a device interrupt; it does not translate data addresses.

START HERE

01 / Choose the translation path

A device sends a memory address. The IOMMU checks a device entry (DTE), follows a set of lookup tables, and either finds an allowed memory location or stops the request. A page is a mapped block of 4 KiB, 2 MiB or 1 GiB; a page entry is an 8-byte record pointing to another table or the final block.

Which path would I use?

Host only

Give a device its own DMA address space. Host tables map a device GPA / IOVA to RAM and enforce isolation. No PASID guest lookup is needed.

V=1 · TV=1 · GV=0 · Mode=1–6

Guest only

Use a PASID-selected address space without a second physical remap. Guest tables map GVA to GPA, and GPA already is SPA.

V=1 · TV=1 · GV=1 · Mode=0

Guest + host

Give a process inside a VM a virtual address space, while the host controls which RAM the VM may use. Guest-table memory also needs host translation.

V=1 · TV=1 · GV=1 · Mode=1–6

These presets assume the required hardware features and global enables. “Guest” names the first translation stage; it does not mean a VM is required for every use. Effective PASID width is the smallest of the PCIe device capability, the IOMMU’s PASmax and its GCR3 capacity. The device capability is separate from the DTE.

02 / Build a mapping

Runs locally in your browser

Choose up to eight PASID contexts. Each maps the chosen device virtual page to a separate GPA and system physical page, illustrating separate address spaces. With mixed page sizes, only the region covered by both stages is accessible. This demonstration is not a hardware isolation test or proof.

Mapped frame bases must align to their leaf size. A DMA address may include any byte offset within that mapping; it does not need to be page aligned. Table pages in this model remain 4 KiB aligned.

Alignment and AMD 48882 details · Compare Arm SMMUv3

Skipped levels retain the original address bit positions; they are not shifted or packed together. A data leaf cannot be skipped. Nested Mode 1 is not offered by this lab because its guest tables are allocated above 2 MiB; the architecture itself permits Mode 1.

How mixed page sizes are mapped

Each PASID has one guest data leaf and one host data leaf. If the host page is smaller, only the host-sized region containing the configured guest address is mapped. Page-table storage always uses 4 KiB blocks. Guest Only bypasses the host stage; Host Only bypasses the guest stage.

How the device and IOMMU limits combine

The PCIe PASID capability describes the device’s maximum PASID width. GLX selects the IOMMU’s GCR3 table capacity. This demo uses the smaller range as a software guard against aliases. With a 16-bit NIC and GLX=1, the effective capacity is 65,536 PASIDs (IDs 0–65,535), even though the two-level GCR3 structure can represent more.

GLX capacity: 0 → 9 bits; 1 → 18 bits; 2 → 20 bits. The demo assumes the IOMMU hardware supports the selected GLX and a 20-bit PASID ceiling. A platform may impose further limits. AMD 48882 §2.2.6.3 specifies that GLX=0 ignores PASID bits above bit 8, and GLX=1 ignores bits above bit 17. Software must choose enough levels for the PASIDs it uses. This demo rejects such aliases before issuing a request; that rejection is not an emulated hardware IO_PAGE_FAULT. These mock capabilities do not read your hardware.

Linux PCIe PASID capability handling · AMD GCR3 table capacity

In this example, the System Physical Address (SPA) of the backing memory is given, and the addresses are synthetic. The C++ linear address is a separate CPU-side buffer association, not a pointer cast or a CPU page-table walk. Values are synthetic; no buffer is allocated at these addresses.

03 / Configure the PCIe device

Mock configuration space

Capabilities describe what the NIC supports; control registers enable those features. “+” means a bit is set and “−” means it is clear, as in lspci output. These controls apply immediately; the device width above changes when you apply the mapping.

DMA and PASID controls

Enabling these permissions does not turn this demo’s user-mode data transfers into instruction fetches or privileged requests.

Address Translation Services

STU=0 selects a 4 KiB minimum translation unit for ATS. Data-page leaves may still be larger; STU does not set their size. Queue depth 32 uses encoded value 0. Queue processing and cache invalidation are not simulated.


        
Register words and bit positions

Offsets are relative to each extended capability: PASID ID 0x001B and ATS ID 0x000F. The Command register is at configuration-space offset 0x04. Unused bits are zero in this selected-field example.

Linux PCIe register definitions · Linux PASID/ATS enablement · pciutils decoding

Ask for a translation with ATS

This query uses the current request below and returns a translation with read/write permissions. It copies no packet bytes and does not add an IO_PAGE_FAULT entry for a failed page lookup. This is a standalone probe; it does not warm the DMA cache. Choose ATS / pretranslated DMA in section 05 to observe the full query, cache and transfer sequence.

A selected DTE with V=1 and TV=0 is not a page-table lookup failure. AMD 48882 rev 3.11 Table 64 classifies that ATS request as INVALID_DEVICE_REQUEST with TR=1; this model stops without a response or event. AMD IOMMU Table 64.

Enable ATSCtl and DTE.I, then issue a query.

Guest walk: PML4 / 48-bit teaching profile · independent of host Mode

04 / Send a DMA request

05 / Observe the DMA transfer

Mock vDMA · browser memory only

A receive operation (RX) writes packet bytes from the NIC into RAM. A transmit operation (TX) reads RAM into the NIC. Both use the request address, PASID, access and fault settings above.

Choose how the NIC addresses memory

Untranslated, translated and pretranslated: what changes?
Operation AT[1:0] Address and effect
Untranslated memory DMA 00 NIC sends the original GVA or GPA/IOVA. The IOMMU translates it and checks access before memory is touched.
ATS translation request 01 NIC asks the translation agent for an address and permissions. This query copies no payload bytes.
Translated / pretranslated memory DMA 10 NIC sends an SPA obtained through ATS and enforces the cached permissions. The IOMMU does not repeat the page-table walk for this permitted device.
Reserved 11 Not selectable in this demonstration.

AT is a two-bit field. AT[1]=1 in the translated encoding 10; the value 01 means a translation request. AMD calls the device-translated address “pretranslated”—it is not a fourth kind of memory DMA. An address calculated by software is not automatically an ATS translation.

Untranslated DMA and ATS can use the same guest and host tables. What changes is when translation occurs: during the memory request, or earlier so the NIC can cache the result. Cache entries must be invalidated when mappings or permissions change. This demo starts with an empty cache on each prepare/configuration change.

AMD PCIe AT encodings · AMD 48882: pretranslated address, Definitions and §1.1

Byte preview: first 64 bytes (offsets 0–63). The counter covers the full descriptor. Each page needs a valid translation and permission before its bytes move. ATS mode uses the NIC’s cached translation for AT=10 requests. A failed page copies zero bytes; earlier completed pages are not rolled back.

NIC packet buffer

RAM · before transfer

RAM · at this event

Hex bytes correspond to consecutive request offsets. “—” means no backing address has been resolved for that byte. RAM starts with byte[offset] = offset mod 256, or your repeating pattern when patterned reads are selected. Highlighted cells were written or read. Each run starts from the same mock memory. Other PASID buffers are separate allocations.

Scenario presets and predicted outcome +

Compare two fault cases

These examples select untranslated DMA (AT=00), enable the NIC and choose a mapped PASID. Your page sizes and read/write choice stay selected. Use the ATS examples above to compare recovery.

Start on an invalid page

The first page fails its presence check. 0 of 16 bytes copied.

Valid page → invalid page

Start 8 bytes before a valid page ends. Copy those 8 bytes, then reject the next page. 8 of 16 bytes copied; 0 from the invalid page.

Predicted outcome · before playback

    The breakdown predicts the configured descriptor. The progress bar and byte display below show only the events reached during playback. An earlier successful page is not rolled back when the next page faults.

    IOMMU event buffer

    Logging enabled · DTE SA=0 / SE=0 · memory requests, not ATS queries. The eight slots are a compact visual teaching buffer, not a hardware-valid event-buffer size. A page fault appends a 16-byte IO_PAGE_FAULT record. This is the IOMMU’s event log, separate from the NIC’s interrupt-remapping table. Successful DMA does not create a fault event.

      Teaching model: one descriptor, chunks of at most 256 bytes that never cross a 4 KiB boundary or a mapping, and no real packets or hardware. Earlier successful chunks remain copied if a later chunk faults; this is not a model of PCIe timing, descriptor rings or a particular NIC.

      06 / Inspect the device and roots

      Device Table Entry · 256 bits / 32 bytes

      The Requester ID chooses this record. It supplies the roots and permissions; it does not contain the page mapping itself.

      Expand DTE fields and bit positions

      Find the page tables for this PASID

      A PASID identifies an address space. The GCR3 tables act like a directory: the PASID selects a guest page-table root. They do not translate the data address yet.

      DTE GCR3 root 0x180000 SPA
      Guest page-table root PML4 → PDPT → PD → PT

      Host root

      DTE Host Root Pointer 0x100000 SPA
      1. PDE · level 6
      2. PDE · level 5
      3. PDE · level 4
      4. PDE · level 3
      5. PDE · level 2
      6. PTE · level 1 → SPA frame

      The selected Host Mode chooses the root level. NextLevel points to the next directory or leaf, allowing the configured zero-index levels to be skipped. A 4 KiB data leaf is at level 1; 2 MiB and 1 GiB leaves end at levels 2 and 3. In Guest + Host, guest-table storage uses separate 4 KiB host mappings.

      FOLLOW THE POINTERS

      07 / See the address map

      You do not need to memorize the hexadecimal numbers. Follow the named tables, select a card, and see which slot points to the next table or data frame. These cards use the same walk as the detailed trace below.

      08 / Walk the tables

      Choose a step to inspect its entries, or step through the request. In Guest + Host, guest and subordinate GCR3 table fetches also need host translation. In Guest Only, GPA already equals SPA. Expand any entry to inspect its bits and mocked memory.

      All table accesses in this walk

      09 / Investigate faults

      A page fault means the lookup or permission check could not complete. It does not automatically mean the hardware is broken. The failing entry tells software what is missing or disallowed.

      Missing mapping

      The next 4 KiB page has no entry. The original page remains accessible.

      Read-only page

      A read succeeds; a write is rejected. Present and writable are different checks.

      A table is missing

      In a nested walk, the host must map the guest table itself before its entry can even be read.

      Examples reset to a known mapping so their outcomes are reproducible.

      Real software may install a mapping, adjust authorized permissions, invalidate cached translations and retry through an appropriate protocol. The address walk shows a teaching diagnosis. Running DMA also encodes the selected IO_PAGE_FAULT record; neither action models automatic hardware recovery.

      10 / Follow the interrupt path

      An IRTE belongs to an interrupt-remapping table. It does not translate the DMA address and is not another guest or host page-table level. This simplified legacy-style table uses 4-byte entries. The independent probe below previews routing; the DMA demonstration delivers a completion interrupt only after a successful transfer. Its generated C++ packs and decodes the selected IRTE fields.

      Synthetic NIC MSIDTE interrupt-table root
      0x1C0000 SPA
      IRTE[index]Vector + APIC destination

      Disabled or absent entries stop this educational interrupt path. Other delivery modes, guest virtual interrupts, interrupt-fault event records and real MSI message decoding are outside this model.

      Model boundaries and public references

      Validity of this model: this is an executable teaching model of selected AMD IOMMU behaviours, not a device emulator or conformance test. The generated C++ and browser walker are compared for translation paths, page sizes, permission faults, skipped levels and ATS/PRI scenarios. Agreement between two implementations is useful regression evidence; it does not prove agreement with silicon. To program an emulated one by hand, open the QEMU labs: QEMU's AMD, Arm and RISC-V IOMMUs in your browser.

      The C++ rejects inconsistent packed/decoded fields, misaligned supported leaves and a guest walk without a leaf. Host entries are limited to read-enabled pages; raw entry editing is not a complete architectural parser. Command execution is serial, PRI handles one group, and timing, coherency, A/D updates and feature-dependent errors are outside its scope. Read the validation scope and reuse guide.

      The guest uses canonical 48-bit virtual addresses and four 9-bit indices (PML4E, PDPE, PDE, PTE). Host Mode selects 1–6 levels; their shifts are 12, 21, 30, 39, 48 and 57; only seven top-index bits are available in a 64-bit input. The mapping builder limits physical page bases to 52 bits.

      The GCR3 root is an SPA in this model; a valid Level 1 GCR3 entry holds the GPA of the guest PML4. Subordinate GCR3 pointers are GPAs; the example identity-maps their backing pages when host translation is active. GLX=0/1/2 selects one/two/three GCR3 levels. Index slices are PASID[8:0], [17:9], and [19:18]. This demo combines the PCIe device’s configured PASID width with the GLX capacity and uses the smaller range as a software guard against aliases. A 16-bit PASID fits in two levels; GLX=2 can still be used with a 16-bit width, with a zero top index. Only explicitly mapped PASIDs have valid leaves. Guest table fetches pass through host translation in the nested path; Guest Only uses GPA = SPA. ATS mode uses a small semantic IOMMU translation cache for permission responses; microarchitectural TLB behaviour is not modelled. ATS mode adds a small NIC address translation cache (ATC).

      All pointers, BDFs, domains and tables are synthetic. Raw words encode a selected page-entry subset of AMD 48882 rev 3.11. Host IR/IW/PR/NextLevel and guest P/RW/US use different bit positions. Guest A/D are pre-set, host HAD is zero, and unmodelled optional features remain off. PCIe and ATS controls shown on this page are configurable. The walker uses decoded teaching fields alongside the displayed packed words; it does not parse arbitrary hardware memory. This is not a complete hardware implementation. The address walker uses teaching diagnostics; mock DMA adds the selected IO_PAGE_FAULT encoding. The model omits other architectural events, access/dirty updates, privileged access details, nonstandard page sizes, real PCIe invalidation packet timing and memory coherency protocols. The mock verifies ATC provenance before translated DMA; this is a teaching safeguard, not ordinary AMD hardware revalidation of an SPA (§2.11). ATS mode models a per-descriptor 4 KiB ATC and semantic PRI/PPR page-in handshake; packed command/PPR words, real PRI registers, timeouts, concurrent requests and secure ATS are omitted. Command and PPR rings model ownership, fetch, completion and wraparound. A per-session IOMMU cache is also invalidated; invalidation scope is conservatively the whole mock session. Mapping changes discard the mock cache. Control/DTE protocol errors are stopped by this teaching model; their full architectural event handling is not emulated. The address inspector validates one address. The mock DMA demo splits a 1–65,536 byte transfer at the active guest/host page boundaries, in requests of at most 256 bytes, and checks each chunk; it is not a hardware protocol implementation. Arm SMMU uses different structures and is not represented by these AMD-specific names.

      11 / Run the matching C++ example

      Generated from the applied mappings, GCR3 tables, request, faults, NIC, DMA length/payload and interrupt settings. Run it to reproduce the complete mock transfer from its initial memory state. Edit manually to explore further, or regenerate to return to the playground state.

      Edit the source, then run the tests. Compilation provided by Compiler Explorer. Clicking Run sends the code to that external service. Use only public, non-confidential code.

      Download combined .cpp

      Ready. No code has been sent.

      Compiler & test output

      Run the example to see actual output here.

      If the external service is unavailable, copy the source and open Compiler Explorer, or compile locally with g++ -std=c++23 example.cpp -o example and run ./example.