ARM SYSTEM IP · PRESENTATION 02

SMMU — the System MMU

I/O MMU for every DMA master in an Arm SoC
SMMU-v2 · SMMU-v3 · SMMU-600 / 700 · StreamIDs · Stage-1 + 2 · ATS / PASID / PRI · IORT
02

What is a System MMU For?

  • 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

AspectSMMU-v2 (2013)SMMU-v3 (2016)
Programming interfaceMemory-mapped registers, context banksDRAM-resident tables + command queue
Max stream IDs~128 (context banks)> 1 M
VirtualizationBasic Stage 1+2Full Stage 1+2, pinned pages, ATS/PRI
PCIe PASIDNoYes (up to 20-bit)
Broadcast TLBILimitedFull TLBI VMALLS12E1IS
InterruptsMMIO to CPUCommand+Event queues, MSI to GIC-ITS
Typical IPSMMU-500 (mobile), CCI + SMMU-v1 hybridsSMMU-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.
  • Reduces SMMU load dramatically for high-throughput devices (100 GbE NICs, NVMe).
  • 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.
  • Command Queue (CMDQ) — CPU writes commands; SMMU consumes. Commands include:
    • CMD_CFGI_* — invalidate stream-table / context entries
    • CMD_TLBI_* — invalidate IOTLB entries
    • CMD_ATC_INV — invalidate device-side ATS TLB
    • CMD_SYNC — barrier; needs to be observed
  • 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

IPYearArchMax StreamIDsPCIeTarget
MMU-5002014SMMU-v2128PCIe Gen 3Mobile / embedded
MMU-6002018SMMU-v3.1> 1 MPCIe Gen 4Server / Neoverse N1-era
MMU-600AE2019SMMU-v3.1> 1 MPCIe Gen 4Automotive ASIL-D
MMU-7002021SMMU-v3.2> 1 MPCIe Gen 5, CXL 2.0N2 / V1 / V2 Neoverse
MMU-700S32024SMMU-v3.3> 1 MPCIe Gen 6, CXL 3.0N3 / 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

SMMU-700 TCU + TBUs PCIe RC + TBU GPU + TBU NPU + TBU TCU walker + stream table + event/cmd queues
  • 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.
10

IORT — ACPI Topology for SMMU

  • IORT (I/O Remapping Table) — ACPI table describing:
    • PCIe root complexes & segments
    • SMMU instances & base addresses
    • Stream ID mapping ranges
    • Named Components (on-chip DMA masters not behind PCIe)
  • Linux parses IORT at boot, creates struct iommu_ops, binds each PCIe / on-chip device to its SMMU + StreamID.
  • Device tree equivalent: iommus = <&smmu StreamID> property on each device node.
// IORT (ACPI) conceptual structure

Node 0: SMMU-v3 @ 0x4f000000
  event IRQ: 42
  cmdq IRQ:  43
  PRIQ IRQ:  44

Node 1: PCIe Root Complex (seg 0)
  PCI bus range 0-255
  ID mapping: PCI RID [0..65535] ->
              StreamID [0..65535] -> Node 0 (SMMU)

Node 2: Named Component "nvme@..."
  ID mapping: -> StreamID 0x1234 -> Node 0 (SMMU)

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.

11

Linux SMMU Driver + VFIO

  • Core drivers: drivers/iommu/arm/arm-smmu-v3/ (v3), drivers/iommu/arm/arm-smmu/ (v2).
  • Implements the Linux iommu_ops — attach/detach, map/unmap, TLB flush.
  • VFIO stack:
    • vfio-pci claims the PCIe device
    • vfio_iommu_type1 provides per-container DMA mapping API
    • Qemu uses VFIO fd to set up guest DMA mappings via SMMU Stage 1
    • KVM maps guest memory via Stage 2 (VMID-tagged)
  • SVM stack: /dev/iommufd (kernel 6.4+) + SVA API for PASID-based sharing.
// PCIe passthrough to VM, at a glance

// 1. Bind device to vfio-pci
echo "vfio-pci" > /sys/bus/pci/devices/.../driver_override
echo <pci_id> > /sys/bus/pci/drivers/vfio-pci/bind

// 2. Launch qemu
qemu-system-aarch64 \
  -machine virt,gic-version=3,iommu=smmuv3 \
  -cpu max \
  -device vfio-pci,host=0000:01:00.0 ...

// Under the hood:
//   SMMU stream table entry for device => CD of VM
//   Stage 2 = VM's IPA -> host PA
//   Guest owns Stage 1 entirely
12

Performance Considerations

  • Per-device IOTLB hit rate is the #1 factor for high-throughput I/O. A 100 GbE NIC sees millions of distinct DMA buffers.
  • Large-page DMA buffers (2 MB huge pages) dramatically reduce translation load — fewer IOTLB entries needed.
  • 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.
  • Example:
    arm-smmu-v3 arm-smmu-v3.0.auto: event 0x10 received:
    0x000000007B2004C0 0x0000020000000000
    0x00000000FFFFFFF0 0x00000000FFFFFFF8
    → translation fault @ IOVA 0xFFFFFFF0, StreamID 0x2000
  • Common causes: device DMA before map, use-after-unmap, page permission mismatch, writing past allocation.
  • ATS failures show up with F_TRANSL_FORBIDDEN.

Subtle bugs

DMA masks wrong → IOVA outside addressable range. Late-setup SMMU → device DMAs before stream table entry valid. Hypervisor-VM desync → guest unmaps page, device still holds ATS-cached translation.

Tooling

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.