virtio_spec/pci.rs
1//! Definitions for Virtio over PCI bus.
2
3use core::mem;
4
5use num_enum::{FromPrimitive, IntoPrimitive};
6use pci_types::capability::PciCapabilityAddress;
7use pci_types::ConfigRegionAccess;
8use volatile::access::{ReadOnly, ReadWrite, Readable, RestrictAccess};
9use volatile::VolatilePtr;
10use volatile_macro::VolatileFieldAccess;
11
12pub use crate::driver_notifications::NotificationData;
13use crate::volatile::WideVolatilePtr;
14use crate::{le16, le32, le64, DeviceConfigSpace, DeviceStatus};
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#[cfg_attr(
331 feature = "zerocopy",
332 derive(
333 zerocopy_derive::KnownLayout,
334 zerocopy_derive::Immutable,
335 zerocopy_derive::FromBytes,
336 )
337)]
338#[derive(VolatileFieldAccess)]
339#[repr(C)]
340pub struct CommonCfg {
341 /// The driver uses this to select which feature bits `device_feature` shows.
342 /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc.
343 device_feature_select: le32,
344
345 /// The device uses this to report which feature bits it is
346 /// offering to the driver: the driver writes to
347 /// `device_feature_select` to select which feature bits are presented.
348 #[access(ReadOnly)]
349 device_feature: le32,
350
351 /// The driver uses this to select which feature bits `driver_feature` shows.
352 /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc.
353 driver_feature_select: le32,
354
355 /// The driver writes this to accept feature bits offered by the device.
356 /// Driver Feature Bits selected by `driver_feature_select`.
357 driver_feature: le32,
358
359 /// The driver sets the Configuration Vector for MSI-X.
360 config_msix_vector: le16,
361
362 /// The device specifies the maximum number of virtqueues supported here.
363 #[access(ReadOnly)]
364 num_queues: le16,
365
366 /// The driver writes the device status here (see [`DeviceStatus`]). Writing 0 into this
367 /// field resets the device.
368 device_status: DeviceStatus,
369
370 /// Configuration atomicity value. The device changes this every time the
371 /// configuration noticeably changes.
372 #[access(ReadOnly)]
373 config_generation: u8,
374
375 /// Queue Select. The driver selects which virtqueue the following
376 /// fields refer to.
377 queue_select: le16,
378
379 /// Queue Size. On reset, specifies the maximum queue size supported by
380 /// the device. This can be modified by the driver to reduce memory requirements.
381 /// A 0 means the queue is unavailable.
382 queue_size: le16,
383
384 /// The driver uses this to specify the queue vector for MSI-X.
385 queue_msix_vector: le16,
386
387 /// The driver uses this to selectively prevent the device from executing requests from this virtqueue.
388 /// 1 - enabled; 0 - disabled.
389 queue_enable: le16,
390
391 /// The driver reads this to calculate the offset from start of Notification structure at
392 /// which this virtqueue is located.
393 ///
394 /// <div class="warning">
395 ///
396 /// This is _not_ an offset in bytes.
397 /// See _Virtio Transport Options / Virtio Over PCI Bus / PCI Device Layout / Notification capability_ below.
398 ///
399 /// </div>
400 #[access(ReadOnly)]
401 queue_notify_off: le16,
402
403 /// The driver writes the physical address of Descriptor Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_.
404 queue_desc_low: le32,
405
406 /// The driver writes the physical address of Descriptor Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_.
407 queue_desc_high: le32,
408
409 /// The driver writes the physical address of Driver Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_.
410 queue_driver_low: le32,
411
412 /// The driver writes the physical address of Driver Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_.
413 queue_driver_high: le32,
414
415 /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_.
416 queue_device_low: le32,
417
418 /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_.
419 queue_device_high: le32,
420
421 /// This field exists only if [`VIRTIO_F_NOTIF_CONFIG_DATA`] has been negotiated.
422 /// The driver will use this value to put it in the 'virtqueue number' field
423 /// in the available buffer notification structure.
424 /// See section _Virtio Transport Options / Virtio Over PCI Bus / PCI-specific Initialization And Device Operation / Available Buffer Notifications_.
425 ///
426 /// <div class="warning">
427 ///
428 /// This field provides the device with flexibility to determine how virtqueues
429 /// will be referred to in available buffer notifications.
430 /// In a trivial case the device can set `queue_notify_data`=vqn. Some devices
431 /// may benefit from providing another value, for example an internal virtqueue
432 /// identifier, or an internal offset related to the virtqueue number.
433 ///
434 /// </div>
435 ///
436 /// [`VIRTIO_F_NOTIF_CONFIG_DATA`]: crate::F::NOTIF_CONFIG_DATA
437 #[access(ReadOnly)]
438 queue_notify_data: le16,
439
440 /// The driver uses this to selectively reset the queue.
441 /// This field exists only if [`VIRTIO_F_RING_RESET`] has been
442 /// negotiated. (see _Basic Facilities of a Virtio Device / Virtqueues / Virtqueue Reset_).
443 ///
444 /// [`VIRTIO_F_RING_RESET`]: crate::F::RING_RESET
445 queue_reset: le16,
446}
447
448impl_wide_field_access! {
449 /// Common configuration structure
450 pub trait CommonCfgVolatileWideFieldAccess<'a, A>: CommonCfg {
451 /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_.
452 #[access(ReadWrite)]
453 queue_desc: queue_desc_low, queue_desc_high;
454
455 /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_.
456 #[access(ReadWrite)]
457 queue_driver: queue_driver_low, queue_driver_high;
458
459 /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_.
460 #[access(ReadWrite)]
461 queue_device: queue_device_low, queue_device_high;
462 }
463}
464
465impl<'a, A> DeviceConfigSpace for VolatilePtr<'a, CommonCfg, A>
466where
467 A: RestrictAccess<ReadOnly>,
468 A::Restricted: Readable,
469{
470 fn read_config_with<F, T>(self, f: F) -> T
471 where
472 F: FnMut() -> T,
473 {
474 let mut f = f;
475 loop {
476 let before = self.config_generation().read();
477 let read = f();
478 let after = self.config_generation().read();
479 if after == before {
480 break read;
481 }
482 }
483 }
484}
485
486virtio_bitflags! {
487 /// ISR Status
488 pub struct IsrStatus: u8 {
489 /// Queue Interrupt
490 const QUEUE_INTERRUPT = 1 << 0;
491
492 /// Device Configuration Interrupt
493 const DEVICE_CONFIGURATION_INTERRUPT = 1 << 1;
494 }
495}