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

virtio-9p (modern PCI 9P transport)

This is a provenance map for the in-tree virtio-9p driver: it cites the specs, summarizes only the wire-format subset the code actually implements, and points into the implementation. It is not a re-spec. The driver reuses the modern split-ring transport seam introduced for virtio-net (virtio-net) and the single-request-queue bring-up shape of virtio-blk; this page covers only the 9p-specific parts.

Status: QEMU fixture that currently grants no userspace authority. The driver, its PCI discovery, and its DMA ledger are gated behind the qemu cargo feature (diagnose_qemu_virtio_9p in kernel/src/pci.rs; kernel/src/virtio.rs is replaced by kernel/src/virtio_stub.rs in non-qemu builds). What is implemented today is transport bring-up plus the 9P2000.L session handshake, observable only as kernel serial diagnostics. No Directory or File capability is served over 9p and no 9p kernel grant source exists, so no userspace process can reach the host share through any manifest. The intended use is dev-loop payload injection and artifact export, per the host-directory proposal, whose “Driver Boundary Decision” section records why this is a kernel-side fixture (Option A) rather than a userspace driver process.

The driver lives in the virtio-9p section of kernel/src/virtio.rs (Virtio9pDriver), and the protocol encoding/decoding is the transport-free client codec capos_lib::ninep (capos-lib/src/ninep.rs).

1. Spec basis

  • Device: virtio 9P transport device, modern (virtio 1.x) PCI transport. PCI vendor 0x1af4; device 0x1049 (modern) / 0x1009 (transitional). IDs at kernel/src/pci.rs (VIRTIO_VENDOR_ID, VIRTIO_9P_MODERN_DEVICE_ID, VIRTIO_9P_TRANSITIONAL_DEVICE_ID; matched by PciDevice::is_virtio_9p). Exactly one function is bound, into the single VIRTIO_9P_DRIVER slot; a second discovered function is refused rather than silently rebinding.
  • Authoritative specs:
  • Reference: cross-checked against the QEMU 9p server (hw/9pfs/9p.c) for the session-establishment behavior, and the Linux 9pnet_virtio transport for the descriptor-chain shape.

2. Wire format (implemented subset)

  • Transport discovery and negotiation: unchanged from the shared modern-PCI seam – vendor-specific capability walk, common/notify/ISR/device-config region selection, and the reset -> ACKNOWLEDGE -> DRIVER -> feature-negotiation -> FEATURES_OK -> DRIVER_OK handshake. See virtio-net §2 for the seam itself (kernel/src/virtio_transport.rs).
  • Features: VIRTIO_F_VERSION_1 and VIRTIO_9P_F_MOUNT_TAG are both required and both selected; nothing else is accepted. A device that does not offer both fails bring-up closed (Ninep9pInitError::MissingRequiredFeatures).
  • Device configuration: struct virtio_9p_config – a u16 tag length followed by that many mount-tag bytes. The driver maps exactly the length the transport capability advertises and validates the tag length against both VIRTIO_9P_MAX_TAG_LEN and the bytes actually present before reading the tag (Ninep9pInitError::MountTagOutOfRange).
  • Queues: one request virtqueue (index 0), a split ring built by the shared Virtqueue::initialize. Size is clamped to the largest power of two not exceeding VIRTIO_9P_REQUEST_QUEUE_SIZE and the device maximum. Completion is polled (QueueInterruptPlan::polled); no MSI-X vector is programmed.
  • Descriptor chain: each 9p exchange is a two-descriptor chain – a device-readable request segment followed by a device-writable reply segment, each backed by its own dedicated DMA page so the two cannot alias (Virtio9pDriver::exchange).
  • Messages: only session establishment is implemented.
    • Tversion/Rversion – negotiates msize and the 9P2000.L version string. The requested msize is VIRTIO_9P_MSIZE (4096); QEMU’s server rejects anything below its P9_MIN_MSIZE of 4096 with Rlerror(EMSGSIZE). The server may shrink msize, and every later message is bounded by what it actually granted.
    • Tattach/Rattach – establishes the share’s root fid (VIRTIO_9P_ROOT_FID) and yields its qid.
    • Rlerror is accepted in place of either success reply and surfaces as Virtio9pHandshakeError::ServerError.
  • Not implemented: walk, open, read, readdir, getattr, and clunk are encoded and decoded by capos_lib::ninep but are not yet driven by the driver; the entire write path (Tlcreate/Twrite/Tfsync/Trename/Tunlinkat) is not implemented at all. Multiple concurrent tags, Tflush, and Tauth are out of scope: the fixture keeps one exchange in flight at a time.

3. capOS mapping

  • Authority gate: the device is enumerated by the qemu-gated diagnose_qemu_virtio_9p (kernel/src/pci.rs), which claims the PCI function through the device-manager ownership ledger as DeviceOwner::Virtio9p and attaches a DMAPool authority record. Manifests without a -device virtio-9p-pci line take the pci: virtio-9p device not found path, so this is a diagnostic and never a boot dependency.
  • DeviceMmio: the common, ISR, notify, and device-config regions are mapped through pci::map_bar_region with the same bounds validation the other virtio drivers use; the device-config region is mapped to exactly the advertised capability length because the 9p config is variable-length. No DeviceMmio capability is granted to userspace.
  • Interrupt: none. The fixture polls its used ring, so it deliberately claims no MSI-X route – interrupt_owner_for_device_owner maps DeviceOwner::Virtio9p to None, and the bring-up diagnostic records interrupt=none completion=polled-used-ring. No Interrupt capability exists for this device.
  • DMAPool: the driver owns a dedicated single-queue DMA ledger (device_dma.rs, Virtio9pPoolConfig), a distinct type from the virtio-blk and virtio-net pools, so a page handle minted against one never validates against another. It holds five pages: the three split-ring pages plus one request and one reply bounce page. The ledger enforces generation-checked page handles and scrub-before-frame-free ordering. Host physical addresses stay kernel-owned; none is exposed to userspace, and there is no userspace consumer to expose them to.
  • Fail-closed / validation rules: bring-up fails closed and leaves the driver unpublished on any of – missing required features, rejected FEATURES_OK, an undersized or oversized device-config/mount-tag, an absent or too-small request queue, or a failed handshake. Every reply is bounded before decoding: the device-reported written length must be at least a 9p header and no larger than the bring-up scratch buffer, and the reply’s size prefix is cleared before each exchange so stale page contents cannot be decoded as a fresh reply. The capos_lib::ninep codec then independently validates the frame against the negotiated msize, the declared size, the expected tag, and the expected reply type before exposing a field. The host-supplied mount tag is rendered as printable ASCII only, so a hostile tag cannot forge or split a diagnostic line.
  • QEMU-emulable vs hardware-only: entirely QEMU-emulable. virtio-9p is a paravirtual device with no hardware counterpart, so there is no hardware-only residue. The end-to-end proof is make test-virtio-9p-bringup (tools/qemu-virtio-9p-smoke.sh, manifests/system-virtio-9p.cue), which asserts the negotiated msize and version, the mount tag read back from device config, and the attach qid’s directory type bit.
  • Implementation: kernel/src/virtio.rs (Virtio9pDriver, Virtio9pDma, diagnose_virtio_9p_transport), kernel/src/pci.rs (diagnose_qemu_virtio_9p, PciDevice::is_virtio_9p), kernel/src/device_dma.rs (Virtio9pPoolConfig, SingleQueueDmaLedger).
  • Protocol codec: capos-lib/src/ninep.rs; fuzz target fuzz/fuzz_targets/ninep_reply_decode.rs.
  • Design: Virtio-9p Host Directory Passthrough.
  • Shared transport seam: virtio-net, virtio-blk.