106 lines
3.6 KiB
Zig
106 lines
3.6 KiB
Zig
//! 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));
|
|
}
|