hermit/drivers/fs/
virtio_fs.rs

1use alloc::boxed::Box;
2use alloc::string::{String, ToString};
3use alloc::vec::Vec;
4use core::str;
5
6use pci_types::InterruptLine;
7use virtio::FeatureBits;
8use virtio::fs::ConfigVolatileFieldAccess;
9use volatile::VolatileRef;
10use volatile::access::ReadOnly;
11
12use crate::config::VIRTIO_MAX_QUEUE_SIZE;
13use crate::drivers::Driver;
14use crate::drivers::virtio::error::VirtioFsError;
15#[cfg(not(feature = "pci"))]
16use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, NotifCfg};
17#[cfg(feature = "pci")]
18use crate::drivers::virtio::transport::pci::{ComCfg, IsrStatus, NotifCfg};
19use crate::drivers::virtio::virtqueue::error::VirtqError;
20use crate::drivers::virtio::virtqueue::split::SplitVq;
21use crate::drivers::virtio::virtqueue::{
22	AvailBufferToken, BufferElem, BufferType, Virtq, VqIndex, VqSize,
23};
24use crate::fs::fuse::{self, FuseInterface, Rsp, RspHeader};
25use crate::mm::device_alloc::DeviceAlloc;
26
27/// A wrapper struct for the raw configuration structure.
28/// Handling the right access to fields, as some are read-only
29/// for the driver.
30pub(crate) struct FsDevCfg {
31	pub raw: VolatileRef<'static, virtio::fs::Config, ReadOnly>,
32	pub dev_id: u16,
33	pub features: virtio::fs::F,
34}
35
36/// Virtio file system driver struct.
37///
38/// Struct allows to control devices virtqueues as also
39/// the device itself.
40#[allow(dead_code)]
41pub(crate) struct VirtioFsDriver {
42	pub(super) dev_cfg: FsDevCfg,
43	pub(super) com_cfg: ComCfg,
44	pub(super) isr_stat: IsrStatus,
45	pub(super) notif_cfg: NotifCfg,
46	pub(super) vqueues: Vec<Box<dyn Virtq>>,
47	pub(super) irq: InterruptLine,
48}
49
50// Backend-independent interface for Virtio network driver
51impl VirtioFsDriver {
52	#[cfg(feature = "pci")]
53	pub fn get_dev_id(&self) -> u16 {
54		self.dev_cfg.dev_id
55	}
56
57	#[cfg(feature = "pci")]
58	pub fn set_failed(&mut self) {
59		self.com_cfg.set_failed();
60	}
61
62	/// Negotiates a subset of features, understood and wanted by both the OS
63	/// and the device.
64	fn negotiate_features(&mut self, driver_features: virtio::fs::F) -> Result<(), VirtioFsError> {
65		let device_features = virtio::fs::F::from(self.com_cfg.dev_features());
66
67		if device_features.requirements_satisfied() {
68			debug!(
69				"Feature set wanted by filesystem driver are in conformance with specification."
70			);
71		} else {
72			return Err(VirtioFsError::FeatureRequirementsNotMet(device_features));
73		}
74
75		if device_features.contains(driver_features) {
76			// If device supports subset of features write feature set to common config
77			self.com_cfg.set_drv_features(driver_features.into());
78			Ok(())
79		} else {
80			Err(VirtioFsError::IncompatibleFeatureSets(
81				driver_features,
82				device_features,
83			))
84		}
85	}
86
87	/// Initializes the device in adherence to specification. Returns Some(VirtioFsError)
88	/// upon failure and None in case everything worked as expected.
89	///
90	/// See Virtio specification v1.1. - 3.1.1.
91	///                      and v1.1. - 5.11.5
92	pub(crate) fn init_dev(&mut self) -> Result<(), VirtioFsError> {
93		// Reset
94		self.com_cfg.reset_dev();
95
96		// Indicate device, that OS noticed it
97		self.com_cfg.ack_dev();
98
99		// Indicate device, that driver is able to handle it
100		self.com_cfg.set_drv();
101
102		let features = virtio::fs::F::VERSION_1;
103		self.negotiate_features(features)?;
104
105		// Indicates the device, that the current feature set is final for the driver
106		// and will not be changed.
107		self.com_cfg.features_ok();
108
109		// Checks if the device has accepted final set. This finishes feature negotiation.
110		if self.com_cfg.check_features() {
111			info!(
112				"Features have been negotiated between virtio filesystem device {:x} and driver.",
113				self.dev_cfg.dev_id
114			);
115			// Set feature set in device config fur future use.
116			self.dev_cfg.features = features;
117		} else {
118			return Err(VirtioFsError::FailFeatureNeg(self.dev_cfg.dev_id));
119		}
120
121		// 1 highprio queue, and n normal request queues
122		let vqnum = self
123			.dev_cfg
124			.raw
125			.as_ptr()
126			.num_request_queues()
127			.read()
128			.to_ne() + 1;
129		if vqnum == 0 {
130			error!("0 request queues requested from device. Aborting!");
131			return Err(VirtioFsError::Unknown);
132		}
133
134		// create the queues and tell device about them
135		for i in 0..vqnum as u16 {
136			let vq = SplitVq::new(
137				&mut self.com_cfg,
138				&self.notif_cfg,
139				VqSize::from(VIRTIO_MAX_QUEUE_SIZE),
140				VqIndex::from(i),
141				self.dev_cfg.features.into(),
142			)
143			.unwrap();
144			self.vqueues.push(Box::new(vq));
145		}
146
147		// At this point the device is "live"
148		self.com_cfg.drv_ok();
149
150		Ok(())
151	}
152}
153
154impl FuseInterface for VirtioFsDriver {
155	fn send_command<O: fuse::ops::Op + 'static>(
156		&mut self,
157		cmd: fuse::Cmd<O>,
158		rsp_payload_len: u32,
159	) -> Result<fuse::Rsp<O>, VirtqError>
160	where
161		<O as fuse::ops::Op>::InStruct: Send,
162		<O as fuse::ops::Op>::OutStruct: Send,
163	{
164		let fuse::Cmd {
165			headers: cmd_headers,
166			payload: cmd_payload_opt,
167		} = cmd;
168		let send = if let Some(cmd_payload) = cmd_payload_opt {
169			vec![
170				BufferElem::Sized(cmd_headers),
171				BufferElem::Vector(cmd_payload),
172			]
173		} else {
174			vec![BufferElem::Sized(cmd_headers)]
175		};
176
177		let rsp_headers = Box::<RspHeader<O>, _>::new_uninit_in(DeviceAlloc);
178		let recv = if rsp_payload_len == 0 {
179			vec![BufferElem::Sized(rsp_headers)]
180		} else {
181			let rsp_payload = Vec::with_capacity_in(rsp_payload_len as usize, DeviceAlloc);
182			vec![
183				BufferElem::Sized(rsp_headers),
184				BufferElem::Vector(rsp_payload),
185			]
186		};
187
188		let buffer_tkn = AvailBufferToken::new(send, recv).unwrap();
189		let mut transfer_result =
190			self.vqueues[1].dispatch_blocking(buffer_tkn, BufferType::Direct)?;
191
192		let headers = transfer_result.used_recv_buff.pop_front_downcast().unwrap();
193		let payload = transfer_result.used_recv_buff.pop_front_vec();
194		Ok(Rsp { headers, payload })
195	}
196
197	fn get_mount_point(&self) -> String {
198		let tag = self.dev_cfg.raw.as_ptr().tag().read();
199		let tag = str::from_utf8(&tag).unwrap();
200		let tag = tag.split('\0').next().unwrap();
201		tag.to_string()
202	}
203}
204
205impl Driver for VirtioFsDriver {
206	fn get_interrupt_number(&self) -> InterruptLine {
207		self.irq
208	}
209
210	fn get_name(&self) -> &'static str {
211		"virtio"
212	}
213}
214
215/// Error module of virtios filesystem driver.
216pub mod error {
217	/// Network filesystem error enum.
218	#[derive(Debug, Copy, Clone)]
219	pub enum VirtioFsError {
220		#[cfg(feature = "pci")]
221		NoDevCfg(u16),
222		FailFeatureNeg(u16),
223		/// The first field contains the feature bits wanted by the driver.
224		/// but which are incompatible with the device feature set, second field.
225		IncompatibleFeatureSets(virtio::fs::F, virtio::fs::F),
226		/// Set of features does not adhere to the requirements of features
227		/// indicated by the specification
228		FeatureRequirementsNotMet(virtio::fs::F),
229		Unknown,
230	}
231}