virtio/
spec.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Constants defined by the virtio spec
5
6use bitfield_struct::bitfield;
7use inspect::Inspect;
8use zerocopy::FromBytes;
9use zerocopy::Immutable;
10use zerocopy::IntoBytes;
11use zerocopy::KnownLayout;
12
13pub use packed_nums::*;
14
15#[expect(non_camel_case_types)]
16mod packed_nums {
17    pub type u16_le = zerocopy::U16<zerocopy::LittleEndian>;
18    pub type u32_le = zerocopy::U32<zerocopy::LittleEndian>;
19    pub type u64_le = zerocopy::U64<zerocopy::LittleEndian>;
20}
21
22#[bitfield(u32)]
23#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
24pub struct VirtioDeviceFeaturesBank0 {
25    #[bits(24)]
26    pub device_specific: u32,
27    #[bits(4)]
28    _reserved1: u8,
29    pub ring_indirect_desc: bool, // VIRTIO_F_INDIRECT_DESC
30    pub ring_event_idx: bool,     // VIRTIO_F_EVENT_IDX
31    #[bits(2)]
32    _reserved2: u8,
33}
34
35#[bitfield(u32)]
36#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
37pub struct VirtioDeviceFeaturesBank1 {
38    pub version_1: bool,         // VIRTIO_F_VERSION_1
39    pub access_platform: bool,   // VIRTIO_F_ACCESS_PLATFORM
40    pub ring_packed: bool,       // VIRTIO_F_RING_PACKED
41    pub in_order: bool,          // VIRTIO_F_IN_ORDER
42    pub order_platform: bool,    // VIRTIO_F_ORDER_PLATFORM
43    pub sriov: bool,             // VIRTIO_F_SR_IOV
44    pub notification_data: bool, // VIRTIO_F_NOTIFICATION_DATA
45    pub notif_config_data: bool, // VIRTIO_F_NOTIF_CONFIG_DATA
46    pub ring_reset: bool,        // VIRTIO_F_RING_RESET
47    pub admin_vq: bool,          // VIRTIO_F_ADMIN_VQ
48    pub device_specific_bit_42: bool,
49    pub suspend: bool, // VIRTIO_F_SUSPEND
50    #[bits(7)]
51    _reserved: u8,
52    #[bits(13)]
53    pub device_specific: u16,
54}
55
56#[derive(Debug, Clone)]
57pub struct VirtioDeviceFeatures(Vec<u32>);
58impl VirtioDeviceFeatures {
59    pub fn new() -> Self {
60        Self(Vec::with_capacity(2))
61    }
62
63    pub fn len(&self) -> usize {
64        self.0.len()
65    }
66
67    pub fn set_bank(&mut self, index: usize, val: u32) {
68        if self.0.len() <= index {
69            self.0.resize(index + 1, 0);
70        }
71        self.0[index] = val;
72    }
73
74    pub fn with_bank(mut self, index: usize, val: u32) -> Self {
75        self.set_bank(index, val);
76        self
77    }
78
79    pub fn with_bank0(self, bank0: VirtioDeviceFeaturesBank0) -> Self {
80        self.with_bank(0, bank0.into_bits())
81    }
82
83    pub fn with_bank1(self, bank1: VirtioDeviceFeaturesBank1) -> Self {
84        self.with_bank(1, bank1.into_bits())
85    }
86
87    pub fn bank(&self, index: usize) -> u32 {
88        self.0.get(index).map_or(0, |x| *x)
89    }
90
91    pub fn bank0(&self) -> VirtioDeviceFeaturesBank0 {
92        VirtioDeviceFeaturesBank0::from_bits(self.bank(0))
93    }
94
95    pub fn bank1(&self) -> VirtioDeviceFeaturesBank1 {
96        VirtioDeviceFeaturesBank1::from_bits(self.bank(1))
97    }
98}
99
100impl Default for VirtioDeviceFeatures {
101    fn default() -> Self {
102        Self::new()
103    }
104}
105
106#[bitfield(u8)]
107#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
108pub struct VirtioDeviceStatus {
109    pub acknowledge: bool,
110    pub driver: bool,
111    pub driver_ok: bool,
112    pub features_ok: bool,
113    pub suspend: bool,
114    _reserved1: bool,
115    pub device_needs_reset: bool,
116    pub failed: bool,
117}
118
119impl VirtioDeviceStatus {
120    pub fn as_u32(&self) -> u32 {
121        self.into_bits() as u32
122    }
123}
124
125// ACPI interrupt status flags
126pub const VIRTIO_MMIO_INTERRUPT_STATUS_USED_BUFFER: u32 = 1;
127pub const VIRTIO_MMIO_INTERRUPT_STATUS_CONFIG_CHANGE: u32 = 2;
128
129/// Virtio over PCI specific constants
130pub mod pci {
131    use open_enum::open_enum;
132
133    open_enum! {
134        /// Virtio PCI capability config type.
135        pub enum VirtioPciCapType: u8 {
136            COMMON_CFG = 1,
137            NOTIFY_CFG = 2,
138            ISR_CFG = 3,
139            DEVICE_CFG = 4,
140            // PCI_CFG = 5,
141            SHARED_MEMORY_CFG = 8,
142        }
143    }
144
145    pub const VIRTIO_VENDOR_ID: u16 = 0x1af4;
146    pub const VIRTIO_PCI_DEVICE_ID_BASE: u16 = 0x1040;
147
148    open_enum! {
149        /// Byte offsets within the `virtio_pci_common_cfg` structure,
150        /// accessed at u32-aligned boundaries.
151        pub enum VirtioPciCommonCfg: u16 {
152            DEVICE_FEATURE_SELECT = 0,
153            DEVICE_FEATURE = 4,
154            DRIVER_FEATURE_SELECT = 8,
155            DRIVER_FEATURE = 12,
156            MSIX_CONFIG = 16,
157            DEVICE_STATUS = 20,
158            QUEUE_SIZE = 24,
159            QUEUE_ENABLE = 28,
160            QUEUE_DESC_LO = 32,
161            QUEUE_DESC_HI = 36,
162            QUEUE_AVAIL_LO = 40,
163            QUEUE_AVAIL_HI = 44,
164            QUEUE_USED_LO = 48,
165            QUEUE_USED_HI = 52,
166        }
167    }
168
169    /// Total size of the common configuration structure.
170    pub const VIRTIO_PCI_COMMON_CFG_SIZE: u16 = 56;
171}
172
173/// Virtio over MMIO register offsets (virtio spec section 4.2.2)
174pub mod mmio {
175    use open_enum::open_enum;
176
177    open_enum! {
178        /// MMIO register offsets for virtio MMIO transport.
179        pub enum VirtioMmioRegister: u16 {
180            MAGIC_VALUE = 0x000,
181            VERSION = 0x004,
182            DEVICE_ID = 0x008,
183            VENDOR_ID = 0x00c,
184            DEVICE_FEATURES = 0x010,
185            DEVICE_FEATURES_SEL = 0x014,
186            DRIVER_FEATURES = 0x020,
187            DRIVER_FEATURES_SEL = 0x024,
188            QUEUE_SEL = 0x030,
189            QUEUE_NUM_MAX = 0x034,
190            QUEUE_NUM = 0x038,
191            QUEUE_READY = 0x044,
192            QUEUE_NOTIFY = 0x050,
193            INTERRUPT_STATUS = 0x060,
194            INTERRUPT_ACK = 0x064,
195            STATUS = 0x070,
196            QUEUE_DESC_LOW = 0x080,
197            QUEUE_DESC_HIGH = 0x084,
198            QUEUE_AVAIL_LOW = 0x090,
199            QUEUE_AVAIL_HIGH = 0x094,
200            QUEUE_USED_LOW = 0x0a0,
201            QUEUE_USED_HIGH = 0x0a4,
202            CONFIG_GENERATION = 0x0fc,
203            CONFIG = 0x100,
204        }
205    }
206}
207
208/// Virtio queue definitions.
209pub mod queue {
210    use super::u16_le;
211    use super::u32_le;
212    use super::u64_le;
213    use bitfield_struct::bitfield;
214
215    use zerocopy::FromBytes;
216    use zerocopy::Immutable;
217    use zerocopy::IntoBytes;
218    use zerocopy::KnownLayout;
219
220    #[repr(C)]
221    #[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
222    pub struct SplitDescriptor {
223        pub address: u64_le,
224        pub length: u32_le,
225        pub flags_raw: u16_le,
226        pub next: u16_le,
227    }
228
229    impl SplitDescriptor {
230        pub fn flags(&self) -> DescriptorFlags {
231            self.flags_raw.get().into()
232        }
233    }
234
235    #[bitfield(u16)]
236    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
237    pub struct DescriptorFlags {
238        pub next: bool,
239        pub write: bool,
240        pub indirect: bool,
241        #[bits(13)]
242        _reserved: u16,
243    }
244
245    /*
246    struct virtq_avail {
247        le16 flags;
248        le16 idx;
249        le16 ring[ /* Queue Size */ ];
250        le16 used_event;
251    }
252    */
253    pub const AVAIL_OFFSET_FLAGS: u64 = 0;
254    pub const AVAIL_OFFSET_IDX: u64 = 2;
255    pub const AVAIL_OFFSET_RING: u64 = 4;
256    pub const AVAIL_ELEMENT_SIZE: u64 = size_of::<u16>() as u64;
257
258    #[bitfield(u16)]
259    pub struct AvailableFlags {
260        pub no_interrupt: bool,
261        #[bits(15)]
262        _reserved: u16,
263    }
264
265    /*
266    struct virtq_used {
267        le16 flags;
268        le16 idx;
269        struct virtq_used_elem ring[ /* Queue Size */];
270        le16 avail_event;
271    };
272    */
273    pub const USED_OFFSET_FLAGS: u64 = 0;
274    pub const USED_OFFSET_IDX: u64 = 2;
275    pub const USED_OFFSET_RING: u64 = 4;
276    pub const USED_ELEMENT_SIZE: u64 = size_of::<UsedElement>() as u64;
277
278    #[repr(C)]
279    #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
280    pub struct UsedElement {
281        pub id: u32_le,
282        pub len: u32_le,
283    }
284
285    #[bitfield(u16)]
286    pub struct UsedFlags {
287        pub no_notify: bool,
288        #[bits(15)]
289        _reserved: u16,
290    }
291}