Every device that does DMA (PCIe NIC, NVMe, GPU, display, video codec, DMA engine) bypasses the CPU's MMU entirely.
Without an IOMMU, a device can write to any physical address — a bug or exploit in the driver turns into arbitrary-kernel-memory-write.
The SMMU gives each device its own virtual address space and permissions — same semantic benefits as the CPU MMU, but for I/O.
It also enables:
VFIO / SR-IOV pass-through — safely give a VM direct PCIe device access
Shared virtual memory (SVM) — the CPU and GPU/NPU use the same VA
PCIe ATS / PRI — device caches its own translations, traps on miss
x86 equivalent
Intel VT-d / AMD-Vi are the x86 IOMMUs. Similar goals, different architecture. Arm's SMMU-v3 is roughly at the feature parity of VT-d 2.x — virt + PRI + PASID + MSI remapping.
SBSA requires it
SBSA Level 3+ mandates an SMMU-v3. No PCIe-capable Arm server ships without one.
03
SMMU-v2 vs SMMU-v3
Aspect
SMMU-v2 (2013)
SMMU-v3 (2016)
Programming interface
Memory-mapped registers, context banks
DRAM-resident tables + command queue
Max stream IDs
~128 (context banks)
> 1 M
Virtualization
Basic Stage 1+2
Full Stage 1+2, pinned pages, ATS/PRI
PCIe PASID
No
Yes (up to 20-bit)
Broadcast TLBI
Limited
Full TLBI VMALLS12E1IS
Interrupts
MMIO to CPU
Command+Event queues, MSI to GIC-ITS
Typical IP
SMMU-500 (mobile), CCI + SMMU-v1 hybrids
SMMU-600, SMMU-700
SMMU-v3 was a clean-sheet redesign for server scale. Every shipping Neoverse system uses SMMU-v3. SMMU-v2 survives in older phone SoCs and some embedded platforms.
04
Stream IDs, Substream IDs, and Context
Every DMA transaction arrives at the SMMU with:
An AXI transaction with address, burst, ID
An AXI user side-band carrying StreamID (up to 32 bits) — identifies the source device
Optionally SubStreamID (aka PASID, up to 20 bits) — identifies the address space within a device
SMMU indexes Stream Table with StreamID:
Entry contains a pointer to a Context Descriptor (CD), or CD table if SubStreamID is used
CD has TTBR0/1, TCR, MAIR — exactly like CPU MMU
For virtualization, Stream Table Entry also points to a VTTBR (Stage-2).
// Flow of a PCIe DMA through SMMU-v3
PCIe device -> AXI transaction
+ StreamID (PCIe Requester ID)
+ PASID (if present)
|
v
SMMU reads Stream Table[StreamID]
|
v
STE -> Context Descriptor Table[PASID]
|
v
CD -> TTBR, TCR, MAIR
|
v
Hardware Table Walker (Stage 1)
-> each walk step further through
Stage 2 if VM-owned
|
v
Output PA -> mesh / HN-F
05
Two-Stage Translation
Stage 1: Virtual Address (from the device, in its address space) → Intermediate Physical Address (IPA).
Stage 2: IPA (what the device/guest sees as "physical") → actual Physical Address (PA).
Native-OS setup: Stage 1 only. Device sees kernel-managed VA space.
Guest pass-through setup: Stage 1 (guest VM owns) + Stage 2 (hypervisor owns). Guest has full control of its device's VA without compromising host isolation.
Same 4/16/64 KB granules as CPU MMU. Stage 2 can use coarse granularity (typically 1 GB blocks) for large VM memory regions.
Translation tables in DRAM; SMMU has IOTLB + walk cache to accelerate repeat hits.
Performance cost
Untranslated DMA: 1 AXI transaction. Translated (Stage-1 miss): up to 4 table reads + DMA. Stage-1+2 miss: up to 24 table reads. IOTLB + walk cache make steady-state near-free; misses are expensive.
Pinned pages
Traditional SMMU setup pins every page a device might DMA to (page tables static, no swap). Legitimate for most workloads. PRI extension relaxes this.
06
PCIe ATS & PRI — Device-Side Translation
ATS (Address Translation Service) — PCIe feature where the device has its own TLB cache. Device sends an ATS Translation Request over PCIe; SMMU responds with the translation; device caches it.
Subsequent DMAs from that device include a Translated bit in the TLP prefix — SMMU treats them as already-translated.
PRI (Page Request Interface) — device can request a page that isn't mapped yet. SMMU queues a page-request event; host kernel faults the page in; SMMU notifies device; device retries.
Together ATS + PRI enable full demand-paged device access — no pinning, no pre-allocation. Essential for SVM (GPU compute sharing CPU memory).
// SVM: GPU and CPU share the same VA
// 1. User mallocs a large buffer
float *data = malloc(1 GiB);
// 2. Open the accelerator, bind to current mm
int fd = open("/dev/gpu0", O_RDWR);
bind_pasid(fd, getpid());
// 3. Submit a kernel, passing `data`
submit_kernel(fd, data, N);
// Under the hood, the GPU uses ATS to translate
// & PRI to fault in pages on demand. No pinning.
// glibc malloc works: swap + demand-paging just
// as with CPU access.
07
SMMU-v3 Queues
SMMU-v3 moved control from MMIO registers to DRAM-resident queues — same pattern as NVMe.
Event Queue (EVTQ) — SMMU posts faults/events; CPU drains it.
PRI Queue (PRIQ) — page-request events, optional.
SMMU MSI-signals the CPU via GIC-ITS when a queue has content — same pattern as NVMe.
Why queues beat MMIO
Each MMIO write to the SMMU is a slow PCIe-like transaction. Batching commands into a DRAM ring lets the SMMU pipeline dozens of invalidations per CMDQ doorbell, ~10× higher throughput.
Driver pattern
Linux arm-smmu-v3.c looks similar to an NVMe driver: producer/consumer pointers, MSI completion, and a completion path that flags the CPU when CMD_SYNC retires.
08
SMMU-600 & SMMU-700 IP
IP
Year
Arch
Max StreamIDs
PCIe
Target
MMU-500
2014
SMMU-v2
128
PCIe Gen 3
Mobile / embedded
MMU-600
2018
SMMU-v3.1
> 1 M
PCIe Gen 4
Server / Neoverse N1-era
MMU-600AE
2019
SMMU-v3.1
> 1 M
PCIe Gen 4
Automotive ASIL-D
MMU-700
2021
SMMU-v3.2
> 1 M
PCIe Gen 5, CXL 2.0
N2 / V1 / V2 Neoverse
MMU-700S3
2024
SMMU-v3.3
> 1 M
PCIe Gen 6, CXL 3.0
N3 / V3 Neoverse
Modern IP: a configurable pipelined design with per-client TBUs (Translation Buffer Units) and a central TCU (Translation Control Unit). Clients close to the CPU mesh get a TBU; the TBU holds the local IOTLB; TBUs talk to the TCU for misses.
09
TCU + TBU Architecture
TBU — Translation Buffer Unit — sits close to each master on the mesh. Holds a fast local IOTLB (64-256 entries), services hits inline.
TCU — Translation Control Unit — central, handles:
Stream-table lookups
Page-table walks
Command/event queue processing
PRI responses
TBUs cache TCU translations; TBU misses query the TCU.
This split lets the SMMU scale bandwidth with the number of masters.
An OS that doesn't understand IORT will still boot — devices just won't have IOMMU isolation (and therefore VFIO won't work). Distros require IORT for PCIe passthrough on Arm.
Broadcast TLBIs from Linux cost CMDQ bandwidth — batch where possible.
Linux iommu.strict=0 (non-strict mode) defers invalidation until a flush point. Higher perf but tiny freshness window.
SMMU PMUs (SMMU-600/700 expose them) track: stream-table misses, walk-cache misses, translation latency, PRI events. Essential for debugging DMA-bandwidth-limited workloads.
Graviton-class numbers
On Graviton 3 + 200 GbE mlx5: ~15 Mpps sustained with the SMMU on; ~16 Mpps with iommu.passthrough=1. ~6% cost for full device-isolation — acceptable for multi-tenant cloud.
ATS payoff
With ATS enabled, the device cache absorbs almost all hits. SMMU only sees misses + invalidations. Essential for 400 GbE + NVMe-class DPUs.
13
SMMU in Virtualization / Cloud
Hyperscalers run VFIO passthrough for GPU, NIC, NVMe — tenant VM gets bare-metal-level device speed.
Stage-2 owned by KVM; Stage-1 owned by the guest.
For a VM to sub-allocate the PCIe device to its own processes, the SMMU needs PASID support + the device must support SR-IOV VFs or SIOV.
vIOMMU — qemu can expose a virtual SMMU to the guest so the guest kernel "sees" an SMMU and can do its own isolation for nested device use.
With CCA / Realms, SMMU needs to honour Granule Protection Checks on its DMAs — a Realm VM's DMA target must be a Realm-owned granule.
SR-IOV + SMMU-v3
SR-IOV VFs all live on the same PCIe Requester-ID space; each gets its own StreamID (or shared StreamID + PASID). Allows fine-grained per-VF isolation.
SIOV
Intel-coined "Scalable I/O Virtualization" — PASID-based rather than VF-based. Arm SMMU-v3 supports it via SubStreamID → very dense tenant packing on NVMe, NIC.
14
Debugging SMMU Issues
dmesg | grep smmu — probe + fault messages.
SMMU fault format (SMMU-v3): Event type (C_BAD_STE, F_TRANSLATION, F_PERMISSION, F_ADDR_SIZE), StreamID, IOVA, Reason.
perf stat -e arm_smmu_v3_pmcg_0/... accesses SMMU PMU. Useful for seeing hit rates, walk counts, event queue pressure.
15
Lessons
"What problem does an SMMU solve?" → DMA isolation (device can't write arbitrary memory), virtualization (pass-through devices to VMs safely), shared virtual memory (GPU/NPU share CPU VAs).
"What's a StreamID?" → identifier the bus carries with every DMA to uniquely identify the source device. For PCIe, typically the Requester-ID. SMMU uses it to index the stream table.
"SMMU-v2 vs SMMU-v3?" → v2 = MMIO-register + context-bank; limited to 128 streams. v3 = DRAM tables + command queue; 1M+ streams, full virt + PASID + PRI.
"What is ATS?" → PCIe Address Translation Service; device has its own TLB, queries SMMU for translations, tags subsequent DMAs as pre-translated. Massive throughput win.
"What's a TBU?" → Translation Buffer Unit — per-master local IOTLB inside the SMMU, close to the master on the mesh. Misses go to the central TCU.
"Why do we need IORT?" → ACPI table mapping PCIe Requester IDs → StreamIDs → SMMU instances. Without it Linux can't wire up device ↔ SMMU relationships on a server.
16
References
Arm Ltd. — Arm System Memory Management Unit Architecture Specification (SMMU-v3) (IHI 0070) Arm Ltd. — SMMU-v2 Architecture Specification (IHI 0062) Arm Ltd. — MMU-600 / 600AE / 700 Technical Reference Manuals ACPI 6.5 — chapter on IORT (I/O Remapping Table) PCI-SIG — PCIe ATS / PRI / PASID specs Linux kernel — drivers/iommu/arm/arm-smmu-v3/, drivers/iommu/io-pgtable.c, Documentation/userspace-api/iommufd.rst Will Deacon / Joerg Roedel — LWN.net articles on IOMMU subsystem + SMMU-v3 upstream patches AMD IOMMU / Intel VT-d specs — useful cross-reference for how x86 handles the same problems
Presentation built with Reveal.js 4.6 · Saira Semi Condensed + Archivo + Share Tech Mono
Educational use.