danos/system/drivers/virtio-gpu/virtio-pci.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));
}