iommu: DMA-region capabilities — per-grant reachability, protocol flag-day

Replaces L2's interim DMA pool (every buffer reachable by every claimed
device) with true per-grant confinement: a device reaches only buffers
whose capability was delegated to its driver.

Kernel:
- DmaRegionObject (handle kind 2): a delegation token naming a dma_alloc'd
  region, passable across processes on the IPC cap slot like an endpoint
  or shared-memory object. Frames stay owned by the allocating address
  space (freed on dma_free/teardown as before); the token carries a `dead`
  flag so a stale downstream handle can no longer bind a freed region.
- dma_alloc gains the dma_shareable flag: it returns a capability handle
  in r8 and every region is tracked in a registry. A task's own regions
  auto-bind into the devices it claims (its rings just work); foreign
  buffers are bound explicitly.
- dma_bind / dma_unbind / handle_close syscalls (51-53). dma_bind maps a
  held region (or shared-memory) capability into a claimed device's domain;
  it is idempotent. handle_close reclaims a table slot (raised 16 -> 32).
- dma_free and task death unmap a region from every domain and invalidate
  BEFORE its frames return to the allocator — the stale-IOTLB use-after-
  free window, closed structurally.

Protocols (flag-day): block gains attach, usb-transfer gains dma_attach —
each carries a region capability on the cap slot. fat allocates its bounce
buffer shareable and attaches it; usb-storage allocates its transport
buffers shareable, attaches them to the controller, and forwards fat's
capability downstream; usb-xhci-bus binds and closes; virtio-gpu binds its
shared scanout surface. The physical addresses on the wire are unchanged
(identity IOVA), so no register-programming code moved.

Cross-process DMA (fat -> usb-storage -> xHC) now flows only through
delegated capabilities. iommu-usb-storage / iommu-usb-hid / iommu-fault
all green under per-grant enforcement; 104/104 overall (fail-open paths
unchanged).
This commit is contained in:
Daniel Samson
2026-07-26 18:31:27 +01:00
parent e94adcfc02
commit 4e7cbc9792
17 changed files with 414 additions and 64 deletions
+8 -1
View File
@@ -7,7 +7,9 @@
//! buffer**, named by its physical address — the same physical-address handoff
//! usb-storage already uses toward the controller, one layer up. So a 512-byte
//! sector never has to cross the 256-byte IPC boundary; only the small request /
//! reply headers do. (Safe while the IOMMU is unenforced; see docs/driver-model.md.)
//! reply headers do. Under an enforcing IOMMU the buffer's physical addresses are
//! only reachable by the device once the filesystem has `attach`ed the buffer's
//! capability (the block server forwards it to the controller); see docs/driver-model.md.
pub const Operation = enum(u32) {
/// geometry() -> { block_size, block_count }
@@ -20,6 +22,11 @@ pub const Operation = enum(u32) {
/// A filesystem calls this to make prior writes durable — e.g. before power-off,
/// so a shutdown-time write isn't lost in the USB flash controller's cache.
flush = 3,
/// attach(): the caller's DMA-region capability rides the call's cap slot; the
/// block server forwards it to the controller so the buffer's physical addresses
/// (named in later read/write) are reachable by the device under an enforcing
/// IOMMU. Call once per buffer before using it in a transfer.
attach = 4,
};
pub const Request = extern struct {