hermit/drivers/fs/
virtio_fs.rs1use 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
27pub(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#[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
50impl 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 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 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 pub(crate) fn init_dev(&mut self) -> Result<(), VirtioFsError> {
93 self.com_cfg.reset_dev();
95
96 self.com_cfg.ack_dev();
98
99 self.com_cfg.set_drv();
101
102 let features = virtio::fs::F::VERSION_1;
103 self.negotiate_features(features)?;
104
105 self.com_cfg.features_ok();
108
109 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 self.dev_cfg.features = features;
117 } else {
118 return Err(VirtioFsError::FailFeatureNeg(self.dev_cfg.dev_id));
119 }
120
121 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 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 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
215pub mod error {
217 #[derive(Debug, Copy, Clone)]
219 pub enum VirtioFsError {
220 #[cfg(feature = "pci")]
221 NoDevCfg(u16),
222 FailFeatureNeg(u16),
223 IncompatibleFeatureSets(virtio::fs::F, virtio::fs::F),
226 FeatureRequirementsNotMet(virtio::fs::F),
229 Unknown,
230 }
231}