//! virtio 1.0 PCI transport — the vendor capabilities in PCI config space that point at the //! device's structures (common config, notify, ISR) in a BAR, the common-config register //! block, and the split-virtqueue layout. `extern` structs matching the spec. Host-tested //! for size. See docs/display-v2.md. const std = @import("std"); /// PCI vendor-specific capability id (0x09) — virtio 1.0 structures are advertised as these. pub const pci_cap_vendor: u8 = 0x09; /// virtio_pci_cap `cfg_type`: which structure a vendor capability points at. pub const cfg_common: u8 = 1; pub const cfg_notify: u8 = 2; pub const cfg_isr: u8 = 3; pub const cfg_device: u8 = 4; pub const cfg_pci: u8 = 5; /// virtio_pci_cap — a vendor capability naming a structure at (bar, offset, length) within /// a PCI BAR. Read straight out of config space. pub const PciCap = extern struct { cap_vndr: u8, // 0x09 cap_next: u8, // next capability's offset in config space (0 = end) cap_len: u8, cfg_type: u8, // cfg_common / cfg_notify / ... bar: u8, // which BAR the structure lives in padding: [3]u8, offset: u32, // offset within the BAR length: u32, // length of the structure }; /// virtio_pci_notify_cap: a notify capability carries a multiplier after the base cap; the /// per-queue notify address is `notify_base + queue_notify_off * notify_off_multiplier`. pub const NotifyCap = extern struct { cap: PciCap, notify_off_multiplier: u32, }; /// virtio_pci_common_cfg — the common configuration register block (little-endian MMIO). pub const CommonCfg = extern struct { device_feature_select: u32, device_feature: u32, driver_feature_select: u32, driver_feature: u32, msix_config: u16, num_queues: u16, device_status: u8, config_generation: u8, queue_select: u16, queue_size: u16, queue_msix_vector: u16, queue_enable: u16, queue_notify_off: u16, queue_desc: u64, queue_driver: u64, queue_device: u64, }; /// device_status bits (written to `CommonCfg.device_status` during bring-up). pub const status_acknowledge: u8 = 1; pub const status_driver: u8 = 2; pub const status_driver_ok: u8 = 4; pub const status_features_ok: u8 = 8; /// VIRTIO_F_VERSION_1 — feature bit 32 (in the second 32-bit feature word). Required for a /// modern device; we negotiate exactly this bit and nothing else. pub const feature_version_1_word: u32 = 1; // device_feature_select value for bits 32..63 pub const feature_version_1_bit: u32 = 1 << 0; // bit 32 within that word // --- split virtqueue ------------------------------------------------------- pub const Desc = extern struct { addr: u64, // guest-physical len: u32, flags: u16, next: u16, }; pub const desc_flag_next: u16 = 1; // buffer continues in `next` pub const desc_flag_write: u16 = 2; // device-writable (else driver-writable/device-readable) /// The available ring's fixed header; a `[queue_size]u16` ring and a trailing `used_event` /// u16 follow it in memory (laid out by the driver). pub const AvailHdr = extern struct { flags: u16, idx: u16, }; /// One entry of the used ring. pub const UsedElem = extern struct { id: u32, len: u32, }; /// The used ring's fixed header; a `[queue_size]UsedElem` ring and a trailing `avail_event` /// u16 follow it. pub const UsedHdr = extern struct { flags: u16, idx: u16, }; test "virtio-pci struct sizes match the spec" { try std.testing.expectEqual(@as(usize, 16), @sizeOf(PciCap)); try std.testing.expectEqual(@as(usize, 56), @sizeOf(CommonCfg)); try std.testing.expectEqual(@as(usize, 16), @sizeOf(Desc)); try std.testing.expectEqual(@as(usize, 8), @sizeOf(UsedElem)); }