hermit/drivers/virtio/virtqueue/
split.rs1use alloc::boxed::Box;
11use alloc::vec::Vec;
12use core::cell::UnsafeCell;
13use core::mem::{self, MaybeUninit};
14
15use mem_barrier::BarrierType;
16#[cfg(not(feature = "pci"))]
17use virtio::mmio::NotificationData;
18#[cfg(feature = "pci")]
19use virtio::pci::NotificationData;
20use virtio::{le16, virtq};
21
22#[cfg(not(feature = "pci"))]
23use super::super::transport::mmio::{ComCfg, NotifCfg, NotifCtrl};
24#[cfg(feature = "pci")]
25use super::super::transport::pci::{ComCfg, NotifCfg, NotifCtrl};
26use super::error::VirtqError;
27use super::index_alloc::IndexAlloc;
28use super::{AvailBufferToken, BufferType, TransferToken, UsedBufferToken, Virtq, VirtqPrivate};
29use crate::mm::device_alloc::DeviceAlloc;
30
31struct DescrRing {
32 read_idx: u16,
33 token_ring: Box<[Option<TransferToken<virtq::Desc>>]>,
34 indexes: IndexAlloc,
35
36 descr_table_cell: Box<UnsafeCell<[MaybeUninit<virtq::Desc>]>, DeviceAlloc>,
37 avail_ring_cell: Box<UnsafeCell<virtq::Avail>, DeviceAlloc>,
38 used_ring_cell: Box<UnsafeCell<virtq::Used>, DeviceAlloc>,
39 order_platform: bool,
40}
41
42impl DescrRing {
43 fn descr_table_mut(&mut self) -> &mut [MaybeUninit<virtq::Desc>] {
44 unsafe { &mut *self.descr_table_cell.get() }
45 }
46 #[expect(dead_code)]
47 fn avail_ring(&self) -> &virtq::Avail {
48 unsafe { &*self.avail_ring_cell.get() }
49 }
50 fn avail_ring_mut(&mut self) -> &mut virtq::Avail {
51 unsafe { &mut *self.avail_ring_cell.get() }
52 }
53 fn used_ring(&self) -> &virtq::Used {
54 unsafe { &*self.used_ring_cell.get() }
55 }
56
57 fn push(&mut self, tkn: TransferToken<virtq::Desc>) -> Result<u16, VirtqError> {
58 let mut index;
59 if let Some(ctrl_desc) = tkn.ctrl_desc.as_ref() {
60 let descriptor = SplitVq::indirect_desc(ctrl_desc.as_ref());
61
62 index = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
63 self.descr_table_mut()[index] = MaybeUninit::new(descriptor);
64 } else {
65 let mut rev_all_desc_iter = SplitVq::descriptor_iter(&tkn.buff_tkn)?.rev();
66
67 {
69 let descriptor = rev_all_desc_iter.next().unwrap();
71
72 index = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
73 self.descr_table_mut()[index] = MaybeUninit::new(descriptor);
74 }
75 for mut descriptor in rev_all_desc_iter {
76 descriptor.next = le16::from_ne(index.try_into().unwrap());
78
79 index = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
80 self.descr_table_mut()[index] = MaybeUninit::new(descriptor);
81 }
82 }
85
86 self.token_ring[index] = Some(tkn);
87
88 let len = self.token_ring.len();
89 let idx = self.avail_ring_mut().idx.to_ne();
90 self.avail_ring_mut().ring_mut(true)[idx as usize % len] =
91 le16::from_ne(index.try_into().unwrap());
92
93 super::virtio_mem_barrier(BarrierType::Write, self.order_platform);
94 let next_idx = idx.wrapping_add(1);
95 self.avail_ring_mut().idx = next_idx.into();
96
97 Ok(next_idx)
98 }
99
100 fn try_recv(&mut self) -> Result<UsedBufferToken, VirtqError> {
101 super::virtio_mem_barrier(BarrierType::Read, self.order_platform);
102 if self.read_idx == self.used_ring().idx.to_ne() {
103 return Err(VirtqError::NoNewUsed);
104 }
105 let cur_ring_index = self.read_idx as usize % self.token_ring.len();
106 let used_elem = self.used_ring().ring()[cur_ring_index];
107
108 let tkn = self.token_ring[used_elem.id.to_ne() as usize]
109 .take()
110 .expect(
111 "The buff_id is incorrect or the reference to the TransferToken was misplaced.",
112 );
113
114 let mut id_ret_idx = u16::try_from(used_elem.id.to_ne()).unwrap();
116 loop {
117 unsafe {
118 self.indexes.deallocate(id_ret_idx.into());
119 }
120 let cur_chain_elem =
121 unsafe { self.descr_table_mut()[usize::from(id_ret_idx)].assume_init() };
122 if cur_chain_elem.flags.contains(virtq::DescF::NEXT) {
123 id_ret_idx = cur_chain_elem.next.to_ne();
124 } else {
125 break;
126 }
127 }
128
129 self.read_idx = self.read_idx.wrapping_add(1);
130 Ok(UsedBufferToken::from_avail_buffer_token(
131 tkn.buff_tkn,
132 used_elem.len.to_ne(),
133 ))
134 }
135
136 fn drv_enable_notif(&mut self) {
137 self.avail_ring_mut()
138 .flags
139 .remove(virtq::AvailF::NO_INTERRUPT);
140 }
141
142 fn drv_disable_notif(&mut self) {
143 self.avail_ring_mut()
144 .flags
145 .insert(virtq::AvailF::NO_INTERRUPT);
146 }
147
148 fn dev_is_notif(&self) -> bool {
149 super::virtio_mem_barrier(BarrierType::General, self.order_platform);
150 !self.used_ring().flags.contains(virtq::UsedF::NO_NOTIFY)
151 }
152}
153
154pub struct SplitVq {
156 ring: DescrRing,
157 size: u16,
158 index: u16,
159
160 notif_ctrl: NotifCtrl,
161}
162
163impl Virtq for SplitVq {
164 fn enable_notifs(&mut self) {
165 self.ring.drv_enable_notif();
166 }
167
168 fn disable_notifs(&mut self) {
169 self.ring.drv_disable_notif();
170 }
171
172 fn try_recv(&mut self) -> Result<UsedBufferToken, VirtqError> {
173 self.ring.try_recv()
174 }
175
176 fn dispatch_batch(
177 &mut self,
178 _tkns: Vec<(AvailBufferToken, BufferType)>,
179 _notif: bool,
180 ) -> Result<(), VirtqError> {
181 unimplemented!();
182 }
183
184 fn dispatch_batch_await(
185 &mut self,
186 _tkns: Vec<(AvailBufferToken, BufferType)>,
187 _notif: bool,
188 ) -> Result<(), VirtqError> {
189 unimplemented!()
190 }
191
192 fn dispatch(
193 &mut self,
194 buffer_tkn: AvailBufferToken,
195 notif: bool,
196 buffer_type: BufferType,
197 ) -> Result<(), VirtqError> {
198 let transfer_tkn = Self::transfer_token_from_buffer_token(buffer_tkn, buffer_type);
199 let next_idx = self.ring.push(transfer_tkn)?;
200
201 if notif {
202 unimplemented!();
205 }
206
207 if self.ring.dev_is_notif() {
208 let notification_data = NotificationData::new()
209 .with_vq_notif_config_data(self.index)
210 .with_next_idx(next_idx);
211 self.notif_ctrl.notify_dev(notification_data);
212 }
213 Ok(())
214 }
215
216 fn index(&self) -> u16 {
217 self.index
218 }
219
220 fn size(&self) -> u16 {
221 self.size
222 }
223
224 fn has_used_buffers(&self) -> bool {
225 self.ring.read_idx != self.ring.used_ring().idx.to_ne()
226 }
227}
228
229impl VirtqPrivate for SplitVq {
230 type Descriptor = virtq::Desc;
231 fn create_indirect_ctrl(
232 buffer_tkn: &AvailBufferToken,
233 ) -> Result<Box<[Self::Descriptor]>, VirtqError> {
234 Ok(Self::descriptor_iter(buffer_tkn)?
235 .zip(1..)
236 .map(|(descriptor, next_id)| Self::Descriptor {
237 next: next_id.into(),
238 ..descriptor
239 })
240 .collect::<Vec<_>>()
241 .into_boxed_slice())
242 }
243}
244
245impl SplitVq {
246 pub(crate) fn new(
247 com_cfg: &mut ComCfg,
248 notif_cfg: &NotifCfg,
249 max_size: u16,
250 index: u16,
251 features: virtio::F,
252 ) -> Result<Self, VirtqError> {
253 let mut vq_handler = com_cfg
255 .select_vq(index)
256 .ok_or(VirtqError::QueueNotExisting(index))?;
257
258 let size = vq_handler.set_vq_size(max_size);
259
260 let mut descr_table_cell = unsafe {
261 mem::transmute::<
262 Box<[MaybeUninit<virtq::Desc>], DeviceAlloc>,
263 Box<UnsafeCell<[MaybeUninit<virtq::Desc>]>, DeviceAlloc>,
264 >(Box::new_uninit_slice_in(size.into(), DeviceAlloc))
265 };
266
267 let mut avail_ring_cell = {
268 let avail = virtq::Avail::try_new_in(size, true, DeviceAlloc)
269 .map_err(|_| VirtqError::AllocationError)?;
270
271 unsafe {
272 mem::transmute::<
273 Box<virtq::Avail, DeviceAlloc>,
274 Box<UnsafeCell<virtq::Avail>, DeviceAlloc>,
275 >(avail)
276 }
277 };
278
279 let mut used_ring_cell = {
280 let used = virtq::Used::try_new_in(size, true, DeviceAlloc)
281 .map_err(|_| VirtqError::AllocationError)?;
282
283 unsafe {
284 mem::transmute::<
285 Box<virtq::Used, DeviceAlloc>,
286 Box<UnsafeCell<virtq::Used>, DeviceAlloc>,
287 >(used)
288 }
289 };
290
291 vq_handler.set_ring_addr(DeviceAlloc.phys_addr_from(descr_table_cell.as_mut()));
293 vq_handler.set_drv_ctrl_addr(DeviceAlloc.phys_addr_from(avail_ring_cell.as_mut()));
295 vq_handler.set_dev_ctrl_addr(DeviceAlloc.phys_addr_from(used_ring_cell.as_mut()));
296
297 let order_platform = features.contains(virtio::F::ORDER_PLATFORM);
298
299 let descr_ring = DescrRing {
300 read_idx: 0,
301 token_ring: core::iter::repeat_with(|| None)
302 .take(size.into())
303 .collect::<Vec<_>>()
304 .into_boxed_slice(),
305 indexes: IndexAlloc::new(size.into()),
306
307 descr_table_cell,
308 avail_ring_cell,
309 used_ring_cell,
310 order_platform,
311 };
312
313 let mut notif_ctrl = NotifCtrl::new(notif_cfg.notification_location(&mut vq_handler));
314
315 if features.contains(virtio::F::NOTIFICATION_DATA) {
316 notif_ctrl.enable_notif_data();
317 }
318
319 vq_handler.enable_queue();
320
321 info!("Created SplitVq: idx={index}, size={size}");
322
323 Ok(SplitVq {
324 ring: descr_ring,
325 notif_ctrl,
326 size,
327 index,
328 })
329 }
330}