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

REFERENCE / RISC-V IOMMU

RISC-V IOMMU: follow the request

A device gives the system an address. Before memory receives it, the IOMMU asks which device sent it, which process it names, and whether the tables for that context allow this access. Follow one 16-byte request. Change its process identity, deny one stage, or leave an old translation cached, and watch the destination and the evidence change independently. Then compare with the AMD and Arm chapters.

A request goes from the device through the device context, the process context, the first stage and the second stage to memory. The tables the first stage reads are guest-physical, so the second stage translates those reads too. Below: software and its command queue, the IOMMU's cache, the device's own cache, the fault queue and the MSI table. DeviceDID, PASID, IOVA Device ctxfound by DID Process ctxfound by PASID Stage 1IOVA to GPA Stage 2GPA to SPA Memorypages A and B table addresses are guest-physical too: the second stage translates those reads Softwaretables, cmds Cmd queueinvalidate, fence IOATCtranslation cache Device cacheATC (ATS) Fault queuerecords, 32 bytes MSI tableinterrupt files

Experiments

Words used in the experiments
DID, PASID
The device ID that arrives with the request (here the PCIe requester ID 02:00.0, 0x0200) and the optional process tag. The spec calls them device_id and process_id.
IOVA, GPA, SPA
The address the device presents; a guest-physical address; a system-physical address. In the main example the device address is a virtual address, then a guest-physical one, then a system-physical one.
DC, PC
Device context and process context: the 64-byte and 16-byte records that say which tables to use.
PSCID, GSCID
Software tags on cached translations (20 and 16 bits) read from the DC and PC. They are not fields of the request.
IOATC
The IOMMU's own cache of translations. It may not see a table edit until software runs an invalidation command.
CQ, FQ, PQ
Command queue (software to IOMMU), fault queue (IOMMU to software), page-request queue (device page requests to software).
cqh, cqt
The command queue's head (the IOMMU's read position) and tail (software's write position). The ring keeps one slot empty, so four slots hold three commands.
IOFENCE.C
A fence command. When the head moves past it, every command fetched before it has completed.
MRIF
A memory-resident interrupt file: pending bits kept in ordinary memory instead of an interrupt controller page.
Posted write
A PCIe write the device sends without waiting for an answer. A refusal comes back, at most, as a fault record.

Experiment

    AMD, Intel and Arm:

    What the model holds after that step

    Evidence from the last request

      What the IOMMU did, in order

        Pages, tables and caches

          Queues

            Interrupts

              Each step replays the experiment from a fresh model, so a later step cannot rewrite what an earlier one showed. A fault record, an allowed access and a changed byte are separate lines on purpose: none of them stands in for another.

              In plain words

              Think of a hotel where guests send in requests for a room by name. The device ID is the guest's badge: the front desk uses it to pull out that guest's folder (the device context). If the guest names a project, the PASID picks one booking sheet inside the folder (the process context). The sheet turns the name the guest used into a room in the building (the first stage); a second desk, run by the hotel, turns that into the real room number (the second stage). The doorman remembers recent answers (the cache). If you change a sheet, the doorman still remembers the old answer until you tell him to forget it, and you only know he forgot once he has said so. Requests to the desk go in a mail slot (the command queue); when something goes wrong the desk writes it in an incident log (the fault queue), and it never fixes the problem itself.

              Five ideas to keep before the first button:

              1. DMA is a device reaching memory by itself. A network card receiving data writes memory; fetching a descriptor reads it. Read and write are named from the memory's side.
              2. IOVA is just "the address presented for I/O translation". Depending on which stages are on, it is a virtual address, a guest-physical address or a system-physical address, not always a fourth kind.
              3. Identity picks the configuration; the address picks a spot inside it. A correct address under the wrong valid identity reaches the wrong allowed memory and nothing faults.
              4. A mapping and its cached copy are different state. Publishing a table value, asking for maintenance, the maintenance finishing and the next DMA request are four separate events.
              5. Permission, payload and notification are separate evidence. An allowed translation is not a completed transfer. A fault record is not a repair. An MSI is not proof that a handler ran.

              For context: the CPU's RISC-V paging does not cover devices, so DMA gets its own specification (ratified 2023; the 2026 release adds four ratified extensions). The CPU instruction set, the IOMMU, the interrupt architecture, firmware description and the operating system are separate contracts.

              Find the context

              Two identities arrive with a request. device_id is up to 24 bits; over PCIe it usually comes from the 16-bit requester ID (bus, device, function), and the platform decides the mapping. 02:00.0 is 0x0200 here. process_id is optional, up to 20 bits, and comes from the PCIe PASID. "PASID present" and "PASID value" are separate facts: PASID 0 is a PASID, and no PASID is none. It is not a Unix process ID.

              The IOMMU's ddtp register points at the root of a device directory of one, two or three levels. The pieces of device_id index it, and the leaf is a device context: 32 bytes in the base format, 64 bytes in the extended format that holds MSI fields too. Directory depth and page-table depth are independent.

              Formatdevice_id slices, low to highEntry
              Base7, 9, 8 bits32-byte device context; 8-byte directory entries above it
              Extended6, 9, 9 bits64-byte device context

              The slices follow how many contexts fit in one 4 KiB leaf page; it is not a different PCIe format. With PDTV=0 the device context itself holds the first-stage root and the PSCID, and a request that carries a PASID is refused (cause 260). With PDTV=1 it points at a process directory (PD8, PD17 or PD20 for 8-, 17- or 20-bit process IDs) whose leaf is a 16-byte process context: first-stage root, mode and PSCID. A request with no PASID follows the context's rule: DPE=1 uses process 0; DPE=0 makes the first stage Bare, and the request still goes through the second stage. There is no rule that a missing PASID always faults.

              Translate and check

              First stageSecond stageThe device address is aResult
              BareBareSPANo translation or page protection from these stages
              Sv39 (or another paged mode)Barevirtual addressFirst stage gives the SPA
              BareSv39x4 (or another)GPASecond stage gives the SPA
              Sv39Sv39x4virtual addressvirtual to GPA to SPA, checked at both stages

              The architecture defines Sv32, Sv39, Sv48 and Sv57 for the first stage and an "x4" version of each for the second; which a chip has is discovered, not assumed. Sv39 is three levels of 9-bit indexes plus a 12-bit offset. Sv39x4 widens the root index by two bits: a 16 KiB root, aligned to 16 KiB, taking 41-bit guest-physical addresses. "x4" means the bigger root, not four stages.

              A valid entry can still refuse an access. This chapter's fixture uses the valid, read, write, user, accessed and dirty bits, no execute and no global mappings, and sets A and D in advance so that a permission failure is not mixed up with hardware updating those bits. The second-stage user check treats the request as a non-supervisor one. Supervisor process requests have more rules (ENS, SUM). "Permission is read AND write" hides the validity, privilege, alignment, width and table-read failures that also exist.

              The tables are guest memory too. When the second stage is on, the process directory pointer and every first-stage table pointer are guest-physical addresses, so reading them needs a second-stage translation. Only the device directory and the second-stage root sit in the IOMMU's own physical address space. That is the thin dashed loop in the diagram, and the nested walk below.

              The worked fixture

              All addresses are made up; none comes from a real machine. The request is a 16-byte untranslated Memory Write from 02:00.0, PASID 42, user privilege, no execute, to IOVA 0x40203abc, carrying de ad be ef four times. The device context is the extended format, a two-level directory and PD8, with Sv39 over Sv39x4, PSCID 0x123 and GSCID 7 (both deliberately unlike 42). Both stages allow the user write, A and D are set, and caches start cold. Page A starts as aa; page B as bb. The intended destination is A.

              ObjectAddress or calculationMeaning
              Directory rootSPA 0x00100000ddtp.MODE = two levels
              Slices of 0x0200DDI[2]=0, DDI[1]=8, DDI[0]=09, 9 and 6 bits from high to low
              Root entry0x00100000 + 8 × 8 = 0x00100040Valid pointer to the leaf at SPA 0x00101000
              Device context0x00101000 + 0 × 64PDTV=1, DPE=0, second-stage root SPA 0x00200000, process directory at GPA 0x00400000
              Process context 42GPA 0x00400000 + 42 × 16 = 0x004002a0Second stage maps this read to SPA 0x804002a0; first-stage root GPA 0x00401000

              The second-stage root occupies SPA 0x00200000 to 0x00203fff; its entry 0 points at a table at 0x00204000. That table's entry 2 (SPA 0x00204010) is one valid 2 MiB leaf that maps GPA 0x00400000 to 0x005fffff onto SPA 0x80400000 to 0x805fffff, which is where the directories and first-stage tables live. That broad mapping keeps the fixture small; a real allocator would be tighter. Entry 256 (SPA 0x00204800) leads to a last-level table at 0x00205000, whose entry 0 maps GPA page 0x20000000 to page A at SPA 0x90000000 and whose entry 1 maps 0x20001000 to page B at 0x91000000.

              Sv39 splits 0x40203abc into indexes 1, 1 and 3 and the offset 0xabc. Every table read below first goes through the second stage, and none of those reads carries the payload.

              StepBefore the second stageActual accessResult
              Read process context 42GPA 0x004002a0SPA 0x804002a0First-stage root GPA 0x00401000
              Sv39 level 2, entry 1GPA 0x00401008SPA 0x80401008Next table GPA 0x00402000
              Sv39 level 1, entry 1GPA 0x00402008SPA 0x80402008Next table GPA 0x00403000
              Sv39 level 0, entry 3GPA 0x00403018SPA 0x80403018Leaf: GPA page 0x20000000
              Translate the payload addressGPA 0x20000abcsecond-stage indexes 0, 256, 0SPA 0x90000abc
              Commit the payloadSPA 0x90000abc to 0x90000acb16 bytesPage A changes; page B does not

              A hit in a translation cache could skip these reads on real hardware; this cold sequence is a teaching trace, not a required number of memory accesses. As packed 64-bit words (little-endian) the entries are: a pointer to page 0x00101000 is 0x40401 (page number shifted left 10, plus V); the first-stage leaf to GPA 0x20000000 with V, R, W, U, A and D is 0x080000d7; the second-stage leaf to 0x90000000 with the same flags is 0x240000d7. The model uses BigInt for these; JavaScript's ordinary numbers lose bits above 2^53.

              Faults, queues and what proves what

              Three queues, three directions. The command queue (CQ) carries software's commands to the IOMMU; the fault queue (FQ) carries fault and event records to software; the optional page-request queue (PQ) carries device page requests. CQ and PQ entries are 16 bytes, FQ records 32. Each is a ring whose head and tail wrap; one slot stays empty, so a four-slot ring holds three entries. Software owns the CQ tail and the FQ and PQ heads. Arm's queues carry a wrap bit; these do not.

              CauseNameWhere the experiments meet itHidden by DTF=1
              15Write page faultFirst-stage leaf has W=0 or A=0yes
              23Write guest-page faultSecond stage refuses the payload write, or refuses a table read (then bit 0 of iotval2 is 1)yes
              258DDT entry not validInvalid device contextno
              260Transaction type disallowedA PASID with PDTV=0yes
              266PDT entry not validNo process context 0 for DPE=1yes

              A guest-page fault keeps the original access type even when the failing access was the IOMMU's own table read. A fault record names the cause, the transaction type, the device, whether a PASID was present and its value, the address, and extra information in iotval2. Some bridges report page offsets as zero. DTF stops translation faults being reported without granting any access. A full or unreachable fault queue drops records and sets fqof or fqmf, and the IOMMU may coalesce identical faults. So the absence of a record proves neither success nor the absence of a fault: check the protected bytes too.

              The chosen PCIe Memory Write is posted: it has no completion. A denied write can transfer nothing and leave no record at all, for example under DTF. How a refused read is answered depends on the I/O bridge and protocol; the spec's guideline shows one illustrative read response, not a universal data value.

              Maintenance is a separate step

              CommandWhat it maintains
              IODIR.INVAL_DDT, IODIR.INVAL_PDTCached directory and context state
              IOTINVAL.VMAFirst-stage translation state, in the scope its operands name
              IOTINVAL.GVMASecond-stage state and applicable MSI-table state
              ATS.INVALThe device's own cache, by a PCIe message the device must answer
              IOFENCE.COrdering: completion of earlier commands

              None of them maintains every other cache, or a CPU's TLB. In IOTINVAL, GV and PSCV say whether the GSCID and PSCID operands are valid; an unused selector is not "ID zero". The scope depends on the command: IOTINVAL.VMA with GV=0 works on host address spaces (second stage Bare), so it does not touch an entry that belongs to GSCID 7; IOTINVAL.GVMA with GV=0 means all VM address spaces. A PSCID-selective invalidation skips global mappings; the fixture has none.

              The head moving past an ordinary command means the IOMMU fetched it, not that it finished. Only the head moving past a successful IOFENCE.C says every earlier command is complete and committed. A fence can also write a value to a configured address, or raise a wired interrupt, as another way to see it. Its read and write ordering bits cover requests the IOMMU already processed, not everything still crossing the interconnect. The experiments implement exact scopes; real hardware may legally invalidate more, so a wrong-scope command that appears to work on one chip is not a recipe.

              The architecture forbids caching entries whose valid bit is 0 in the directories, page tables and MSI table. This is why the chapter's stale-mapping story uses a valid-to-valid remap; the not-present cache lesson from the AMD chapters has no counterpart here.

              ATS, PRI and MSI: optional branches

              With ATS a device asks the IOMMU for a translation and keeps it in its own cache. With PRI it asks software to bring a page in; that is not the fault queue repairing a failed request. An ATS request that cannot complete (a missing page) gets a Success answer with R=0 and W=0 and no fault record; a page that is merely read-only gets R=1 and W=0. Configuration errors get Unsupported Request or Completer Abort and are logged. Software answers a page request with ATS.PRGR. A Success status says the group is finished, not that a page is resident, and the response moves no payload: the device must ask again, receive a usable translation, and send a new request. Invalid Request and Response Failure statuses tell it not to bother. Even a Success answer is not proof a page is resident: when the page-request queue overflows, a request that needs an answer can still be auto-answered Success.

              With T2GPA, the answer to a translation request is a GPA, not the final SPA, and later translated requests still pass the second stage. That is safe only if the physical path really routes those requests through the IOMMU, including PCIe peer-routing controls; it is not the same as trusting a device that holds SPAs, and Linux group numbers do not prove it either.

              An MSI is another memory write. After the first stage, a write whose guest-physical page matches the device context's interrupt-file mask and pattern is not looked up in the ordinary tables but in the MSI table. Basic mode redirects it to a real interrupt-file page. Optional MRIF mode instead sets a pending bit in a 512-byte structure in memory (32 pairs of pending and enable doublewords) and sends a configured notice MSI. In AIA 1.0 the notice is required even when the identity's enable bit is 0. It still does not show a guest handler ran. A translation request for such an address is told "untranslated access only", so a device never caches a normal address for it. The model assumes atomic MRIF updates and does not simulate several writers racing. The 20260222 text also says msiptp.MODE must be Off when the second stage is Bare; checking implementations are recommended to reject it as misconfigured (cause 259).

              What is supported where

              Three labels apply to every claim: the architecture says what the pinned specification defines; the capability and platform says what this chip advertises, enables and routes; the lesson or Linux snapshot says what the synthetic model or the pinned driver does. An optional feature is not a proposal; a ratified feature is not necessarily in a chip; a capability bit does not prove software enabled or used it.

              FacilityArchitecture and hardware gateLinux 7.3-rc5 snapshotIn this chapter
              Device contexts and directoryCore; discover directory modes and context formatImplementedWalked exactly
              First-stage pagingA subset of modes may be presentSv39, Sv48 and Sv57 selected from capabilitiesSv39
              Second-stage pagingSvNx4 capabilities and iohgatpComments say not yet mergedSynthetic
              Process directory and PASIDPD8, PD17, PD20; device and request enablementComments say SVA/PASID not yet mergedPD8, synthetic
              Command and fault queuesCoreImplemented; the fault handler logs, rate-limitedBounded model
              ATS, PRI, device invalidationATS capability plus device, platform and context controlsNo complete end-to-end path exposedSynthetic branch
              T2GPAExtra capability, routing assumptionsNot a working Linux feature hereCompares GPA and SPA modes
              MSI translation and MRIFMSI_FLAT, MSI_MRIF, AMO_MRIF; AIAThe IOMMU's own queue notifications are not device MSI remappingSynthetic
              Atomic A/D updatesAMO_HWAD with SADE and GADENo claim from capability bits aloneA and D preset
              QoS, non-leaf and range invalidationRatified extensions with capability bitsNon-leaf and range handling exists in the driverNot used
              Performance counters, translation debugOptional capabilitiesRegister definitions are not usageNot used

              The driver (drivers/iommu/riscv/, first in Linux 6.13) allocates PSCIDs, manages the directory and the queues, offers paging, identity and blocked domains, and has platform and PCI integration. The Device Tree binding describes architectural features; that does not mean the driver implements all of them. On a real system, check the capabilities register for address widths, byte order, interrupt generation and directory depth, and the firmware tables for routing. Registers are little-endian even where table memory is configured otherwise; the fixture is little-endian throughout.

              The same questions in four architectures

              This is a map of roles, not a table of encodings. The editions compared are AMD 48882 rev 3.11 (April 2026), Intel VT-d rev 5.0 (August 2024, not claimed to be the newest publication) and Arm IHI 0070 H.a (6 March 2026).

              QuestionRISC-V IOMMUAMDIntel VT-dArm SMMUv3
              What selects the device's configuration?device_id, device directory, DCDeviceID, device table, DTERequester identity, root and context structuresStreamID, stream table, STE
              What selects a process context?process_id (PASID), process directory, PCPASID, guest-CR3 hierarchy where enabledPASID, scalable PASID directory and tableSubstreamID, context descriptor; PCIe commonly maps the PASID
              Where is first-stage root state?iosatp in the DC or PCGuest CR3 and guest paging modeFirst-stage pointer in the scalable PASID entryCD translation table base
              What selects the second stage?DC iohgatpHost translation controls in the DTESecond-stage pointer and controlsStage-2 configuration in the STE
              Which tags scope translation maintenance?PSCID and GSCIDDomainID and PASID scopesDomain ID and PASID-related scopesASID, VMID and translation regime
              What maintains configuration caches?IODIR.INVAL_DDT/PDTINVALIDATE_DEVTAB_ENTRY and guest-root maintenanceContext-cache and PASID-cache invalidationCFGI_STE, CFGI_CD
              What maintains page translations?IOTINVAL.VMA/GVMAINVALIDATE_IOMMU_PAGESIOTLB and PASID-based IOTLB invalidationTLBI commands, or configured broadcast maintenance
              What proves queued maintenance finished?Successful IOFENCE.CCOMPLETION_WAITInvalidation Wait descriptorCMD_SYNC
              How does software see ordinary faults?Fault queue plus status bitsEvent log and status; PPR is separateFault-recording and status; page requests separateEvent queue plus global errors; stall and PRI paths differ
              How is a device's translation cache maintained?ATS.INVAL, endpoint completion, fence orderingINVALIDATE_IOTLB_PAGESDevice-TLB invalidationATC invalidation commands
              How are interrupts handled?Optional MSI table, IMSIC and MRIF through AIAInterrupt remapping tables, optional guest featuresInterrupt remapping table, optional posted interruptsGIC and ITS, distinct from the SMMU's DMA translation
              Can CPU and IOMMU share table memory?Possible; separately configured and maintainedSelected guest and host table-sharing formatsCPU-compatible first-stage formatsCompatible formats subject to regime and maintenance rules

              Where the analogy stops

              What claims does this page make? 3 points
              • Spec rule: the RISC-V IOMMU Architecture Specification release 20260222 (source commit 054005f4; base architecture version 1.0, shown as v1.0.1 in the official library), the privileged ISA release 20260120 for page tables, and AIA 1.0 §7.1 for MSI tables and memory-resident interrupt files.
              • Platform assumption: which features a chip has and which the Linux driver uses. The Linux statements are about mainline commit ce1e0223d8ad (1 October 2026, Makefile says 7.3-rc5), not a distribution kernel.
              • Teaching model: one synthetic device, made-up addresses and pages, deterministic. Not a RISC-V CPU, not QEMU, not a hardware conformance claim.

              What this model leaves out

              Sources