Start with one buffer
Choose Bounce buffers + emulated IOMMU faults and press Take the next step. Watch three separate things: where the device writes, when the CPU sees those bytes, and whether a fault record fits in the Event log. This runs entirely in your browser; it needs no IOMMU hardware and does not execute a Linux kernel.
The model combines a Linux-style buffer lifecycle with a synthetic AMD translation and Event-log machinery. It is an independent implementation of selected behaviour, not Linux code compiled to JavaScript.
What SWIOTLB contributes
SWIOTLB gives a device an accessible temporary buffer and uses CPU copies to move data between it and the original. Drivers still use the DMA API. In this fixture, the original is above 4 GiB and the bounce region is below 4 GiB. Mapping initializes the bounce data; receive/bidirectional data is copied back at the applicable CPU sync or unmap. Transmit/bidirectional CPU edits need the corresponding sync for the device.
SWIOTLB alone does not block malicious DMA or produce IOMMU Event/PPR/GA records. Its name does not mean that it is a hardware translation-cache emulator. A SWIOTLB-only wrong-direction transaction can still touch accessible bounce memory. The lesson reports the API violation separately from the hardware outcome.
Where the synthetic faults come from
The combined profile maps the bounce memory through the emulated AMD IOMMU. Linux's DMA-IOMMU implementation can also use SWIOTLB for selected buffers; this site’s combination makes that layering visible without requiring hardware. It does not reproduce every condition that selects bouncing in a real kernel.
| Observation | Producer in this lesson | What to do next |
|---|---|---|
| Mapping allocation or DMA-mask failure | DMA API layer; no descriptor submitted | Check dma_mapping_error(); recover or fail the operation |
| Wrong-direction access through the emulated IOMMU | Selected page permission check; synthetic IO_PAGE_FAULT |
Inspect identity, address and requested access; correct the mapping or request |
| DMA after completed strict unmap | Emulated not-present translation failure | Fix descriptor lifetime; do not reuse an unmapped handle |
| Event log full | Event-buffer state machine; incoming record lost | Preserve/drain records, observe Run=0, disable, clear overflow and restart logging |
| Wrong unmap parameters or premature unmap | Teaching API guard | Fix API lifetime/ownership; this guard is not an IOMMU fault |
The Event record uses the 16-byte AMD encoder and buffer model: four raw 32-bit words, DeviceID, DomainID, fault address, present and permission evidence. No PASID is attached to these ordinary host-domain DMA requests. Record production, notification acknowledgment, consumption and repair are separate operations. A lost record never turns a denied DMA into an allowed transfer.
PPR requires its own page-request protocol; GA requires guest interrupt-remapping activity. Neither is invented for an ordinary bad DMA. Continue to Command & event buffers, the next chapter, then Interrupts & GA log; command halt and restart cases are in Faults & recovery.
Direction is from the device's perspective
| DMA API direction | Intended device operation | Selected IOMMU permission |
|---|---|---|
DMA_TO_DEVICE |
Device reads memory, such as NIC transmit | IOMMU_READ |
DMA_FROM_DEVICE |
Device writes memory, such as NIC receive | IOMMU_WRITE |
DMA_BIDIRECTIONAL |
Both | Read + Write |
Use the returned dma_addr_t in the descriptor. A CPU virtual pointer, a physical address and a DMA address are different concepts even when two happen to be numerically equal. The API also takes the device's DMA mask into account. This fixture uses an identity bus-address mapping only in its direct and SWIOTLB-only profiles.
The short lifecycle
- Prepare the original CPU buffer and choose the direction.
- Map it and check the result. The mapping publishes device access before a descriptor is submitted.
- Submit the returned DMA address and let the device access it.
- Observe device completion. This is a descriptor/driver event, not a fabricated PCIe Completion TLP for every posted write.
- If retaining a streaming mapping, sync for CPU before CPU access and sync for device before returning ownership. In the bounce profile, these steps expose the actual copies.
- Unmap with the original device, handle, size and direction after the device has finished using the buffer.
A teaching guard catches API misuse here so the visitor can recover. Real Linux may diagnose misuse through DMA-API debugging, or the bug may cause corruption; the guard is not a claim that all such calls fail safely in the kernel. Memory inspection panels are observers and do not represent unsynchronized CPU accesses.
Optional: strict versus deferred invalidation
The IOMMU profiles reuse this site’s host walker and payload-copy engine. Strict unmap performs page removal and modelled invalidation completion before releasing the IOVA. The deferred profile retains the IOVA allocation until its flush completes. Its software mapping can be gone while a retained hardware translation still exists. The deliberate Inject read after unmap action demonstrates that misuse; normal drivers must stop using a mapping before unmap.
The two-slot allocator makes delayed reuse visible: a second mapping uses another slot while the first is retired. Flushing permits reuse. Once an IOVA is legitimately reused for another buffer, an old descriptor using that same address can reach the new mapping: invalidation does not replace descriptor-lifetime discipline. This is not the Linux allocator's exact placement, queue size, timeout or scheduling policy. Hardware cache retention and over-invalidation are also implementation-dependent.
Unmapping DMA is separate from interrupt-cache maintenance and guest CR3/TTBR maintenance. Those remain in the cache experiments and shared guest-table lesson.
Keep the scope small
The default experience needs only Map, device access, completion, sync and unmap. Advanced actions expose the negative cases. Each experiment resets to a deterministic fixture. JSON export preserves the source profile and action snapshots for comparison.
The data buffer starts at offset 64 inside a 4 KiB page. Choose 1–4032 transfer bytes and a repeating pattern of 1–32 hex bytes. Allocation and protection are page-granular, not a promise of sub-page isolation. The memory preview shows up to 32 bytes; exported transfer evidence is bounded by the engine's 64-byte preview. The model moves the requested number of bytes.
Omitted: a running kernel, a kernel driver, full SWIOTLB pool/slot/area allocation, scatter-gather merging, noncoherent cache maintenance, DMA attributes, confidential-computing encryption, endpoint ATC, PASID/SVA binding, topology/group attachment and concurrent DMA. The combined profile uses strict invalidation. No server configuration, boot parameter or kernel module needs changing.
Sources
The implementation is pinned to Linux commit 80a4e3ad8daba66915a9bdf0fcae5831cc8dbd5f; its Makefile identifies 7.3.0-rc2. This is a reviewed reference snapshot, not a requirement to install that kernel. The source links below point to the exact commit; the general documentation links can change over time.
- Linux DMA API guide: public driver lifecycle and ownership.
- Linux SWIOTLB guide: bounce-buffer responsibilities.
- Exact function references are listed below. The AMD record, walker and buffer modules keep their AMD 48882 Rev. 3.11 provenance.
Copy accounting and controlled contrasts
The visible counters separate device requests and payload bytes from CPU copies: initialization, sync-to-device, sync copy-back and unmap cleanup. Every copy records its source, destination, actor and size. A 16-byte receive followed by sync-for-CPU and ordinary unmap performs 16 initialization bytes, 16 sync copy-back bytes and another 16 cleanup-copy bytes. Equal bytes do not mean no copy occurred. IOMMU-only and direct examples report zero bounce-copy bytes; that does not mean the CPU or OS did no work.
LINUX-09 is the positive 64-bit direct-address contrast to the 32-bit mapping failure. LINUX-10 shows that invalidating a translation does not copy bounce bytes into the original buffer.
Fixture addresses: original PA 0x100002040, first bounce PA 0x2000040, first IOVA 0x800040. One active mapping and two teaching allocation slots. Injected allocation failure and deferred-IOVA exhaustion are executable; a concurrent real SWIOTLB pool allocator is outside scope.
Exact Linux source references
- drivers/iommu/dma-iommu.c: dma_info_to_prot, __iommu_dma_map, __iommu_dma_unmap, iommu_dma_free_iova, queue_iova. Direction, offset/granule handling and strict/deferred release.
- kernel/dma/swiotlb.c: swiotlb_tbl_map_single, __swiotlb_tbl_unmap_single, __swiotlb_sync_single_for_cpu, __swiotlb_sync_single_for_device. Initial copy, copy-back and ownership synchronization.
- kernel/dma/mapping.c: dma_map_page_attrs, dma_unmap_page_attrs. Public DMA API dispatch.
- drivers/iommu/iommu.c: iommu_map, iommu_unmap_fast. Generic mapping and invalidation layer.