Can a device reach a confidential VM's private memory?
The previous chapter kept the host's
CPU out of a confidential VM's pages. A device is the other way in: it
writes RAM directly (DMA). With SEV-SNP on, the IOMMU translates the
device's address as usual, then checks the same Reverse Map Table
(RMP) the CPU uses. These experiments run on the site's emulated AMD
IOMMU (dmakit), driven the way a
test drives hardware: posted writes say nothing, so
each result reads the bytes and the event log.
What claims does this page make? · 3 points
Spec rule: AMD IOMMU specification 48882 rev 3.11 sets the order of checks, the register locks, the completion store and the Secure ATS rules (§2.11, §2.12) and the RMP_PAGE_FAULT record (§2.5.14, Tables 54 and 71).
Platform assumption: the driver steps, the bounce-buffer copies and how Linux 7.3 prints the fault record depend on the kernel version.
Teaching model: the experiments run on the site's emulated IOMMU (dmakit), which treats pretranslated DMA as a target abort and leaves out some flags.
Experiments
Words used in the experiments
B, O
B is a shared bounce buffer at SPA 0x2000000; O is guest A's private page at SPA 0x100002000 (the study notes' W18).
IOVA
The address the device uses. The IOMMU translates it to an SPA through the host's table for the device.
The command the host sends the AMD Secure Processor to turn SNP on. After it the IOMMU is "SNP-enabled".
RMP_PAGE_FAULT
The event-log record for a device access that failed the RMP check.
ATS, ATC
Address Translation Services: the device asks for a translation and keeps it in its own cache (ATC), then sends already translated requests.
COMPLETION_WAIT
A command that stores a value when every earlier command has finished; software polls that value.
0010h, 0018h, 0020h, 0028h, 0030h
Offsets of IOMMU registers: event log base, Control (IommuEn, CmdBufEn), the exclusion or completion-store base and limit, and the feature register.
Head, tail
Byte offsets in the command buffer: the IOMMU reads at head, software writes at tail; head = tail means the IOMMU has fetched every command, not that each has finished executing; completion is signalled separately (COMPLETION_WAIT).
ASID
The RMP's number for a guest (A is 1, as in confidential VMs); not the DTE's DomainID.
Target abort (CA)
The IOMMU refuses the request; nothing is read or written. A read or non-posted write gets a Completer Abort (CA) back; a posted write gets nothing.
IO_PAGE_FAULT, ILLEGAL_DEV_TABLE_ENTRY
Event records: no mapping or no permission in the tables; a device table entry the IOMMU must not use.
Flags
In RMP_PAGE_FAULT, which RMP checks failed: 0x2 ASID_MISMATCH (the page belongs to a guest), 0x20 PAGE_IMMUTABLE (a page nobody may write).
PASID, GVA
A tag on a request that picks a process address space, and an address in it.
Posted write
A PCIe Memory Write the device sends without waiting for an answer; a refusal sends nothing back, and software sees an event record only when event logging is enabled and the IOMMU reports that fault.
Experiment
Intel and Arm:
What the IOMMU holds after that step
This "What if" step ran on its own copy; the panels show that copy.
RAM · B and O
RMP · pages not the host's
IOMMU registers
Device table entry
Event log
The device's ATC
The last RMP_PAGE_FAULT, field by field
Runs on dmakit 4.3.0 (JavaScript; its C++ twin prints the same). Not
modelled: RMP_HARDWARE_ERROR, the PAGE_LOCKED and PAGE_SIZE_MISMATCH
flags, RMP checks of the IOMMU's own table reads, trusted devices
(the trusted I/O chapter).
The order of the checks
For one ordinary (untranslated) DMA request on an SNP-enabled IOMMU:
Device table entry. V=0 is now an ILLEGAL_DEV_TABLE_ENTRY and the request is aborted; so is sATS=0 when the IOMMU lacks SnpPageMode0Sup (dmakit applies this to I=1 devices), and sATS=1 with Mode=0 (§2.12.2.4).
Translation. The host's table (and the guest's, with a PASID) turns the IOVA into an SPA; a missing or read-only entry is an IO_PAGE_FAULT, as without SNP. There is no exclusion range any more (§2.12.2.3).
RMP check. The SPA's page must be hypervisor-owned (§2.12.1). If not: target abort and RMP_PAGE_FAULT for DMA; R=W=0 and RMP_PAGE_FAULT for a translation request (Table 54).
Memory. Only now do the bytes move.
"Hypervisor-owned" is the point: to an ordinary device, a guest's
private page is off limits however correct its mapping. A device may
reach private memory only as a trusted device interface
(SEV-TIO).
The fault record
The panel above takes the last RMP_PAGE_FAULT apart bit by bit
(Figure 69, Table 71). Flags says which RMP checks failed; AT says
which kind of request it was; SPA says the address is an SPA. The
record has no DomainID and no PASID, so it cannot say which
translation produced the address. The last line shows how Linux 7.3
prints the same 16 bytes: it reads an older layout of this record.
Request
If the RMP check fails
Untranslated DMA
Target abort; RMP_PAGE_FAULT; interrupt if enabled (Table 54).
ATS translation request
Answered with R=W=0; RMP_PAGE_FAULT (Table 54).
Pretranslated DMA
RMP_PAGE_FAULT with AT=10b (Table 71); the model target-aborts.
Turning SNP on, step by step
BIOS decides whether the system may use SEV-SNP at all.
The IOMMU driver refuses SNP if SnpPageMode0Sup=0 and the default domain is passthrough or uses v2 tables, both of which need Mode=0 or GCR3-only entries (Linux 7.3); checks 0030h[SNPSup]; points 0020h/0028h at its completion page (iommu_set_cwwb_range); and gives every unused device V=1 without TV (init_device_table_dma).
The hypervisor sends SNP_INIT to the AMD Secure Processor. From then on the IOMMU is SNP-enabled: registers locked, the completion page and the logs immutable.
SNP-aware guests may be launched.
The event and PPR logs cannot be written by the CPU any more, so the
driver stops clearing consumed entries (Linux's comments on errata 732
and 733). Undoing SNP (amd_iommu_snp_disable) makes
exactly those pages shared again.
Secure ATS
Ordinary ATS
Secure ATS (DTE[SATS]=1)
Translation reply
The SPA
The GPA before the last stage
A translated request
Passed on after DTE[I]=1 is checked
Translated again, GPA → SPA
A device that forges an address
Reaches any page the RMP allows (under SNP, any host page)
Reaches only what the host table maps
Needs
DTE[I]=1
DTE[I]=1, 0030h[SATSSup]=1, a host table (Mode ≠ 0)
"Secure" here means the device never holds an SPA; nothing is
encrypted.
What did the request actually use?
Bounce buffers: how ordinary devices still work
A confidential VM's driver cannot let a NIC write its private page,
so Linux copies through a shared bounce buffer (SWIOTLB): for a
receive, the device writes B and the CPU copies B to O; for a send,
the CPU copies O to B first. The copies are the CPU's, not the
IOMMU's, and whatever reaches B is visible to the host. The
Linux DMA buffers lab runs these
copies byte by byte, and the
buffer workbook asks what the host
can read at each step.
Copy only the intended length (E15); check a length the device reports against the mapping before using it (E16).
Copy a shared descriptor into private memory before checking it, and use the copy: the host can change B after you looked (E17).
If no bounce buffer is free, the mapping fails before any DMA: no fault is logged because no request was made (E18).