Skip to main content

hermit/drivers/virtio/
mod.rs

1//! Virtio infrastructure.
2//!
3//! This module provides [`transport`] infrastructure as well as [`virtqueue`] infrastructure.
4
5#![cfg_attr(
6	not(any(
7		feature = "virtio-console",
8		feature = "virtio-fs",
9		feature = "virtio-net",
10		feature = "virtio-vsock"
11	)),
12	allow(dead_code)
13)]
14
15pub mod transport;
16pub mod virtqueue;
17
18use core::fmt;
19
20use bitflags::Flags;
21use virtio::FeatureBits;
22
23use crate::drivers::virtio::error::VirtioError;
24use crate::drivers::virtio::transport::UniCapsColl;
25
26trait VirtioIdExt {
27	fn as_feature(&self) -> Option<&str>;
28}
29
30impl VirtioIdExt for virtio::Id {
31	fn as_feature(&self) -> Option<&str> {
32		let feature = match self {
33			Self::Net => "virtio-net",
34			Self::Console => "virtio-console",
35			Self::Fs => "virtio-fs",
36			Self::Vsock => "virtio-vsock",
37			_ => return None,
38		};
39
40		Some(feature)
41	}
42}
43
44mod control_registers_access {
45	use core::{array, mem};
46
47	use virtio::{le32, le128};
48	use volatile::VolatilePtr;
49	use volatile::access::ReadWrite;
50
51	pub trait ControlRegistersAccess<'a>: Sized + Copy {
52		fn read_device_feature_word(self, i: u32) -> le32;
53		fn write_driver_feature_word(self, i: u32, word: le32);
54
55		fn read_device_features(self) -> virtio::F {
56			let features = array::from_fn(|i| {
57				let i = u32::try_from(i).unwrap();
58				self.read_device_feature_word(i)
59			});
60
61			let features = unsafe { mem::transmute::<[le32; 4], le128>(features) };
62
63			virtio::F::from_bits_retain(features)
64		}
65
66		fn write_driver_features(self, features: virtio::F) {
67			let features = features.bits();
68
69			let features = unsafe { mem::transmute::<le128, [le32; 4]>(features) };
70
71			for (i, word) in features.into_iter().enumerate() {
72				let i = u32::try_from(i).unwrap();
73				self.write_driver_feature_word(i, word);
74			}
75		}
76	}
77
78	#[cfg(feature = "pci")]
79	impl<'a> ControlRegistersAccess<'a> for VolatilePtr<'a, virtio::pci::CommonCfg, ReadWrite> {
80		fn read_device_feature_word(self, i: u32) -> le32 {
81			use virtio::pci::CommonCfgVolatileFieldAccess;
82
83			self.device_feature_select().write(i.into());
84			self.device_feature().read()
85		}
86
87		fn write_driver_feature_word(self, i: u32, word: le32) {
88			use virtio::pci::CommonCfgVolatileFieldAccess;
89
90			self.driver_feature_select().write(i.into());
91			self.driver_feature().write(word);
92		}
93	}
94
95	#[cfg(not(feature = "pci"))]
96	impl<'a> ControlRegistersAccess<'a> for VolatilePtr<'a, virtio::mmio::DeviceRegisters, ReadWrite> {
97		fn read_device_feature_word(self, i: u32) -> le32 {
98			use virtio::mmio::DeviceRegistersVolatileFieldAccess;
99
100			// QEMU only supports index 0 and 1 for virtio-mmio:
101			// https://gitlab.com/qemu-project/qemu/-/blob/v10.2.0/hw/virtio/virtio-mmio.c#L305-311
102			if i > 1 {
103				return 0.into();
104			}
105
106			self.device_features_sel().write(i.into());
107			self.device_features().read()
108		}
109
110		fn write_driver_feature_word(self, i: u32, word: le32) {
111			use virtio::mmio::DeviceRegistersVolatileFieldAccess;
112
113			// QEMU only supports index 0 and 1 for virtio-mmio:
114			// https://gitlab.com/qemu-project/qemu/-/blob/v10.2.0/hw/virtio/virtio-mmio.c#L326-332
115			if i > 1 {
116				debug_assert!(word.to_ne() == 0);
117				return;
118			}
119
120			self.driver_features_sel().write(i.into());
121			self.driver_features().write(word);
122		}
123	}
124}
125
126pub trait ControlRegisters<'a>: control_registers_access::ControlRegistersAccess<'a> {
127	fn negotiate_features<DF>(self, driver_features: DF) -> DF
128	where
129		DF: FeatureBits + fmt::Debug + Copy;
130}
131
132impl<'a, T> ControlRegisters<'a> for T
133where
134	T: control_registers_access::ControlRegistersAccess<'a>,
135{
136	fn negotiate_features<DF>(self, driver_features: DF) -> DF
137	where
138		DF: FeatureBits + fmt::Debug + Copy,
139	{
140		let device_features = DF::from(self.read_device_features());
141		info!("device_features = {device_features:?}");
142		debug_assert!(
143			device_features.requirements_satisfied(),
144			"The device offers a feature which requires another feature which was not offered."
145		);
146
147		info!("driver_features = {driver_features:?}");
148		debug_assert!(
149			driver_features.requirements_satisfied(),
150			"The driver offers a feature which requires another feature which was not offered.",
151		);
152
153		let common_features = device_features.intersection(driver_features);
154		info!("common_features = {common_features:?}");
155		// This should be logically unreachable.
156		debug_assert!(
157			common_features.requirements_satisfied(),
158			"We negotiated a feature which requires another feature which was not negotiated."
159		);
160
161		self.write_driver_features(common_features.into());
162
163		common_features
164	}
165}
166
167pub(super) trait VirtioDriver: super::Driver + Sized
168where
169	virtio::F:
170		From<Self::DeviceFeatures> + AsRef<Self::DeviceFeatures> + AsMut<Self::DeviceFeatures>,
171{
172	type Config: 'static;
173	type Error: Into<VirtioError>;
174	type DeviceFeatures: FeatureBits + fmt::Debug + Copy;
175
176	const MINIMAL_FEATURES: Self::DeviceFeatures;
177	const OPTIONAL_FEATURES: Self::DeviceFeatures;
178
179	fn init_dev(
180		caps_tuple: (
181			UniCapsColl,
182			volatile::VolatileRef<'static, Self::Config, volatile::access::ReadOnly>,
183		),
184		handlers: &mut super::InterruptHandlerMap,
185		irq: Option<super::InterruptLine>,
186	) -> Result<Self, (VirtioError, UniCapsColl)>;
187
188	#[cfg(feature = "pci")]
189	fn no_dev_cfg_err(dev_id: u16) -> Self::Error;
190}
191
192impl UniCapsColl {
193	pub(super) fn init_caps<T: VirtioDriver>(
194		&mut self,
195		dev_cfg_raw: volatile::VolatileRef<'static, T::Config, volatile::access::ReadOnly>,
196		mut device_specific_setup: impl FnMut(&mut Self, &mut DevCfg<T>) -> Result<(), T::Error>,
197	) -> Result<DevCfg<T>, VirtioError>
198	where
199		virtio::F: From<T::DeviceFeatures> + AsRef<T::DeviceFeatures> + AsMut<T::DeviceFeatures>,
200	{
201		// Reset
202		self.com_cfg.reset_dev();
203
204		// Indicate device, that OS noticed it
205		self.com_cfg.ack_dev();
206
207		// Indicate device, that driver is able to handle it
208		self.com_cfg.set_drv();
209
210		let negotiated_features = self
211			.com_cfg
212			.control_registers()
213			.negotiate_features(T::MINIMAL_FEATURES.union(T::OPTIONAL_FEATURES));
214
215		if !negotiated_features.contains(T::MINIMAL_FEATURES) {
216			error!("Device features set, does not satisfy minimal features needed. Aborting!");
217			return Err(VirtioError::FailFeatureNeg);
218		}
219
220		// Indicates the device, that the current feature set is final for the driver
221		// and will not be changed.
222		self.com_cfg.features_ok();
223
224		// Checks if the device has accepted final set. This finishes feature negotiation.
225		let mut dev_cfg = if self.com_cfg.check_features() {
226			info!(
227				"Features have been negotiated between {} device and driver.",
228				T::get_name()
229			);
230			// Set feature set in device config for future use.
231			DevCfg {
232				raw: dev_cfg_raw,
233				features: negotiated_features,
234			}
235		} else {
236			error!("The device does not support our subset of features.");
237			return Err(VirtioError::FailFeatureNeg);
238		};
239
240		device_specific_setup(self, &mut dev_cfg).map_err(|err| err.into())?;
241
242		// At this point the device is "live"
243		self.com_cfg.drv_ok();
244
245		Ok(dev_cfg)
246	}
247}
248
249/// A wrapper struct for the raw configuration structure.
250/// Handling the right access to fields, as some are read-only
251/// for the driver.
252pub(super) struct DevCfg<T: VirtioDriver>
253where
254	virtio::F: From<T::DeviceFeatures> + AsRef<T::DeviceFeatures> + AsMut<T::DeviceFeatures>,
255{
256	pub(super) features: T::DeviceFeatures,
257	#[cfg_attr(
258		all(
259			not(any(
260				feature = "virtio-fs",
261				feature = "virtio-net",
262				feature = "virtio-vsock",
263			)),
264			feature = "virtio-console"
265		),
266		expect(dead_code)
267	)]
268	pub(super) raw: volatile::VolatileRef<'static, T::Config, volatile::access::ReadOnly>,
269}
270
271pub mod error {
272	use thiserror::Error;
273
274	#[cfg(feature = "virtio-console")]
275	pub use crate::drivers::console::error::VirtioConsoleError;
276	#[cfg(feature = "virtio-fs")]
277	pub use crate::drivers::fs::error::VirtioFsInitError;
278	#[cfg(all(
279		not(all(target_arch = "riscv64", feature = "gem-net", not(feature = "pci"))),
280		not(feature = "rtl8139"),
281		feature = "virtio-net",
282	))]
283	pub use crate::drivers::net::virtio::error::VirtioNetError;
284	#[cfg(feature = "pci")]
285	use crate::drivers::pci::error::PciError;
286	#[cfg(feature = "virtio-rng")]
287	pub use crate::drivers::rng::error::VirtioRngError;
288	#[cfg(feature = "virtio-vsock")]
289	pub use crate::drivers::vsock::error::VirtioVsockError;
290
291	#[derive(Error, Debug)]
292	pub enum VirtioError {
293		#[cfg(feature = "pci")]
294		#[error(transparent)]
295		FromPci(PciError),
296
297		#[cfg(feature = "pci")]
298		#[error(
299			"Virtio driver failed, for device {0:x}, due to a missing or malformed common config!"
300		)]
301		NoComCfg(u16),
302
303		#[cfg(feature = "pci")]
304		#[error(
305			"Virtio driver failed, for device {0:x}, due to a missing or malformed ISR status config!"
306		)]
307		NoIsrCfg(u16),
308
309		#[cfg(feature = "pci")]
310		#[error(
311			"Virtio driver failed, for device {0:x}, due to a missing or malformed notification config!"
312		)]
313		NoNotifCfg(u16),
314
315		#[error("Device with id {0:#x} not supported.")]
316		DevNotSupported(u16),
317
318		#[error("Virtio driver failed, device did not acknowledge negotiated feature set!")]
319		FailFeatureNeg,
320
321		#[cfg(all(
322			not(all(target_arch = "riscv64", feature = "gem-net", not(feature = "pci"))),
323			not(feature = "rtl8139"),
324			feature = "virtio-net",
325		))]
326		#[error(transparent)]
327		NetDriver(#[from] VirtioNetError),
328
329		#[cfg(feature = "virtio-fs")]
330		#[error(transparent)]
331		FsDriver(#[from] VirtioFsInitError),
332
333		#[cfg(feature = "virtio-vsock")]
334		#[error(transparent)]
335		VsockDriver(#[from] VirtioVsockError),
336
337		#[cfg(feature = "virtio-console")]
338		#[error(transparent)]
339		ConsoleDriver(#[from] VirtioConsoleError),
340
341		#[cfg(feature = "virtio-rng")]
342		#[error(transparent)]
343		RngDriver(#[from] VirtioRngError),
344	}
345}