• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! VirtIO transports.
2 
3 #[cfg(test)]
4 pub mod fake;
5 pub mod mmio;
6 pub mod pci;
7 mod some;
8 #[cfg(target_arch = "x86_64")]
9 pub mod x86_64;
10 
11 use crate::{PhysAddr, Result, PAGE_SIZE};
12 use bitflags::{bitflags, Flags};
13 use core::{fmt::Debug, ops::BitAnd};
14 use log::debug;
15 pub use some::SomeTransport;
16 use zerocopy::{FromBytes, Immutable, IntoBytes};
17 
18 /// A VirtIO device-side transport layer.
19 pub trait DeviceTransport {
20     /// Gets the client VM ID
get_client_id(&self) -> u1621     fn get_client_id(&self) -> u16;
22 
23     /// Gets the max size of the given queue.
max_queue_size(&mut self, queue: u16) -> u3224     fn max_queue_size(&mut self, queue: u16) -> u32;
25 
26     /// Returns whether the transport requires queues to use the legacy layout.
27     ///
28     /// Ref: 2.6.2 Legacy Interfaces: A Note on Virtqueue Layout
requires_legacy_layout(&self) -> bool29     fn requires_legacy_layout(&self) -> bool;
30 
31     /// Gets the physical addresses for descriptors, driver area and device area for a given queue.
queue_get(&mut self, queue: u16) -> [PhysAddr; 3]32     fn queue_get(&mut self, queue: u16) -> [PhysAddr; 3];
33 
34     /// Notifies the given queue on the device.
notify(&mut self, queue: u16)35     fn notify(&mut self, queue: u16);
36 }
37 
38 /// A VirtIO transport layer.
39 pub trait Transport {
40     /// Gets the device type.
device_type(&self) -> DeviceType41     fn device_type(&self) -> DeviceType;
42 
43     /// Reads device features.
read_device_features(&mut self) -> u6444     fn read_device_features(&mut self) -> u64;
45 
46     /// Writes device features.
write_driver_features(&mut self, driver_features: u64)47     fn write_driver_features(&mut self, driver_features: u64);
48 
49     /// Gets the max size of the given queue.
max_queue_size(&mut self, queue: u16) -> u3250     fn max_queue_size(&mut self, queue: u16) -> u32;
51 
52     /// Notifies the given queue on the device.
notify(&mut self, queue: u16)53     fn notify(&mut self, queue: u16);
54 
55     /// Gets the device status.
get_status(&self) -> DeviceStatus56     fn get_status(&self) -> DeviceStatus;
57 
58     /// Sets the device status.
set_status(&mut self, status: DeviceStatus)59     fn set_status(&mut self, status: DeviceStatus);
60 
61     /// Sets the guest page size.
set_guest_page_size(&mut self, guest_page_size: u32)62     fn set_guest_page_size(&mut self, guest_page_size: u32);
63 
64     /// Returns whether the transport requires queues to use the legacy layout.
65     ///
66     /// Ref: 2.6.2 Legacy Interfaces: A Note on Virtqueue Layout
requires_legacy_layout(&self) -> bool67     fn requires_legacy_layout(&self) -> bool;
68 
69     /// Sets up the given queue.
queue_set( &mut self, queue: u16, size: u32, descriptors: PhysAddr, driver_area: PhysAddr, device_area: PhysAddr, )70     fn queue_set(
71         &mut self,
72         queue: u16,
73         size: u32,
74         descriptors: PhysAddr,
75         driver_area: PhysAddr,
76         device_area: PhysAddr,
77     );
78 
79     /// Disables and resets the given queue.
queue_unset(&mut self, queue: u16)80     fn queue_unset(&mut self, queue: u16);
81 
82     /// Returns whether the queue is in use, i.e. has a nonzero PFN or is marked as ready.
queue_used(&mut self, queue: u16) -> bool83     fn queue_used(&mut self, queue: u16) -> bool;
84 
85     /// Acknowledges an interrupt.
86     ///
87     /// Returns true on success.
ack_interrupt(&mut self) -> bool88     fn ack_interrupt(&mut self) -> bool;
89 
90     /// Begins initializing the device.
91     ///
92     /// Ref: virtio 3.1.1 Device Initialization
93     ///
94     /// Returns the negotiated set of features.
begin_init<F: Flags<Bits = u64> + BitAnd<Output = F> + Debug>( &mut self, supported_features: F, ) -> F95     fn begin_init<F: Flags<Bits = u64> + BitAnd<Output = F> + Debug>(
96         &mut self,
97         supported_features: F,
98     ) -> F {
99         self.set_status(DeviceStatus::empty());
100         self.set_status(DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER);
101 
102         let device_features = F::from_bits_truncate(self.read_device_features());
103         debug!("Device features: {:?}", device_features);
104         let negotiated_features = device_features & supported_features;
105         self.write_driver_features(negotiated_features.bits());
106 
107         self.set_status(
108             DeviceStatus::ACKNOWLEDGE | DeviceStatus::DRIVER | DeviceStatus::FEATURES_OK,
109         );
110 
111         self.set_guest_page_size(PAGE_SIZE as u32);
112 
113         negotiated_features
114     }
115 
116     /// Finishes initializing the device.
finish_init(&mut self)117     fn finish_init(&mut self) {
118         self.set_status(
119             DeviceStatus::ACKNOWLEDGE
120                 | DeviceStatus::DRIVER
121                 | DeviceStatus::FEATURES_OK
122                 | DeviceStatus::DRIVER_OK,
123         );
124     }
125 
126     /// Reads the configuration space generation.
read_config_generation(&self) -> u32127     fn read_config_generation(&self) -> u32;
128 
129     /// Reads a value from the device config space.
read_config_space<T: FromBytes>(&self, offset: usize) -> Result<T>130     fn read_config_space<T: FromBytes>(&self, offset: usize) -> Result<T>;
131 
132     /// Writes a value to the device config space.
write_config_space<T: IntoBytes + Immutable>( &mut self, offset: usize, value: T, ) -> Result<()>133     fn write_config_space<T: IntoBytes + Immutable>(
134         &mut self,
135         offset: usize,
136         value: T,
137     ) -> Result<()>;
138 
139     /// Safely reads multiple fields from config space by ensuring that the config generation is the
140     /// same before and after all reads, and retrying if not.
read_consistent<T>(&self, f: impl Fn() -> Result<T>) -> Result<T>141     fn read_consistent<T>(&self, f: impl Fn() -> Result<T>) -> Result<T> {
142         loop {
143             let before = self.read_config_generation();
144             let result = f();
145             let after = self.read_config_generation();
146             if before == after {
147                 break result;
148             }
149         }
150     }
151 }
152 
153 bitflags! {
154     /// The device status field. Writing 0 into this field resets the device.
155     #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
156     pub struct DeviceStatus: u32 {
157         /// Indicates that the guest OS has found the device and recognized it
158         /// as a valid virtio device.
159         const ACKNOWLEDGE = 1;
160 
161         /// Indicates that the guest OS knows how to drive the device.
162         const DRIVER = 2;
163 
164         /// Indicates that something went wrong in the guest, and it has given
165         /// up on the device. This could be an internal error, or the driver
166         /// didn’t like the device for some reason, or even a fatal error
167         /// during device operation.
168         const FAILED = 128;
169 
170         /// Indicates that the driver has acknowledged all the features it
171         /// understands, and feature negotiation is complete.
172         const FEATURES_OK = 8;
173 
174         /// Indicates that the driver is set up and ready to drive the device.
175         const DRIVER_OK = 4;
176 
177         /// Indicates that the device has experienced an error from which it
178         /// can’t recover.
179         const DEVICE_NEEDS_RESET = 64;
180     }
181 }
182 
183 /// Types of virtio devices.
184 #[repr(u8)]
185 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
186 #[allow(missing_docs)]
187 pub enum DeviceType {
188     Invalid = 0,
189     Network = 1,
190     Block = 2,
191     Console = 3,
192     EntropySource = 4,
193     MemoryBallooning = 5,
194     IoMemory = 6,
195     Rpmsg = 7,
196     ScsiHost = 8,
197     _9P = 9,
198     Mac80211 = 10,
199     RprocSerial = 11,
200     VirtioCAIF = 12,
201     MemoryBalloon = 13,
202     GPU = 16,
203     Timer = 17,
204     Input = 18,
205     Socket = 19,
206     Crypto = 20,
207     SignalDistributionModule = 21,
208     Pstore = 22,
209     IOMMU = 23,
210     Memory = 24,
211     Sound = 25,
212 }
213 
214 impl From<u32> for DeviceType {
from(virtio_device_id: u32) -> Self215     fn from(virtio_device_id: u32) -> Self {
216         match virtio_device_id {
217             1 => DeviceType::Network,
218             2 => DeviceType::Block,
219             3 => DeviceType::Console,
220             4 => DeviceType::EntropySource,
221             5 => DeviceType::MemoryBalloon,
222             6 => DeviceType::IoMemory,
223             7 => DeviceType::Rpmsg,
224             8 => DeviceType::ScsiHost,
225             9 => DeviceType::_9P,
226             10 => DeviceType::Mac80211,
227             11 => DeviceType::RprocSerial,
228             12 => DeviceType::VirtioCAIF,
229             13 => DeviceType::MemoryBalloon,
230             16 => DeviceType::GPU,
231             17 => DeviceType::Timer,
232             18 => DeviceType::Input,
233             19 => DeviceType::Socket,
234             20 => DeviceType::Crypto,
235             21 => DeviceType::SignalDistributionModule,
236             22 => DeviceType::Pstore,
237             23 => DeviceType::IOMMU,
238             24 => DeviceType::Memory,
239             25 => DeviceType::Sound,
240             _ => DeviceType::Invalid,
241         }
242     }
243 }
244 
245 impl From<u16> for DeviceType {
from(virtio_device_id: u16) -> Self246     fn from(virtio_device_id: u16) -> Self {
247         u32::from(virtio_device_id).into()
248     }
249 }
250 
251 impl From<u8> for DeviceType {
from(virtio_device_id: u8) -> Self252     fn from(virtio_device_id: u8) -> Self {
253         u32::from(virtio_device_id).into()
254     }
255 }
256