1 //! VirtIO transports.
2 
3 #[cfg(test)]
4 pub mod fake;
5 pub mod mmio;
6 pub mod pci;
7 
8 use crate::{PhysAddr, Result, PAGE_SIZE};
9 use bitflags::{bitflags, Flags};
10 use core::{fmt::Debug, ops::BitAnd, ptr::NonNull};
11 use log::debug;
12 
13 /// A VirtIO transport layer.
14 pub trait Transport {
15     /// Gets the device type.
device_type(&self) -> DeviceType16     fn device_type(&self) -> DeviceType;
17 
18     /// Reads device features.
read_device_features(&mut self) -> u6419     fn read_device_features(&mut self) -> u64;
20 
21     /// Writes device features.
write_driver_features(&mut self, driver_features: u64)22     fn write_driver_features(&mut self, driver_features: u64);
23 
24     /// Gets the max size of the given queue.
max_queue_size(&mut self, queue: u16) -> u3225     fn max_queue_size(&mut self, queue: u16) -> u32;
26 
27     /// Notifies the given queue on the device.
notify(&mut self, queue: u16)28     fn notify(&mut self, queue: u16);
29 
30     /// Gets the device status.
get_status(&self) -> DeviceStatus31     fn get_status(&self) -> DeviceStatus;
32 
33     /// Sets the device status.
set_status(&mut self, status: DeviceStatus)34     fn set_status(&mut self, status: DeviceStatus);
35 
36     /// Sets the guest page size.
set_guest_page_size(&mut self, guest_page_size: u32)37     fn set_guest_page_size(&mut self, guest_page_size: u32);
38 
39     /// Returns whether the transport requires queues to use the legacy layout.
40     ///
41     /// Ref: 2.6.2 Legacy Interfaces: A Note on Virtqueue Layout
requires_legacy_layout(&self) -> bool42     fn requires_legacy_layout(&self) -> bool;
43 
44     /// Sets up the given queue.
queue_set( &mut self, queue: u16, size: u32, descriptors: PhysAddr, driver_area: PhysAddr, device_area: PhysAddr, )45     fn queue_set(
46         &mut self,
47         queue: u16,
48         size: u32,
49         descriptors: PhysAddr,
50         driver_area: PhysAddr,
51         device_area: PhysAddr,
52     );
53 
54     /// Disables and resets the given queue.
queue_unset(&mut self, queue: u16)55     fn queue_unset(&mut self, queue: u16);
56 
57     /// Returns whether the queue is in use, i.e. has a nonzero PFN or is marked as ready.
queue_used(&mut self, queue: u16) -> bool58     fn queue_used(&mut self, queue: u16) -> bool;
59 
60     /// Acknowledges an interrupt.
61     ///
62     /// Returns true on success.
ack_interrupt(&mut self) -> bool63     fn ack_interrupt(&mut self) -> bool;
64 
65     /// Begins initializing the device.
66     ///
67     /// Ref: virtio 3.1.1 Device Initialization
68     ///
69     /// Returns the negotiated set of features.
begin_init<F: Flags<Bits = u64> + BitAnd<Output = F> + Debug>( &mut self, supported_features: F, ) -> F70     fn begin_init<F: Flags<Bits = u64> + BitAnd<Output = F> + Debug>(
71         &mut self,
72         supported_features: F,
73     ) -> F {
74         self.set_status(DeviceStatus::empty());
75         self.set_status(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER);
76 
77         let device_features = F::from_bits_truncate(self.read_device_features());
78         debug!("Device features: {:?}", device_features);
79         let negotiated_features = device_features & supported_features;
80         self.write_driver_features(negotiated_features.bits());
81 
82         self.set_status(
83             DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK,
84         );
85 
86         self.set_guest_page_size(PAGE_SIZE as u32);
87 
88         negotiated_features
89     }
90 
91     /// Finishes initializing the device.
finish_init(&mut self)92     fn finish_init(&mut self) {
93         self.set_status(
94             DeviceStatus::ACKNOWLEDGE
95                 | DeviceStatus::DRIVER
96                 | DeviceStatus::FEATURES_OK
97                 | DeviceStatus::DRIVER_OK,
98         );
99     }
100 
101     /// Gets the pointer to the config space.
config_space<T: 'static>(&self) -> Result<NonNull<T>>102     fn config_space<T: 'static>(&self) -> Result<NonNull<T>>;
103 }
104 
105 bitflags! {
106     /// The device status field. Writing 0 into this field resets the device.
107     #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
108     pub struct DeviceStatus: u32 {
109         /// Indicates that the guest OS has found the device and recognized it
110         /// as a valid virtio device.
111         const ACKNOWLEDGE = 1;
112 
113         /// Indicates that the guest OS knows how to drive the device.
114         const DRIVER = 2;
115 
116         /// Indicates that something went wrong in the guest, and it has given
117         /// up on the device. This could be an internal error, or the driver
118         /// didn’t like the device for some reason, or even a fatal error
119         /// during device operation.
120         const FAILED = 128;
121 
122         /// Indicates that the driver has acknowledged all the features it
123         /// understands, and feature negotiation is complete.
124         const FEATURES_OK = 8;
125 
126         /// Indicates that the driver is set up and ready to drive the device.
127         const DRIVER_OK = 4;
128 
129         /// Indicates that the device has experienced an error from which it
130         /// can’t recover.
131         const DEVICE_NEEDS_RESET = 64;
132     }
133 }
134 
135 /// Types of virtio devices.
136 #[repr(u8)]
137 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
138 #[allow(missing_docs)]
139 pub enum DeviceType {
140     Invalid = 0,
141     Network = 1,
142     Block = 2,
143     Console = 3,
144     EntropySource = 4,
145     MemoryBallooning = 5,
146     IoMemory = 6,
147     Rpmsg = 7,
148     ScsiHost = 8,
149     _9P = 9,
150     Mac80211 = 10,
151     RprocSerial = 11,
152     VirtioCAIF = 12,
153     MemoryBalloon = 13,
154     GPU = 16,
155     Timer = 17,
156     Input = 18,
157     Socket = 19,
158     Crypto = 20,
159     SignalDistributionModule = 21,
160     Pstore = 22,
161     IOMMU = 23,
162     Memory = 24,
163 }
164 
165 impl From<u32> for DeviceType {
from(virtio_device_id: u32) -> Self166     fn from(virtio_device_id: u32) -> Self {
167         match virtio_device_id {
168             1 => DeviceType::Network,
169             2 => DeviceType::Block,
170             3 => DeviceType::Console,
171             4 => DeviceType::EntropySource,
172             5 => DeviceType::MemoryBalloon,
173             6 => DeviceType::IoMemory,
174             7 => DeviceType::Rpmsg,
175             8 => DeviceType::ScsiHost,
176             9 => DeviceType::_9P,
177             10 => DeviceType::Mac80211,
178             11 => DeviceType::RprocSerial,
179             12 => DeviceType::VirtioCAIF,
180             13 => DeviceType::MemoryBalloon,
181             16 => DeviceType::GPU,
182             17 => DeviceType::Timer,
183             18 => DeviceType::Input,
184             19 => DeviceType::Socket,
185             20 => DeviceType::Crypto,
186             21 => DeviceType::SignalDistributionModule,
187             22 => DeviceType::Pstore,
188             23 => DeviceType::IOMMU,
189             24 => DeviceType::Memory,
190             _ => DeviceType::Invalid,
191         }
192     }
193 }
194 
195 impl From<u16> for DeviceType {
from(virtio_device_id: u16) -> Self196     fn from(virtio_device_id: u16) -> Self {
197         u32::from(virtio_device_id).into()
198     }
199 }
200 
201 impl From<u8> for DeviceType {
from(virtio_device_id: u8) -> Self202     fn from(virtio_device_id: u8) -> Self {
203         u32::from(virtio_device_id).into()
204     }
205 }
206