Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Proposal: Virtio-9p Host Directory Passthrough

A QEMU-only userspace driver that mounts a host directory into a capOS guest as Directory/File capabilities over the virtio-9p device, using the 9P2000.L protocol. Read-only first; a separate write-support slice follows. Like every other QEMU fixture source, the grant fails closed in non-qemu manifests: this is development infrastructure, never a production path.

Motivation

Three concrete needs, in priority order:

  1. Dev-loop payload injection. Today every guest-visible input — WASI payloads, Lua scripts, test corpora, fixture files — must be baked into the boot ISO or a disk image (tools/mkstore-image) before boot. Changing one test input costs a full ISO rebuild. A host-shared directory removes that rebuild from the inner loop.
  2. Artifact export from proofs (write slice). QEMU proofs assert by grepping serial-log markers. With guest→host file export, a proof can write structured results (reports, dumps, benchmark tables) that host harnesses read directly — richer assertions than ordered-marker matching, and a natural output channel for a future declarative proof runner.
  3. Task-backend persistence shortcut. The Self-Hosted Task Backend track aims to replace the host-side PostgreSQL task board with a capOS-served coordinator. Its durable serve loop is currently blocked on a spawn-grant authority gap for BlockDevice caps; Directory caps already have a landed spawn-grant path (make run-spawn-grant-directory). A 9p-backed Directory gives the coordinator durable, host-inspectable state files through the grant path that exists today.

What Exists Today

Directory/File/Store/Namespace are established Cap’n Proto interfaces with several backers: the RAM-backed kernel fixtures (kernel/src/cap/ directory.rs, file.rs, store.rs, namespace.rs), the block-device filesystems (readonly_fs, fat_fs, persistent_store, writable_fs), and the boot-ISO reader (installable_image). All disk-backed paths require an image prepared host-side before boot. There is no host-directory passthrough of any kind in the tree.

The transport groundwork exists: capos-rt’s userspace driver track has landed polled virtio drivers over the device-agnostic modern-PCI surface (kernel/src/virtio_transport.rs) using DDF grants (DeviceMmio/DMAPool/Interrupt), proven by the virtio-net and NVMe userspace driver proofs.

Design

  • Device: virtio-9p (-fsdev local,path=<hostdir>,security_model=none, readonly=on -device virtio-9p-pci,mount_tag=...). Fully emulated inside QEMU; no external daemon.
  • Protocol: 9P2000.L, minimal client subset. Read path: Tversion/Tattach/Twalk/Tlopen/Tread/Treaddir/Tgetattr/ Tclunk. Write slice adds Tlcreate/Twrite/Tfsync/Trename/ Tunlinkat. Message codec is pure no_std logic in capos-lib style: host-tested, fuzzable, bounded, fail-closed on malformed replies.
  • Driver shape: a userspace driver process (the established DDF pattern), polled — no MSI-X dependency, matching the landed polled virtio-net driver default. A dev fixture has no latency requirement that justifies interrupt plumbing.
  • Capability surface: the driver serves the existing Directory/File interfaces over Endpoint IPC — consumers (shell ls/cat, wasm-host, the task coordinator) need no new interface. Read-only exports serve a Directory whose mutating methods fail closed.
  • Manifest gating: the grant source is qemu-gated and fails closed in non-qemu manifests, exactly like the kernel socket and virtio-blk fixture sources. Write-enabled exports additionally require an explicit manifest flag so a read-only share cannot be silently upgraded.

Why 9p and not virtiofs

virtiofs needs an external virtiofsd daemon, vhost-user shared-memory plumbing, and a guest FUSE client — an order of magnitude more machinery whose payoff is performance, which is irrelevant for a development fixture. virtio-9p is built into QEMU, its transport is plain virtqueues over the already-landed modern-PCI surface, and the 9P2000.L request/reply messages map nearly 1:1 onto the Directory/File capability methods. Other rejected alternatives: fw_cfg (single small blobs, no directory semantics) and keeping the disk-image-only workflow (remains correct for production-shaped storage proofs, but leaves the dev loop and the spawn-grant shortcut on the table).

Task-Backend Shortcut: Honest Trade-offs

The CAPOSRS1 WAL record store over BlockDevice already provides durable, crash-recovering coordinator persistence (make run-task-coordinator-persist) — 9p adds no new durability capability. What it adds:

  • A grant path that works now. Directory spawn-grants are landed; the BlockDevice spawn-grant gap blocks the durable Endpoint serve loop.
  • Host-inspectable state. Record-per-file layout (one file per task, atomic Trename commit) makes the board state a plain host directory: greppable, diffable, backed up or versioned with ordinary tools, no image mounting.
  • Operational simplicity for self-hosting. A capOS-in-QEMU task backend whose state survives VM restarts in a host directory is a realistic dogfooding deployment shape for the multi-agent workflow.

Limits to state plainly: durability depends on the host filesystem and QEMU’s Tfsync handling, weaker than the fenced CAPOSRS1 frame guarantees — the WAL-over-BlockDevice path remains the reference persistence design, and the 9p path is a pragmatic parallel track, not a replacement. Single writer only. QEMU-only by construction; a production deployment still needs the NVMe/ writable_fs path or the future userspace storage service.

Decomposition

Tracked as loopyard tasks; each behavior slice carries its QEMU proof and the driver slices maintain a docs/devices/virtio-9p.md provenance map as part of the same change:

  1. virtio-9p transport bring-up — modern-PCI probe, feature negotiation, virtqueue setup over virtio_transport, Tversion/Tattach handshake proof.
  2. 9P2000.L client core — host-tested no_std message codec (read subset), bounded fail-closed decode, fuzz target.
  3. Directory/File caps over 9p — read-only Directory/File served over Endpoint IPC from a host fixture directory; shell ls/cat smoke; fail-closed non-qemu proof.
  4. Write support — write-path messages, explicit manifest write flag, host-verified write/rename/fsync smoke.
  5. Task-coordinator 9p persistence — record-per-file durable serve loop over a spawn-granted 9p Directory, proven across VM restart with host-visible state files.
  6. WASI payload hot-reloadwasm-host loads its payload from the share; rebuild on host, re-run in guest, no ISO rebuild.

Security Considerations

The guest→host boundary is the new surface. Mitigations: the share is scoped to one dedicated host directory; QEMU-level readonly=on mirrors the capability-level read-only export (defense in depth); security_model=none maps all access to the QEMU process’s own uid — the guest can never act as another host principal; write-enabled shares are opt-in per manifest and should point only at dedicated scratch/state directories. The 9p client treats the QEMU server as untrusted input: all replies are bounded and fail closed, and reply parsing is host-fuzzed like the other mount/wire parsers.