# 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`](virtio-net.md)) and the single-request-queue bring-up shape of
[`virtio-blk`](virtio-blk.md); 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](../proposals/virtio-9p-host-directory-proposal.md),
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**:
  - *Virtual I/O Device (VIRTIO) Version 1.2*, OASIS Committee Specification 01
    (2022-07-01).
    Source: <https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.html>.
    Relevant sections: 4.1 (virtio over PCI bus), 2.7 (split virtqueues),
    5.7 (9P transport device).
  - *9P2000.L* protocol, the Linux-flavoured 9P dialect.
    Source: <https://github.com/chaos/diod/blob/master/protocol.md>.
    The version string the driver negotiates is exactly `9P2000.L`
    (`capos_lib::ninep::VERSION`).
- **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](virtio-net.md) 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.

## Related

- 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](../proposals/virtio-9p-host-directory-proposal.md).
- Shared transport seam: [`virtio-net`](virtio-net.md),
  [`virtio-blk`](virtio-blk.md).
