Skip to main content

virtio_drivers/transport/
mod.rs

1//! VirtIO transports.
2
3#[cfg(test)]
4pub mod fake;
5pub mod mmio;
6pub mod pci;
7mod some;
8#[cfg(target_arch = "x86_64")]
9pub mod x86_64;
10
11use crate::{PAGE_SIZE, PhysAddr, Result};
12use bitflags::{Flags, bitflags};
13use core::{
14    fmt::{self, Debug, Formatter},
15    ops::BitAnd,
16};
17use log::debug;
18pub use some::SomeTransport;
19use thiserror::Error;
20use zerocopy::{FromBytes, Immutable, IntoBytes};
21
22/// A VirtIO transport layer.
23pub trait Transport {
24    /// Gets the device type.
25    fn device_type(&self) -> DeviceType;
26
27    /// Reads device features.
28    fn read_device_features(&mut self) -> u64;
29
30    /// Writes device features.
31    fn write_driver_features(&mut self, driver_features: u64);
32
33    /// Gets the max size of the given queue.
34    fn max_queue_size(&mut self, queue: u16) -> u32;
35
36    /// Notifies the given queue on the device.
37    fn notify(&mut self, queue: u16);
38
39    /// Gets the device status.
40    fn get_status(&self) -> DeviceStatus;
41
42    /// Sets the device status.
43    fn set_status(&mut self, status: DeviceStatus);
44
45    /// Sets the guest page size.
46    fn set_guest_page_size(&mut self, guest_page_size: u32);
47
48    /// Returns whether the transport requires queues to use the legacy layout.
49    ///
50    /// Ref: 2.6.2 Legacy Interfaces: A Note on Virtqueue Layout
51    fn requires_legacy_layout(&self) -> bool;
52
53    /// Sets up the given queue.
54    fn queue_set(
55        &mut self,
56        queue: u16,
57        size: u32,
58        descriptors: PhysAddr,
59        driver_area: PhysAddr,
60        device_area: PhysAddr,
61    );
62
63    /// Disables and resets the given queue.
64    fn queue_unset(&mut self, queue: u16);
65
66    /// Returns whether the queue is in use, i.e. has a nonzero PFN or is marked as ready.
67    fn queue_used(&mut self, queue: u16) -> bool;
68
69    /// Acknowledges an interrupt.
70    ///
71    /// Returns true on success.
72    fn ack_interrupt(&mut self) -> InterruptStatus;
73
74    /// Begins initializing the device.
75    ///
76    /// Ref: virtio 3.1.1 Device Initialization
77    ///
78    /// Returns the negotiated set of features.
79    fn begin_init<F: Flags<Bits = u64> + BitAnd<Output = F> + Debug>(
80        &mut self,
81        supported_features: F,
82    ) -> F {
83        self.set_status(DeviceStatus::empty());
84        self.set_status(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER);
85
86        let device_feature_bits = self.read_device_features();
87        let device_features = F::from_bits_truncate(device_feature_bits);
88        debug!("Device features: {:?}", device_features);
89        let negotiated_features = device_features & supported_features;
90        if cfg!(debug_assertions) {
91            use crate::device::common::Feature;
92
93            if device_feature_bits & Feature::VERSION_1.bits() > 0 {
94                // > 6.1 Driver Requirements: Reserved Feature Bits
95                // > A driver MUST accept VIRTIO_F_VERSION_1 if it is offered.
96                debug_assert!(
97                    negotiated_features.bits() & Feature::VERSION_1.bits() > 0,
98                    "Driver must accept VIRTIO_F_VERSION_1 in supported features because it is offered by the device."
99                );
100            }
101        }
102        self.write_driver_features(negotiated_features.bits());
103
104        self.set_status(
105            DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK,
106        );
107
108        self.set_guest_page_size(PAGE_SIZE as u32);
109
110        negotiated_features
111    }
112
113    /// Finishes initializing the device.
114    fn finish_init(&mut self) {
115        self.set_status(
116            DeviceStatus::ACKNOWLEDGE
117                | DeviceStatus::DRIVER
118                | DeviceStatus::FEATURES_OK
119                | DeviceStatus::DRIVER_OK,
120        );
121    }
122
123    /// Reads the configuration space generation.
124    fn read_config_generation(&self) -> u32;
125
126    /// Reads a value from the device config space.
127    fn read_config_space<T: FromBytes + IntoBytes>(&self, offset: usize) -> Result<T>;
128
129    /// Writes a value to the device config space.
130    fn write_config_space<T: IntoBytes + Immutable>(
131        &mut self,
132        offset: usize,
133        value: T,
134    ) -> Result<()>;
135
136    /// Safely reads multiple fields from config space by ensuring that the config generation is the
137    /// same before and after all reads, and retrying if not.
138    fn read_consistent<T>(&self, f: impl Fn() -> Result<T>) -> Result<T> {
139        loop {
140            let before = self.read_config_generation();
141            let result = f();
142            let after = self.read_config_generation();
143            if before == after {
144                break result;
145            }
146        }
147    }
148}
149
150/// The device status field. Writing 0 into this field resets the device.
151#[derive(Copy, Clone, Default, Eq, FromBytes, Immutable, IntoBytes, PartialEq)]
152pub struct DeviceStatus(u32);
153
154impl Debug for DeviceStatus {
155    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
156        write!(f, "DeviceStatus(")?;
157        bitflags::parser::to_writer(self, &mut *f)?;
158        write!(f, ")")?;
159        Ok(())
160    }
161}
162
163bitflags! {
164    impl DeviceStatus: u32 {
165        /// Indicates that the guest OS has found the device and recognized it
166        /// as a valid virtio device.
167        const ACKNOWLEDGE = 1;
168
169        /// Indicates that the guest OS knows how to drive the device.
170        const DRIVER = 2;
171
172        /// Indicates that something went wrong in the guest, and it has given
173        /// up on the device. This could be an internal error, or the driver
174        /// didn’t like the device for some reason, or even a fatal error
175        /// during device operation.
176        const FAILED = 128;
177
178        /// Indicates that the driver has acknowledged all the features it
179        /// understands, and feature negotiation is complete.
180        const FEATURES_OK = 8;
181
182        /// Indicates that the driver is set up and ready to drive the device.
183        const DRIVER_OK = 4;
184
185        /// Indicates that the device has experienced an error from which it
186        /// can’t recover.
187        const DEVICE_NEEDS_RESET = 64;
188    }
189}
190
191/// The set of interrupts which were pending
192///
193/// Ref: 4.1.4.5 ISR status capability
194#[derive(Copy, Clone, Default, Eq, FromBytes, PartialEq)]
195pub struct InterruptStatus(u32);
196
197bitflags! {
198    impl InterruptStatus: u32 {
199        /// Indicates that a virtqueue buffer was used
200        const QUEUE_INTERRUPT = 1 << 0;
201
202        /// Indicates that a virtio device changed its configuration state
203        const DEVICE_CONFIGURATION_INTERRUPT = 1 << 1;
204    }
205}
206
207/// Types of virtio devices.
208#[repr(u8)]
209#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
210#[allow(missing_docs)]
211pub enum DeviceType {
212    Network = 1,
213    Block = 2,
214    Console = 3,
215    EntropySource = 4,
216    MemoryBallooning = 5,
217    IoMemory = 6,
218    Rpmsg = 7,
219    ScsiHost = 8,
220    _9P = 9,
221    Mac80211 = 10,
222    RprocSerial = 11,
223    VirtioCAIF = 12,
224    MemoryBalloon = 13,
225    GPU = 16,
226    Timer = 17,
227    Input = 18,
228    Socket = 19,
229    Crypto = 20,
230    SignalDistributionModule = 21,
231    Pstore = 22,
232    IOMMU = 23,
233    Memory = 24,
234    Sound = 25,
235}
236
237/// Errors converting a number to a virtio device type.
238#[derive(Copy, Clone, Debug, Eq, Error, PartialEq)]
239pub enum DeviceTypeError {
240    /// Invalid or unknown virtio device type.
241    #[error("Invalid or unknown virtio device type {0}")]
242    InvalidDeviceType(u32),
243}
244
245impl TryFrom<u32> for DeviceType {
246    type Error = DeviceTypeError;
247
248    fn try_from(virtio_device_id: u32) -> core::result::Result<Self, Self::Error> {
249        match virtio_device_id {
250            1 => Ok(DeviceType::Network),
251            2 => Ok(DeviceType::Block),
252            3 => Ok(DeviceType::Console),
253            4 => Ok(DeviceType::EntropySource),
254            5 => Ok(DeviceType::MemoryBalloon),
255            6 => Ok(DeviceType::IoMemory),
256            7 => Ok(DeviceType::Rpmsg),
257            8 => Ok(DeviceType::ScsiHost),
258            9 => Ok(DeviceType::_9P),
259            10 => Ok(DeviceType::Mac80211),
260            11 => Ok(DeviceType::RprocSerial),
261            12 => Ok(DeviceType::VirtioCAIF),
262            13 => Ok(DeviceType::MemoryBalloon),
263            16 => Ok(DeviceType::GPU),
264            17 => Ok(DeviceType::Timer),
265            18 => Ok(DeviceType::Input),
266            19 => Ok(DeviceType::Socket),
267            20 => Ok(DeviceType::Crypto),
268            21 => Ok(DeviceType::SignalDistributionModule),
269            22 => Ok(DeviceType::Pstore),
270            23 => Ok(DeviceType::IOMMU),
271            24 => Ok(DeviceType::Memory),
272            25 => Ok(DeviceType::Sound),
273            _ => Err(DeviceTypeError::InvalidDeviceType(virtio_device_id)),
274        }
275    }
276}
277
278impl TryFrom<u16> for DeviceType {
279    type Error = DeviceTypeError;
280
281    fn try_from(virtio_device_id: u16) -> core::result::Result<Self, Self::Error> {
282        u32::from(virtio_device_id).try_into()
283    }
284}
285
286impl TryFrom<u8> for DeviceType {
287    type Error = DeviceTypeError;
288
289    fn try_from(virtio_device_id: u8) -> core::result::Result<Self, Self::Error> {
290        u32::from(virtio_device_id).try_into()
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297
298    #[test]
299    fn debug_device_status() {
300        let status = DeviceStatus::from_bits_retain(0x23);
301        assert_eq!(
302            format!("{:?}", status),
303            "DeviceStatus(ACKNOWLEDGE | DRIVER | 0x20)"
304        );
305    }
306}