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