Skip to main content

virtio_spec/
pci.rs

1//! Definitions for Virtio over PCI bus.
2
3use core::mem;
4
5use num_enum::{FromPrimitive, IntoPrimitive};
6use pci_types::ConfigRegionAccess;
7use pci_types::capability::PciCapabilityAddress;
8use volatile::VolatilePtr;
9use volatile::access::{ReadOnly, ReadWrite, Readable, RestrictAccess};
10use volatile_macro::VolatileFieldAccess;
11
12pub use crate::driver_notifications::NotificationData;
13use crate::volatile::WideVolatilePtr;
14use crate::{DeviceConfigSpace, DeviceStatus, le16, le32, le64};
15
16/// PCI Capability
17///
18/// See [`CapData`] for reading additional fields.
19#[doc(alias = "virtio_pci_cap")]
20#[cfg_attr(
21    feature = "zerocopy",
22    derive(
23        zerocopy_derive::KnownLayout,
24        zerocopy_derive::Immutable,
25        zerocopy_derive::FromBytes,
26    )
27)]
28#[derive(Clone, Copy, Debug)]
29#[repr(C)]
30pub struct Cap {
31    /// Generic PCI field: `PCI_CAP_ID_VNDR`
32    ///
33    /// 0x09; Identifies a vendor-specific capability.
34    pub cap_vndr: u8,
35
36    /// Generic PCI field: next ptr.
37    ///
38    /// Link to next capability in the capability list in the PCI configuration space.
39    pub cap_next: u8,
40
41    /// Generic PCI field: capability length
42    ///
43    /// Length of this capability structure, including the whole of
44    /// struct virtio_pci_cap, and extra data if any.
45    /// This length MAY include padding, or fields unused by the driver.
46    pub cap_len: u8,
47
48    /// Identifies the structure.
49    ///
50    /// Each structure is detailed individually below.
51    ///
52    /// The device MAY offer more than one structure of any type - this makes it
53    /// possible for the device to expose multiple interfaces to drivers.  The order of
54    /// the capabilities in the capability list specifies the order of preference
55    /// suggested by the device.  A device may specify that this ordering mechanism be
56    /// overridden by the use of the `id` field.
57    ///
58    /// <div class="warning">
59    ///
60    /// For example, on some hypervisors, notifications using IO accesses are
61    /// faster than memory accesses. In this case, the device would expose two
62    /// capabilities with `cfg_type` set to VIRTIO_PCI_CAP_NOTIFY_CFG:
63    /// the first one addressing an I/O BAR, the second one addressing a memory BAR.
64    /// In this example, the driver would use the I/O BAR if I/O resources are available, and fall back on
65    /// memory BAR when I/O resources are unavailable.
66    ///
67    /// </div>
68    pub cfg_type: u8,
69
70    /// Where to find it.
71    ///
72    /// values 0x0 to 0x5 specify a Base Address register (BAR) belonging to
73    /// the function located beginning at 10h in PCI Configuration Space
74    /// and used to map the structure into Memory or I/O Space.
75    /// The BAR is permitted to be either 32-bit or 64-bit, it can map Memory Space
76    /// or I/O Space.
77    ///
78    /// Any other value is reserved for future use.
79    pub bar: u8,
80
81    /// Multiple capabilities of the same type
82    ///
83    /// Used by some device types to uniquely identify multiple capabilities
84    /// of a certain type. If the device type does not specify the meaning of
85    /// this field, its contents are undefined.
86    pub id: u8,
87
88    /// Pad to full dword.
89    pub padding: [u8; 2],
90
91    /// Offset within bar.
92    ///
93    /// indicates where the structure begins relative to the base address associated
94    /// with the BAR.  The alignment requirements of `offset` are indicated
95    /// in each structure-specific section below.
96    pub offset: le32,
97
98    /// Length of the structure, in bytes.
99    ///
100    /// indicates the length of the structure.
101    ///
102    /// `length` MAY include padding, or fields unused by the driver, or
103    /// future extensions.
104    ///
105    /// <div class="warning">
106    ///
107    /// For example, a future device might present a large structure size of several
108    /// MBytes.
109    /// As current devices never utilize structures larger than 4KBytes in size,
110    /// driver MAY limit the mapped structure size to e.g.
111    /// 4KBytes (thus ignoring parts of structure after the first
112    /// 4KBytes) to allow forward compatibility with such devices without loss of
113    /// functionality and without wasting resources.
114    ///
115    /// </div>
116    pub length: le32,
117}
118
119impl Cap {
120    pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option<Self> {
121        let data = unsafe { access.read(addr.address, addr.offset) };
122        let [cap_vndr, _cap_next, cap_len, _cfg_type] = data.to_le_bytes();
123
124        if cap_vndr != 0x09 {
125            return None;
126        }
127
128        if cap_len < 16 {
129            return None;
130        }
131
132        let data = [
133            data,
134            unsafe { access.read(addr.address, addr.offset + 4) },
135            unsafe { access.read(addr.address, addr.offset + 8) },
136            unsafe { access.read(addr.address, addr.offset + 12) },
137        ];
138
139        let data: [u32; 4] = data.map(u32::from_le);
140
141        let this = unsafe { mem::transmute::<[u32; 4], Self>(data) };
142
143        Some(this)
144    }
145}
146
147/// PCI Capability 64
148#[doc(alias = "virtio_pci_cap64")]
149#[cfg_attr(
150    feature = "zerocopy",
151    derive(
152        zerocopy_derive::KnownLayout,
153        zerocopy_derive::Immutable,
154        zerocopy_derive::FromBytes,
155    )
156)]
157#[derive(Clone, Copy, Debug)]
158#[repr(C)]
159pub struct Cap64 {
160    pub cap: Cap,
161    pub offset_hi: le32,
162    pub length_hi: le32,
163}
164
165/// PCI Notify Capability
166#[doc(alias = "virtio_pci_notify_cap")]
167#[cfg_attr(
168    feature = "zerocopy",
169    derive(
170        zerocopy_derive::KnownLayout,
171        zerocopy_derive::Immutable,
172        zerocopy_derive::FromBytes,
173    )
174)]
175#[derive(Clone, Copy, Debug)]
176#[repr(C)]
177pub struct NotifyCap {
178    pub cap: Cap,
179
180    /// Multiplier for queue_notify_off.
181    pub notify_off_multiplier: le32,
182}
183
184/// PCI Configuration Capability
185#[doc(alias = "virtio_pci_cfg_cap")]
186#[cfg_attr(
187    feature = "zerocopy",
188    derive(
189        zerocopy_derive::KnownLayout,
190        zerocopy_derive::Immutable,
191        zerocopy_derive::FromBytes,
192    )
193)]
194#[derive(Clone, Copy, Debug)]
195#[repr(C)]
196pub struct CfgCap {
197    pub cap: Cap,
198
199    /// Data for BAR access.
200    pub pci_cfg_data: [u8; 4],
201}
202
203/// PCI Capability Data
204#[derive(Clone, Copy, Debug)]
205pub struct CapData {
206    /// Identifies the structure.
207    pub cfg_type: CapCfgType,
208
209    /// Where to find it.
210    pub bar: u8,
211
212    /// Multiple capabilities of the same type
213    pub id: u8,
214
215    /// Offset within bar.
216    pub offset: le64,
217
218    /// Length of the structure, in bytes.
219    pub length: le64,
220
221    /// Multiplier for queue_notify_off.
222    pub notify_off_multiplier: Option<le32>,
223}
224
225impl CapData {
226    pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option<Self> {
227        let cap = Cap::read(addr, &access)?;
228        let cfg_type = CapCfgType::from(cap.cfg_type);
229
230        let (offset, length) = match cfg_type {
231            CapCfgType::SharedMemory => {
232                if cap.cap_len < 24 {
233                    return None;
234                }
235
236                let offset_hi = unsafe { access.read(addr.address, addr.offset + 16) };
237                let offset_hi = le32::from_ne(offset_hi);
238                let offset = le64::from([cap.offset, offset_hi]);
239
240                let length_hi = unsafe { access.read(addr.address, addr.offset + 20) };
241                let length_hi = le32::from_ne(length_hi);
242                let length = le64::from([cap.length, length_hi]);
243
244                (offset, length)
245            }
246            _ => (le64::from(cap.offset), le64::from(cap.length)),
247        };
248
249        let notify_off_multiplier = match cfg_type {
250            CapCfgType::Notify => {
251                if cap.cap_len < 20 {
252                    return None;
253                }
254
255                let notify_off_multiplier = unsafe { access.read(addr.address, addr.offset + 16) };
256                let notify_off_multiplier = le32::from_ne(notify_off_multiplier);
257
258                Some(notify_off_multiplier)
259            }
260            _ => None,
261        };
262
263        Some(Self {
264            cfg_type,
265            bar: cap.bar,
266            id: cap.id,
267            offset,
268            length,
269            notify_off_multiplier,
270        })
271    }
272}
273
274/// PCI Capability Configuration Type
275///
276/// <div class="warning">
277///
278/// This enum is not ABI-compatible with it's corresponding field.
279/// Use [`CapCfgType::from`] for converting from an integer.
280///
281/// </div>
282///
283/// [`CapCfgType::from`]: CapCfgType#impl-From<u8>-for-CapCfgType
284#[doc(alias = "VIRTIO_PCI_CAP")]
285#[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
286#[non_exhaustive]
287#[repr(u8)]
288pub enum CapCfgType {
289    /// Common configuration
290    #[doc(alias = "VIRTIO_PCI_CAP_COMMON_CFG")]
291    Common = 1,
292
293    /// Notifications
294    #[doc(alias = "VIRTIO_PCI_CAP_NOTIFY_CFG")]
295    Notify = 2,
296
297    /// ISR Status
298    #[doc(alias = "VIRTIO_PCI_CAP_ISR_CFG")]
299    Isr = 3,
300
301    /// Device specific configuration
302    #[doc(alias = "VIRTIO_PCI_CAP_DEVICE_CFG")]
303    Device = 4,
304
305    /// PCI configuration access
306    #[doc(alias = "VIRTIO_PCI_CAP_PCI_CFG")]
307    Pci = 5,
308
309    /// Shared memory region
310    #[doc(alias = "VIRTIO_PCI_CAP_SHARED_MEMORY_CFG")]
311    SharedMemory = 8,
312
313    /// Vendor-specific data
314    #[doc(alias = "VIRTIO_PCI_CAP_VENDOR_CFG")]
315    Vendor = 9,
316
317    /// Unknown device
318    #[num_enum(catch_all)]
319    Unknown(u8),
320}
321
322/// Common configuration structure
323///
324/// The common configuration structure is found at the bar and offset within the [`VIRTIO_PCI_CAP_COMMON_CFG`] capability.
325///
326/// [`VIRTIO_PCI_CAP_COMMON_CFG`]: CapCfgType::Common
327///
328/// Use [`CommonCfgVolatileFieldAccess`] and [`CommonCfgVolatileWideFieldAccess`] to work with this struct.
329#[doc(alias = "virtio_pci_common_cfg")]
330#[derive(VolatileFieldAccess)]
331#[repr(C)]
332pub struct CommonCfg {
333    /// The driver uses this to select which feature bits `device_feature` shows.
334    /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc.
335    device_feature_select: le32,
336
337    /// The device uses this to report which feature bits it is
338    /// offering to the driver: the driver writes to
339    /// `device_feature_select` to select which feature bits are presented.
340    #[access(ReadOnly)]
341    device_feature: le32,
342
343    /// The driver uses this to select which feature bits `driver_feature` shows.
344    /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc.
345    driver_feature_select: le32,
346
347    /// The driver writes this to accept feature bits offered by the device.
348    /// Driver Feature Bits selected by `driver_feature_select`.
349    driver_feature: le32,
350
351    /// Set by the driver to the MSI-X vector for configuration change notifications.
352    config_msix_vector: le16,
353
354    /// The device specifies the maximum number of virtqueues supported here.
355    /// This excludes administration virtqueues if any are supported.
356    #[access(ReadOnly)]
357    num_queues: le16,
358
359    /// The driver writes the device status here (see [`DeviceStatus`]). Writing 0 into this
360    /// field resets the device.
361    device_status: DeviceStatus,
362
363    /// Configuration atomicity value.  The device changes this every time the
364    /// configuration noticeably changes.
365    #[access(ReadOnly)]
366    config_generation: u8,
367
368    /// Queue Select. The driver selects which virtqueue the following
369    /// fields refer to.
370    queue_select: le16,
371
372    /// Queue Size.  On reset, specifies the maximum queue size supported by
373    /// the device. This can be modified by the driver to reduce memory requirements.
374    /// A 0 means the queue is unavailable.
375    queue_size: le16,
376
377    /// Set by the driver to the MSI-X vector for virtqueue notifications.
378    queue_msix_vector: le16,
379
380    /// The driver uses this to selectively prevent the device from executing requests from this virtqueue.
381    /// 1 - enabled; 0 - disabled.
382    queue_enable: le16,
383
384    /// The driver reads this to calculate the offset from start of Notification structure at
385    /// which this virtqueue is located.
386    ///
387    /// <div class="warning">
388    ///
389    /// This is _not_ an offset in bytes.
390    /// See _Virtio Transport Options / Virtio Over PCI Bus / PCI Device Layout / Notification capability_ below.
391    ///
392    /// </div>
393    #[access(ReadOnly)]
394    queue_notify_off: le16,
395
396    /// The driver writes the physical address of Descriptor Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
397    queue_desc_low: le32,
398
399    /// The driver writes the physical address of Descriptor Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
400    queue_desc_high: le32,
401
402    /// The driver writes the physical address of Driver Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
403    queue_driver_low: le32,
404
405    /// The driver writes the physical address of Driver Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
406    queue_driver_high: le32,
407
408    /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
409    queue_device_low: le32,
410
411    /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
412    queue_device_high: le32,
413
414    /// This field exists only if [`VIRTIO_F_NOTIF_CONFIG_DATA`] has been negotiated.
415    /// The driver will use this value when driver sends available buffer
416    /// notification to the device.
417    /// See section _Virtio Transport Options / Virtio Over PCI Bus / PCI-specific Initialization And Device Operation / Available Buffer Notifications_.
418    ///
419    /// <div class="warning">
420    ///
421    /// This field provides the device with flexibility to determine how virtqueues
422    /// will be referred to in available buffer notifications.
423    /// In a trivial case the device can set `queue_notif_config_data` to
424    /// the virtqueue index. Some devices may benefit from providing another value,
425    /// for example an internal virtqueue identifier, or an internal offset
426    /// related to the virtqueue index.
427    ///
428    /// </div>
429    ///
430    /// [`VIRTIO_F_NOTIF_CONFIG_DATA`]: crate::F::NOTIF_CONFIG_DATA
431    #[doc(alias = "queue_notify_data")]
432    #[access(ReadOnly)]
433    queue_notif_config_data: le16,
434
435    /// The driver uses this to selectively reset the queue.
436    /// This field exists only if [`VIRTIO_F_RING_RESET`] has been
437    /// negotiated. (see _Basic Facilities of a Virtio Device / Virtqueues / Virtqueue Reset_).
438    ///
439    /// [`VIRTIO_F_RING_RESET`]: crate::F::RING_RESET
440    queue_reset: le16,
441
442    /// The device uses this to report the index of the first administration virtqueue.
443    /// This field is valid only if VIRTIO_F_ADMIN_VQ has been negotiated.
444    #[access(ReadOnly)]
445    admin_queue_index: le16,
446
447    /// The device uses this to report the number of the
448    /// supported administration virtqueues.
449    /// Virtqueues with index
450    /// between `admin_queue_index` and (`admin_queue_index` +
451    /// `admin_queue_num` - 1) inclusive serve as administration
452    /// virtqueues.
453    /// The value 0 indicates no supported administration virtqueues.
454    /// This field is valid only if VIRTIO_F_ADMIN_VQ has been
455    /// negotiated.
456    #[access(ReadOnly)]
457    admin_queue_num: le16,
458}
459
460impl_wide_field_access! {
461    /// Common configuration structure
462    pub trait CommonCfgVolatileWideFieldAccess<'a, A>: CommonCfg {
463        /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
464        #[access(ReadWrite)]
465        queue_desc: queue_desc_low, queue_desc_high;
466
467        /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
468        #[access(ReadWrite)]
469        queue_driver: queue_driver_low, queue_driver_high;
470
471        /// The driver writes the physical address of Device Area here.  See section _Basic Facilities of a Virtio Device / Virtqueues_.
472        #[access(ReadWrite)]
473        queue_device: queue_device_low, queue_device_high;
474    }
475}
476
477impl<'a, A> DeviceConfigSpace for VolatilePtr<'a, CommonCfg, A>
478where
479    A: RestrictAccess<ReadOnly>,
480    A::Restricted: Readable,
481{
482    fn read_config_with<F, T>(self, f: F) -> T
483    where
484        F: FnMut() -> T,
485    {
486        let mut f = f;
487        loop {
488            let before = self.config_generation().read();
489            let read = f();
490            let after = self.config_generation().read();
491            if after == before {
492                break read;
493            }
494        }
495    }
496}
497
498virtio_bitflags! {
499    /// ISR Status
500    pub struct IsrStatus: u8 {
501        /// Queue Interrupt
502        const QUEUE_INTERRUPT = 1 << 0;
503
504        /// Device Configuration Interrupt
505        const DEVICE_CONFIGURATION_INTERRUPT = 1 << 1;
506    }
507}