hermit/drivers/fs/
virtio_fs.rs1use alloc::borrow::ToOwned;
2use alloc::boxed::Box;
3use alloc::string::String;
4use alloc::vec::Vec;
5use core::mem::MaybeUninit;
6use core::str;
7
8use fuse_abi::linux::fuse_out_header;
9use num_enum::TryFromPrimitive;
10use pci_types::InterruptLine;
11use smallvec::SmallVec;
12use virtio::FeatureBits;
13use virtio::fs::ConfigVolatileFieldAccess;
14use volatile::VolatileRef;
15use volatile::access::ReadOnly;
16
17use crate::config::VIRTIO_MAX_QUEUE_SIZE;
18use crate::drivers::Driver;
19use crate::drivers::virtio::error::VirtioFsError;
20#[cfg(not(feature = "pci"))]
21use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, NotifCfg};
22#[cfg(feature = "pci")]
23use crate::drivers::virtio::transport::pci::{ComCfg, IsrStatus, NotifCfg};
24use crate::drivers::virtio::virtqueue::error::VirtqError;
25use crate::drivers::virtio::virtqueue::split::SplitVq;
26use crate::drivers::virtio::virtqueue::{
27 AvailBufferToken, BufferElem, BufferType, VirtQueue, Virtq,
28};
29use crate::errno::Errno;
30use crate::fs::fuse::{self, FuseError, FuseInterface, Rsp, RspHeader};
31use crate::mm::device_alloc::DeviceAlloc;
32
33pub(crate) struct FsDevCfg {
37 pub raw: VolatileRef<'static, virtio::fs::Config, ReadOnly>,
38 pub dev_id: u16,
39 pub features: virtio::fs::F,
40}
41
42#[allow(dead_code)]
47pub(crate) struct VirtioFsDriver {
48 pub(super) dev_cfg: FsDevCfg,
49 pub(super) com_cfg: ComCfg,
50 pub(super) isr_stat: IsrStatus,
51 pub(super) notif_cfg: NotifCfg,
52 pub(super) vqueues: Vec<VirtQueue>,
53 pub(super) irq: InterruptLine,
54}
55
56impl VirtioFsDriver {
58 #[cfg(feature = "pci")]
59 pub fn get_dev_id(&self) -> u16 {
60 self.dev_cfg.dev_id
61 }
62
63 #[cfg(feature = "pci")]
64 pub fn set_failed(&mut self) {
65 self.com_cfg.set_failed();
66 }
67
68 fn negotiate_features(&mut self, driver_features: virtio::fs::F) -> Result<(), VirtioFsError> {
71 let device_features = virtio::fs::F::from(self.com_cfg.dev_features());
72
73 if device_features.requirements_satisfied() {
74 debug!(
75 "Feature set wanted by filesystem driver are in conformance with specification."
76 );
77 } else {
78 return Err(VirtioFsError::FeatureRequirementsNotMet(device_features));
79 }
80
81 if device_features.contains(driver_features) {
82 self.com_cfg.set_drv_features(driver_features.into());
84 Ok(())
85 } else {
86 Err(VirtioFsError::IncompatibleFeatureSets(
87 driver_features,
88 device_features,
89 ))
90 }
91 }
92
93 pub(crate) fn init_dev(&mut self) -> Result<(), VirtioFsError> {
99 self.com_cfg.reset_dev();
101
102 self.com_cfg.ack_dev();
104
105 self.com_cfg.set_drv();
107
108 let features = virtio::fs::F::VERSION_1;
109 self.negotiate_features(features)?;
110
111 self.com_cfg.features_ok();
114
115 if self.com_cfg.check_features() {
117 info!(
118 "Features have been negotiated between virtio filesystem device {:x} and driver.",
119 self.dev_cfg.dev_id
120 );
121 self.dev_cfg.features = features;
123 } else {
124 return Err(VirtioFsError::FailFeatureNeg(self.dev_cfg.dev_id));
125 }
126
127 let vqnum = self
129 .dev_cfg
130 .raw
131 .as_ptr()
132 .num_request_queues()
133 .read()
134 .to_ne() + 1;
135 if vqnum == 0 {
136 error!("0 request queues requested from device. Aborting!");
137 return Err(VirtioFsError::Unknown);
138 }
139
140 for i in 0..vqnum as u16 {
142 let vq = VirtQueue::Split(
143 SplitVq::new(
144 &mut self.com_cfg,
145 &self.notif_cfg,
146 VIRTIO_MAX_QUEUE_SIZE,
147 i,
148 self.dev_cfg.features.into(),
149 )
150 .unwrap(),
151 );
152 self.vqueues.push(vq);
153 }
154
155 self.com_cfg.drv_ok();
157
158 Ok(())
159 }
160}
161
162impl FuseInterface for VirtioFsDriver {
163 fn send_command<O: fuse::ops::Op + 'static>(
164 &mut self,
165 cmd: fuse::Cmd<O>,
166 rsp_payload_len: u32,
167 ) -> Result<fuse::Rsp<O>, FuseError>
168 where
169 <O as fuse::ops::Op>::InStruct: Send,
170 <O as fuse::ops::Op>::OutStruct: Send,
171 {
172 let fuse::Cmd {
173 headers: cmd_headers,
174 payload: cmd_payload_opt,
175 } = cmd;
176 let send = if let Some(cmd_payload) = cmd_payload_opt {
177 SmallVec::from_buf([
178 BufferElem::Sized(cmd_headers),
179 BufferElem::Vector(cmd_payload),
180 ])
181 } else {
182 let mut vec = SmallVec::new();
183 vec.push(BufferElem::Sized(cmd_headers));
184 vec
185 };
186
187 let rsp_headers =
191 Box::<RspHeader<O, MaybeUninit<O::OutStruct>>, _>::new_uninit_in(DeviceAlloc);
192 let recv = if rsp_payload_len == 0 {
193 let mut vec = SmallVec::new();
194 vec.push(BufferElem::Sized(rsp_headers));
195 vec
196 } else {
197 SmallVec::from_buf([
198 BufferElem::Sized(rsp_headers),
199 BufferElem::Vector(Vec::with_capacity_in(rsp_payload_len as usize, DeviceAlloc)),
200 ])
201 };
202
203 let buffer_tkn = AvailBufferToken::new(send, recv).unwrap();
204 let mut transfer_result =
205 self.vqueues[1].dispatch_blocking(buffer_tkn, BufferType::Direct)?;
206
207 let (dyn_headers, written_header_len) =
208 transfer_result.used_recv_buff.pop_front_raw().unwrap();
209 let headers = dyn_headers
210 .downcast::<MaybeUninit<RspHeader<O, MaybeUninit<O::OutStruct>>>>()
211 .unwrap();
212 if written_header_len < size_of::<fuse_out_header>() {
213 return Err(VirtqError::IncompleteWrite.into());
214 }
215
216 let headers = unsafe { headers.assume_init() };
219
220 if headers.out_header.error != 0
221 || (written_header_len - size_of::<fuse_out_header>()) != size_of::<O::OutStruct>()
222 {
223 return Err(FuseError::IOError(
227 Errno::try_from_primitive(-headers.out_header.error).unwrap_or(Errno::Io),
228 ));
229 }
230
231 let headers = unsafe {
233 core::mem::transmute::<
234 Box<RspHeader<O, MaybeUninit<O::OutStruct>>, _>,
235 Box<RspHeader<O>, _>,
236 >(headers)
237 };
238 let payload = transfer_result.used_recv_buff.pop_front_vec();
239 Ok(Rsp { headers, payload })
240 }
241
242 fn get_mount_point(&self) -> String {
243 let tag = self.dev_cfg.raw.as_ptr().tag().read();
244 let tag = str::from_utf8(&tag).unwrap();
245 let tag = tag.split('\0').next().unwrap();
246 tag.to_owned()
247 }
248}
249
250impl Driver for VirtioFsDriver {
251 fn get_interrupt_number(&self) -> InterruptLine {
252 self.irq
253 }
254
255 fn get_name(&self) -> &'static str {
256 "virtio"
257 }
258}
259
260pub mod error {
262 use thiserror::Error;
263
264 #[derive(Error, Debug, Copy, Clone)]
266 pub enum VirtioFsError {
267 #[cfg(feature = "pci")]
268 #[error(
269 "Virtio filesystem driver failed, for device {0:x}, due to a missing or malformed device config!"
270 )]
271 NoDevCfg(u16),
272
273 #[error(
274 "Virtio filesystem driver failed, for device {0:x}, device did not acknowledge negotiated feature set!"
275 )]
276 FailFeatureNeg(u16),
277
278 #[error("Feature set: {0:?} , is incompatible with the device features: {1:?}")]
281 IncompatibleFeatureSets(virtio::fs::F, virtio::fs::F),
282
283 #[error(
286 "Virtio filesystem driver tried to set feature bit without setting dependency feature. Feat set: {0:?}"
287 )]
288 FeatureRequirementsNotMet(virtio::fs::F),
289
290 #[error("Virtio filesystem failed, driver failed due unknown reason!")]
291 Unknown,
292 }
293}