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 virtio::FeatureBits;
21
22trait VirtioIdExt {
23	fn as_feature(&self) -> Option<&str>;
24}
25
26impl VirtioIdExt for virtio::Id {
27	fn as_feature(&self) -> Option<&str> {
28		let feature = match self {
29			Self::Net => "virtio-net",
30			Self::Console => "virtio-console",
31			Self::Fs => "virtio-fs",
32			Self::Vsock => "virtio-vsock",
33			_ => return None,
34		};
35
36		Some(feature)
37	}
38}
39
40mod control_registers_access {
41	use core::{array, mem};
42
43	use virtio::{le32, le128};
44	use volatile::VolatilePtr;
45	use volatile::access::ReadWrite;
46
47	pub trait ControlRegistersAccess<'a>: Sized + Copy {
48		fn read_device_feature_word(self, i: u32) -> le32;
49		fn write_driver_feature_word(self, i: u32, word: le32);
50
51		fn read_device_features(self) -> virtio::F {
52			let features = array::from_fn(|i| {
53				let i = u32::try_from(i).unwrap();
54				self.read_device_feature_word(i)
55			});
56
57			let features = unsafe { mem::transmute::<[le32; 4], le128>(features) };
58
59			virtio::F::from_bits_retain(features)
60		}
61
62		fn write_driver_features(self, features: virtio::F) {
63			let features = features.bits();
64
65			let features = unsafe { mem::transmute::<le128, [le32; 4]>(features) };
66
67			for (i, word) in features.into_iter().enumerate() {
68				let i = u32::try_from(i).unwrap();
69				self.write_driver_feature_word(i, word);
70			}
71		}
72	}
73
74	#[cfg(feature = "pci")]
75	impl<'a> ControlRegistersAccess<'a> for VolatilePtr<'a, virtio::pci::CommonCfg, ReadWrite> {
76		fn read_device_feature_word(self, i: u32) -> le32 {
77			use virtio::pci::CommonCfgVolatileFieldAccess;
78
79			self.device_feature_select().write(i.into());
80			self.device_feature().read()
81		}
82
83		fn write_driver_feature_word(self, i: u32, word: le32) {
84			use virtio::pci::CommonCfgVolatileFieldAccess;
85
86			self.driver_feature_select().write(i.into());
87			self.driver_feature().write(word);
88		}
89	}
90
91	#[cfg(not(feature = "pci"))]
92	impl<'a> ControlRegistersAccess<'a> for VolatilePtr<'a, virtio::mmio::DeviceRegisters, ReadWrite> {
93		fn read_device_feature_word(self, i: u32) -> le32 {
94			use virtio::mmio::DeviceRegistersVolatileFieldAccess;
95
96			// QEMU only supports index 0 and 1 for virtio-mmio:
97			// https://gitlab.com/qemu-project/qemu/-/blob/v10.2.0/hw/virtio/virtio-mmio.c#L305-311
98			if i > 1 {
99				return 0.into();
100			}
101
102			self.device_features_sel().write(i.into());
103			self.device_features().read()
104		}
105
106		fn write_driver_feature_word(self, i: u32, word: le32) {
107			use virtio::mmio::DeviceRegistersVolatileFieldAccess;
108
109			// QEMU only supports index 0 and 1 for virtio-mmio:
110			// https://gitlab.com/qemu-project/qemu/-/blob/v10.2.0/hw/virtio/virtio-mmio.c#L326-332
111			if i > 1 {
112				debug_assert!(word.to_ne() == 0);
113				return;
114			}
115
116			self.driver_features_sel().write(i.into());
117			self.driver_features().write(word);
118		}
119	}
120}
121
122pub trait ControlRegisters<'a>: self::control_registers_access::ControlRegistersAccess<'a> {
123	fn negotiate_features<DF>(self, driver_features: DF) -> DF
124	where
125		DF: FeatureBits + From<virtio::F> + AsRef<virtio::F> + AsMut<virtio::F> + fmt::Debug + Copy,
126		virtio::F: From<DF> + AsRef<DF> + AsMut<DF>;
127}
128
129impl<'a, T> ControlRegisters<'a> for T
130where
131	T: self::control_registers_access::ControlRegistersAccess<'a>,
132{
133	fn negotiate_features<DF>(self, driver_features: DF) -> DF
134	where
135		DF: FeatureBits + From<virtio::F> + AsRef<virtio::F> + AsMut<virtio::F> + fmt::Debug + Copy,
136		virtio::F: From<DF> + AsRef<DF> + AsMut<DF>,
137	{
138		let device_features = DF::from(self.read_device_features());
139		info!("device_features = {device_features:?}");
140		debug_assert!(
141			device_features.requirements_satisfied(),
142			"The device offers a feature which requires another feature which was not offered."
143		);
144
145		info!("driver_features = {driver_features:?}");
146		debug_assert!(
147			driver_features.requirements_satisfied(),
148			"The driver offers a feature which requires another feature which was not offered.",
149		);
150
151		let common_features = device_features.intersection(driver_features);
152		info!("common_features = {common_features:?}");
153		// This should be logically unreachable.
154		debug_assert!(
155			common_features.requirements_satisfied(),
156			"We negotiated a feature which requires another feature which was not negotiated."
157		);
158
159		self.write_driver_features(common_features.into());
160
161		common_features
162	}
163}
164
165pub mod error {
166	use thiserror::Error;
167
168	#[cfg(feature = "virtio-console")]
169	pub use crate::drivers::console::error::VirtioConsoleError;
170	#[cfg(feature = "virtio-fs")]
171	pub use crate::drivers::fs::error::VirtioFsError;
172	#[cfg(all(
173		not(all(target_arch = "riscv64", feature = "gem-net", not(feature = "pci"))),
174		not(feature = "rtl8139"),
175		feature = "virtio-net",
176	))]
177	pub use crate::drivers::net::virtio::error::VirtioNetError;
178	#[cfg(feature = "pci")]
179	use crate::drivers::pci::error::PciError;
180	#[cfg(feature = "virtio-vsock")]
181	pub use crate::drivers::vsock::error::VirtioVsockError;
182
183	#[derive(Error, Debug)]
184	pub enum VirtioError {
185		#[cfg(feature = "pci")]
186		#[error(transparent)]
187		FromPci(PciError),
188
189		#[cfg(feature = "pci")]
190		#[error(
191			"Virtio driver failed, for device {0:x}, due to a missing or malformed common config!"
192		)]
193		NoComCfg(u16),
194
195		#[cfg(feature = "pci")]
196		#[error(
197			"Virtio driver failed, for device {0:x}, due to a missing or malformed ISR status config!"
198		)]
199		NoIsrCfg(u16),
200
201		#[cfg(feature = "pci")]
202		#[error(
203			"Virtio driver failed, for device {0:x}, due to a missing or malformed notification config!"
204		)]
205		NoNotifCfg(u16),
206
207		#[error("Device with id {0:#x} not supported.")]
208		DevNotSupported(u16),
209
210		#[cfg(all(
211			not(all(target_arch = "riscv64", feature = "gem-net", not(feature = "pci"))),
212			not(feature = "rtl8139"),
213			feature = "virtio-net",
214		))]
215		#[error(transparent)]
216		NetDriver(VirtioNetError),
217
218		#[cfg(feature = "virtio-fs")]
219		#[error(transparent)]
220		FsDriver(VirtioFsError),
221
222		#[cfg(feature = "virtio-vsock")]
223		#[error(transparent)]
224		VsockDriver(VirtioVsockError),
225
226		#[cfg(feature = "virtio-console")]
227		#[error(transparent)]
228		ConsoleDriver(VirtioConsoleError),
229	}
230}