virtio-spec 0.3.2

Definitions from the Virtual I/O Device (VIRTIO) specification.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
//! Definitions for Virtio over PCI bus.

use core::mem;

use num_enum::{FromPrimitive, IntoPrimitive};
use pci_types::capability::PciCapabilityAddress;
use pci_types::ConfigRegionAccess;
use volatile::access::{ReadOnly, ReadWrite, Readable, RestrictAccess};
use volatile::VolatilePtr;
use volatile_macro::VolatileFieldAccess;

pub use crate::driver_notifications::NotificationData;
use crate::volatile::WideVolatilePtr;
use crate::{le16, le32, le64, DeviceConfigSpace, DeviceStatus};

/// PCI Capability
///
/// See [`CapData`] for reading additional fields.
#[doc(alias = "virtio_pci_cap")]
#[cfg_attr(
    feature = "zerocopy",
    derive(
        zerocopy_derive::KnownLayout,
        zerocopy_derive::Immutable,
        zerocopy_derive::FromBytes,
    )
)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Cap {
    /// Generic PCI field: `PCI_CAP_ID_VNDR`
    ///
    /// 0x09; Identifies a vendor-specific capability.
    pub cap_vndr: u8,

    /// Generic PCI field: next ptr.
    ///
    /// Link to next capability in the capability list in the PCI configuration space.
    pub cap_next: u8,

    /// Generic PCI field: capability length
    ///
    /// Length of this capability structure, including the whole of
    /// struct virtio_pci_cap, and extra data if any.
    /// This length MAY include padding, or fields unused by the driver.
    pub cap_len: u8,

    /// Identifies the structure.
    ///
    /// Each structure is detailed individually below.
    ///
    /// The device MAY offer more than one structure of any type - this makes it
    /// possible for the device to expose multiple interfaces to drivers.  The order of
    /// the capabilities in the capability list specifies the order of preference
    /// suggested by the device.  A device may specify that this ordering mechanism be
    /// overridden by the use of the `id` field.
    ///
    /// <div class="warning">
    ///
    /// For example, on some hypervisors, notifications using IO accesses are
    /// faster than memory accesses. In this case, the device would expose two
    /// capabilities with `cfg_type` set to VIRTIO_PCI_CAP_NOTIFY_CFG:
    /// the first one addressing an I/O BAR, the second one addressing a memory BAR.
    /// In this example, the driver would use the I/O BAR if I/O resources are available, and fall back on
    /// memory BAR when I/O resources are unavailable.
    ///
    /// </div>
    pub cfg_type: u8,

    /// Where to find it.
    ///
    /// values 0x0 to 0x5 specify a Base Address register (BAR) belonging to
    /// the function located beginning at 10h in PCI Configuration Space
    /// and used to map the structure into Memory or I/O Space.
    /// The BAR is permitted to be either 32-bit or 64-bit, it can map Memory Space
    /// or I/O Space.
    ///
    /// Any other value is reserved for future use.
    pub bar: u8,

    /// Multiple capabilities of the same type
    ///
    /// Used by some device types to uniquely identify multiple capabilities
    /// of a certain type. If the device type does not specify the meaning of
    /// this field, its contents are undefined.
    pub id: u8,

    /// Pad to full dword.
    pub padding: [u8; 2],

    /// Offset within bar.
    ///
    /// indicates where the structure begins relative to the base address associated
    /// with the BAR.  The alignment requirements of `offset` are indicated
    /// in each structure-specific section below.
    pub offset: le32,

    /// Length of the structure, in bytes.
    ///
    /// indicates the length of the structure.
    ///
    /// `length` MAY include padding, or fields unused by the driver, or
    /// future extensions.
    ///
    /// <div class="warning">
    ///
    /// For example, a future device might present a large structure size of several
    /// MBytes.
    /// As current devices never utilize structures larger than 4KBytes in size,
    /// driver MAY limit the mapped structure size to e.g.
    /// 4KBytes (thus ignoring parts of structure after the first
    /// 4KBytes) to allow forward compatibility with such devices without loss of
    /// functionality and without wasting resources.
    ///
    /// </div>
    pub length: le32,
}

impl Cap {
    pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option<Self> {
        let data = unsafe { access.read(addr.address, addr.offset) };
        let [cap_vndr, _cap_next, cap_len, _cfg_type] = data.to_le_bytes();

        if cap_vndr != 0x09 {
            return None;
        }

        if cap_len < 16 {
            return None;
        }

        let data = [
            data,
            unsafe { access.read(addr.address, addr.offset + 4) },
            unsafe { access.read(addr.address, addr.offset + 8) },
            unsafe { access.read(addr.address, addr.offset + 12) },
        ];

        let data: [u32; 4] = data.map(u32::from_le);

        let this = unsafe { mem::transmute::<[u32; 4], Self>(data) };

        Some(this)
    }
}

/// PCI Capability 64
#[doc(alias = "virtio_pci_cap64")]
#[cfg_attr(
    feature = "zerocopy",
    derive(
        zerocopy_derive::KnownLayout,
        zerocopy_derive::Immutable,
        zerocopy_derive::FromBytes,
    )
)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Cap64 {
    pub cap: Cap,
    pub offset_hi: le32,
    pub length_hi: le32,
}

/// PCI Notify Capability
#[doc(alias = "virtio_pci_notify_cap")]
#[cfg_attr(
    feature = "zerocopy",
    derive(
        zerocopy_derive::KnownLayout,
        zerocopy_derive::Immutable,
        zerocopy_derive::FromBytes,
    )
)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct NotifyCap {
    pub cap: Cap,

    /// Multiplier for queue_notify_off.
    pub notify_off_multiplier: le32,
}

/// PCI Configuration Capability
#[doc(alias = "virtio_pci_cfg_cap")]
#[cfg_attr(
    feature = "zerocopy",
    derive(
        zerocopy_derive::KnownLayout,
        zerocopy_derive::Immutable,
        zerocopy_derive::FromBytes,
    )
)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct CfgCap {
    pub cap: Cap,

    /// Data for BAR access.
    pub pci_cfg_data: [u8; 4],
}

/// PCI Capability Data
#[derive(Clone, Copy, Debug)]
pub struct CapData {
    /// Identifies the structure.
    pub cfg_type: CapCfgType,

    /// Where to find it.
    pub bar: u8,

    /// Multiple capabilities of the same type
    pub id: u8,

    /// Offset within bar.
    pub offset: le64,

    /// Length of the structure, in bytes.
    pub length: le64,

    /// Multiplier for queue_notify_off.
    pub notify_off_multiplier: Option<le32>,
}

impl CapData {
    pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option<Self> {
        let cap = Cap::read(addr, &access)?;
        let cfg_type = CapCfgType::from(cap.cfg_type);

        let (offset, length) = match cfg_type {
            CapCfgType::SharedMemory => {
                if cap.cap_len < 24 {
                    return None;
                }

                let offset_hi = unsafe { access.read(addr.address, addr.offset + 16) };
                let offset_hi = le32::from_ne(offset_hi);
                let offset = le64::from([cap.offset, offset_hi]);

                let length_hi = unsafe { access.read(addr.address, addr.offset + 20) };
                let length_hi = le32::from_ne(length_hi);
                let length = le64::from([cap.length, length_hi]);

                (offset, length)
            }
            _ => (le64::from(cap.offset), le64::from(cap.length)),
        };

        let notify_off_multiplier = match cfg_type {
            CapCfgType::Notify => {
                if cap.cap_len < 20 {
                    return None;
                }

                let notify_off_multiplier = unsafe { access.read(addr.address, addr.offset + 16) };
                let notify_off_multiplier = le32::from_ne(notify_off_multiplier);

                Some(notify_off_multiplier)
            }
            _ => None,
        };

        Some(Self {
            cfg_type,
            bar: cap.bar,
            id: cap.id,
            offset,
            length,
            notify_off_multiplier,
        })
    }
}

/// PCI Capability Configuration Type
///
/// <div class="warning">
///
/// This enum is not ABI-compatible with it's corresponding field.
/// Use [`CapCfgType::from`] for converting from an integer.
///
/// </div>
///
/// [`CapCfgType::from`]: CapCfgType#impl-From<u8>-for-CapCfgType
#[doc(alias = "VIRTIO_PCI_CAP")]
#[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum CapCfgType {
    /// Common configuration
    #[doc(alias = "VIRTIO_PCI_CAP_COMMON_CFG")]
    Common = 1,

    /// Notifications
    #[doc(alias = "VIRTIO_PCI_CAP_NOTIFY_CFG")]
    Notify = 2,

    /// ISR Status
    #[doc(alias = "VIRTIO_PCI_CAP_ISR_CFG")]
    Isr = 3,

    /// Device specific configuration
    #[doc(alias = "VIRTIO_PCI_CAP_DEVICE_CFG")]
    Device = 4,

    /// PCI configuration access
    #[doc(alias = "VIRTIO_PCI_CAP_PCI_CFG")]
    Pci = 5,

    /// Shared memory region
    #[doc(alias = "VIRTIO_PCI_CAP_SHARED_MEMORY_CFG")]
    SharedMemory = 8,

    /// Vendor-specific data
    #[doc(alias = "VIRTIO_PCI_CAP_VENDOR_CFG")]
    Vendor = 9,

    /// Unknown device
    #[num_enum(catch_all)]
    Unknown(u8),
}

/// Common configuration structure
///
/// The common configuration structure is found at the bar and offset within the [`VIRTIO_PCI_CAP_COMMON_CFG`] capability.
///
/// [`VIRTIO_PCI_CAP_COMMON_CFG`]: CapCfgType::Common
///
/// Use [`CommonCfgVolatileFieldAccess`] and [`CommonCfgVolatileWideFieldAccess`] to work with this struct.
#[doc(alias = "virtio_pci_common_cfg")]
#[cfg_attr(
    feature = "zerocopy",
    derive(
        zerocopy_derive::KnownLayout,
        zerocopy_derive::Immutable,
        zerocopy_derive::FromBytes,
    )
)]
#[derive(VolatileFieldAccess)]
#[repr(C)]
pub struct CommonCfg {
    /// The driver uses this to select which feature bits `device_feature` shows.
    /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc.
    device_feature_select: le32,

    /// The device uses this to report which feature bits it is
    /// offering to the driver: the driver writes to
    /// `device_feature_select` to select which feature bits are presented.
    #[access(ReadOnly)]
    device_feature: le32,

    /// The driver uses this to select which feature bits `driver_feature` shows.
    /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc.
    driver_feature_select: le32,

    /// The driver writes this to accept feature bits offered by the device.
    /// Driver Feature Bits selected by `driver_feature_select`.
    driver_feature: le32,

    /// The driver sets the Configuration Vector for MSI-X.
    config_msix_vector: le16,

    /// The device specifies the maximum number of virtqueues supported here.
    #[access(ReadOnly)]
    num_queues: le16,

    /// The driver writes the device status here (see [`DeviceStatus`]). Writing 0 into this
    /// field resets the device.
    device_status: DeviceStatus,

    /// Configuration atomicity value.  The device changes this every time the
    /// configuration noticeably changes.
    #[access(ReadOnly)]
    config_generation: u8,

    /// Queue Select. The driver selects which virtqueue the following
    /// fields refer to.
    queue_select: le16,

    /// Queue Size.  On reset, specifies the maximum queue size supported by
    /// the device. This can be modified by the driver to reduce memory requirements.
    /// A 0 means the queue is unavailable.
    queue_size: le16,

    /// The driver uses this to specify the queue vector for MSI-X.
    queue_msix_vector: le16,

    /// The driver uses this to selectively prevent the device from executing requests from this virtqueue.
    /// 1 - enabled; 0 - disabled.
    queue_enable: le16,

    /// The driver reads this to calculate the offset from start of Notification structure at
    /// which this virtqueue is located.
    ///
    /// <div class="warning">
    ///
    /// This is _not_ an offset in bytes.
    /// See _Virtio Transport Options / Virtio Over PCI Bus / PCI Device Layout / Notification capability_ below.
    ///
    /// </div>
    #[access(ReadOnly)]
    queue_notify_off: le16,

    /// The driver writes the physical address of Descriptor Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
    queue_desc_low: le32,

    /// The driver writes the physical address of Descriptor Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
    queue_desc_high: le32,

    /// The driver writes the physical address of Driver Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
    queue_driver_low: le32,

    /// The driver writes the physical address of Driver Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
    queue_driver_high: le32,

    /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
    queue_device_low: le32,

    /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
    queue_device_high: le32,

    /// This field exists only if [`VIRTIO_F_NOTIF_CONFIG_DATA`] has been negotiated.
    /// The driver will use this value to put it in the 'virtqueue number' field
    /// in the available buffer notification structure.
    /// See section _Virtio Transport Options / Virtio Over PCI Bus / PCI-specific Initialization And Device Operation / Available Buffer Notifications_.
    ///
    /// <div class="warning">
    ///
    /// This field provides the device with flexibility to determine how virtqueues
    /// will be referred to in available buffer notifications.
    /// In a trivial case the device can set `queue_notify_data`=vqn. Some devices
    /// may benefit from providing another value, for example an internal virtqueue
    /// identifier, or an internal offset related to the virtqueue number.
    ///
    /// </div>
    ///
    /// [`VIRTIO_F_NOTIF_CONFIG_DATA`]: crate::F::NOTIF_CONFIG_DATA
    #[access(ReadOnly)]
    queue_notify_data: le16,

    /// The driver uses this to selectively reset the queue.
    /// This field exists only if [`VIRTIO_F_RING_RESET`] has been
    /// negotiated. (see _Basic Facilities of a Virtio Device / Virtqueues / Virtqueue Reset_).
    ///
    /// [`VIRTIO_F_RING_RESET`]: crate::F::RING_RESET
    queue_reset: le16,
}

impl_wide_field_access! {
    /// Common configuration structure
    pub trait CommonCfgVolatileWideFieldAccess<'a, A>: CommonCfg {
        /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
        #[access(ReadWrite)]
        queue_desc: queue_desc_low, queue_desc_high;

        /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
        #[access(ReadWrite)]
        queue_driver: queue_driver_low, queue_driver_high;

        /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
        #[access(ReadWrite)]
        queue_device: queue_device_low, queue_device_high;
    }
}

impl<'a, A> DeviceConfigSpace for VolatilePtr<'a, CommonCfg, A>
where
    A: RestrictAccess<ReadOnly>,
    A::Restricted: Readable,
{
    fn read_config_with<F, T>(self, f: F) -> T
    where
        F: FnMut() -> T,
    {
        let mut f = f;
        loop {
            let before = self.config_generation().read();
            let read = f();
            let after = self.config_generation().read();
            if after == before {
                break read;
            }
        }
    }
}

virtio_bitflags! {
    /// ISR Status
    pub struct IsrStatus: u8 {
        /// Queue Interrupt
        const QUEUE_INTERRUPT = 1 << 0;

        /// Device Configuration Interrupt
        const DEVICE_CONFIGURATION_INTERRUPT = 1 << 1;
    }
}