hermit/drivers/virtio/virtqueue/
packed.rs1use alloc::boxed::Box;
11use alloc::vec::Vec;
12use core::cell::Cell;
13use core::ops;
14
15use align_address::Align;
16use mem_barrier::BarrierType;
17#[cfg(not(feature = "pci"))]
18use virtio::mmio::NotificationData;
19#[cfg(feature = "pci")]
20use virtio::pci::NotificationData;
21use virtio::pvirtq::{EventSuppressDesc, EventSuppressFlags};
22use virtio::virtq::DescF;
23use virtio::{RingEventFlags, pvirtq};
24
25#[cfg(not(feature = "pci"))]
26use super::super::transport::mmio::{ComCfg, NotifCfg, NotifCtrl};
27#[cfg(feature = "pci")]
28use super::super::transport::pci::{ComCfg, NotifCfg, NotifCtrl};
29use super::error::VirtqError;
30use super::index_alloc::IndexAlloc;
31use super::{AvailBufferToken, BufferType, TransferToken, UsedBufferToken, Virtq, VirtqPrivate};
32use crate::arch::mm::paging::{BasePageSize, PageSize};
33use crate::mm::device_alloc::DeviceAlloc;
34
35trait RingIndexRange {
36 fn wrapping_contains(&self, item: &EventSuppressDesc) -> bool;
37}
38
39impl RingIndexRange for ops::Range<EventSuppressDesc> {
40 fn wrapping_contains(&self, item: &EventSuppressDesc) -> bool {
41 let start_off = self.start.desc_event_off();
42 let start_wrap = self.start.desc_event_wrap();
43 let end_off = self.end.desc_event_off();
44 let end_wrap = self.end.desc_event_wrap();
45 let item_off = item.desc_event_off();
46 let item_wrap = item.desc_event_wrap();
47
48 if start_wrap == end_wrap {
49 item_wrap == start_wrap && start_off <= item_off && item_off < end_off
50 } else if item_wrap == start_wrap {
51 start_off <= item_off
52 } else {
53 debug_assert!(item_wrap == end_wrap);
54 item_off < end_off
55 }
56 }
57}
58
59struct DescriptorRing {
61 ring: Box<[pvirtq::Desc], DeviceAlloc>,
62 tkn_ref_ring: Box<[Option<TransferToken<pvirtq::Desc>>]>,
63
64 write_index: EventSuppressDesc,
69 capacity: u16,
71 poll_index: EventSuppressDesc,
75 indexes: IndexAlloc,
77 order_platform: bool,
80}
81
82impl DescriptorRing {
83 fn new(size: u16, order_platform: bool) -> Self {
84 let ring = unsafe { Box::new_zeroed_slice_in(size.into(), DeviceAlloc).assume_init() };
85
86 let tkn_ref_ring = core::iter::repeat_with(|| None)
88 .take(size.into())
89 .collect::<Vec<_>>()
90 .into_boxed_slice();
91
92 let write_index = EventSuppressDesc::new()
93 .with_desc_event_off(0)
94 .with_desc_event_wrap(1);
95
96 let poll_index = write_index;
97
98 DescriptorRing {
99 order_platform,
100 ring,
101 tkn_ref_ring,
102 write_index,
103 capacity: size,
104 poll_index,
105 indexes: IndexAlloc::new(size.into()),
106 }
107 }
108
109 fn try_recv(&mut self) -> Result<UsedBufferToken, VirtqError> {
111 let mut ctrl = self.get_read_ctrler();
112
113 ctrl.poll_next()
114 .map(|(tkn, written_len)| {
115 UsedBufferToken::from_avail_buffer_token(tkn.buff_tkn, written_len)
116 })
117 .ok_or(VirtqError::NoNewUsed)
118 }
119
120 fn push_batch(
121 &mut self,
122 tkn_lst: impl IntoIterator<Item = TransferToken<pvirtq::Desc>>,
123 ) -> Result<EventSuppressDesc, VirtqError> {
124 let mut tkn_iterator = tkn_lst.into_iter();
128 let Some(first_tkn) = tkn_iterator.next() else {
129 return Err(VirtqError::BufferNotSpecified);
131 };
132
133 let mut ctrl = self.push_without_making_available(&first_tkn)?;
134 let first_ctrl_settings = (ctrl.start, ctrl.buff_id, ctrl.first_flags);
135 let first_buffer = first_tkn;
136
137 for tkn in tkn_iterator {
139 ctrl.make_avail(tkn);
140 }
141
142 self.make_avail_with_state(
146 first_buffer,
147 first_ctrl_settings.0,
148 first_ctrl_settings.1,
149 first_ctrl_settings.2,
150 );
151
152 Ok(self.write_index)
153 }
154
155 fn push(&mut self, tkn: TransferToken<pvirtq::Desc>) -> Result<EventSuppressDesc, VirtqError> {
156 self.push_batch([tkn])
157 }
158
159 fn push_without_making_available(
160 &mut self,
161 tkn: &TransferToken<pvirtq::Desc>,
162 ) -> Result<WriteCtrl<'_>, VirtqError> {
163 if tkn.num_consuming_descr() > self.capacity {
164 return Err(VirtqError::NoDescrAvail);
165 }
166
167 let mut ctrl = self.get_write_ctrler()?;
170
171 if let Some(ctrl_desc) = tkn.ctrl_desc.as_ref() {
177 let desc = PackedVq::indirect_desc(ctrl_desc.as_ref());
178 ctrl.write_desc(desc);
179 } else {
180 for incomplete_desc in PackedVq::descriptor_iter(&tkn.buff_tkn)? {
181 ctrl.write_desc(incomplete_desc);
182 }
183 }
184 Ok(ctrl)
185 }
186
187 fn as_mut_ptr(&mut self) -> *mut pvirtq::Desc {
188 self.ring.as_mut_ptr()
189 }
190
191 fn get_write_ctrler(&mut self) -> Result<WriteCtrl<'_>, VirtqError> {
194 let desc_id = self.indexes.allocate().ok_or(VirtqError::NoDescrAvail)?;
195 Ok(WriteCtrl {
196 start: self.write_index.desc_event_off(),
197 position: self.write_index.desc_event_off(),
198 modulo: u16::try_from(self.ring.len()).unwrap(),
199 first_flags: DescF::empty(),
200 buff_id: u16::try_from(desc_id).unwrap(),
201
202 desc_ring: self,
203 })
204 }
205
206 fn get_read_ctrler(&mut self) -> ReadCtrl<'_> {
209 ReadCtrl {
210 position: self.poll_index.desc_event_off(),
211 modulo: u16::try_from(self.ring.len()).unwrap(),
212
213 desc_ring: self,
214 }
215 }
216
217 fn make_avail_with_state(
218 &mut self,
219 raw_tkn: TransferToken<pvirtq::Desc>,
220 start: u16,
221 buff_id: u16,
222 first_flags: DescF,
223 ) {
224 self.tkn_ref_ring[usize::from(buff_id)] = Some(raw_tkn);
226 super::virtio_mem_barrier(BarrierType::Write, self.order_platform);
229 self.ring[usize::from(start)].flags = first_flags;
230 }
231
232 fn to_marked_avail(&self, mut flags: DescF) -> DescF {
244 let avail = self.write_index.desc_event_wrap() != 0;
245 flags.set(DescF::AVAIL, avail);
246 flags.set(DescF::USED, !avail);
247 flags
248 }
249
250 fn is_marked_used(&self, flags: DescF) -> bool {
258 if self.poll_index.desc_event_wrap() != 0 {
259 flags.contains(DescF::AVAIL | DescF::USED)
260 } else {
261 !flags.intersects(DescF::AVAIL | DescF::USED)
262 }
263 }
264}
265
266struct ReadCtrl<'a> {
267 position: u16,
269 modulo: u16,
270
271 desc_ring: &'a mut DescriptorRing,
272}
273
274impl ReadCtrl<'_> {
275 fn poll_next(&mut self) -> Option<(TransferToken<pvirtq::Desc>, u32)> {
278 let desc = &self.desc_ring.ring[usize::from(self.position)];
280 if !self.desc_ring.is_marked_used(desc.flags) {
281 return None;
282 }
283
284 super::virtio_mem_barrier(BarrierType::Read, self.desc_ring.order_platform);
289
290 let desc = &self.desc_ring.ring[usize::from(self.position)];
291 let buff_id = desc.id.to_ne();
292 let tkn = self.desc_ring.tkn_ref_ring[usize::from(buff_id)]
293 .take()
294 .expect(
295 "The buff_id is incorrect or the reference to the TransferToken was misplaced.",
296 );
297
298 let write_len = desc.len.to_ne();
318
319 for _ in 0..tkn.num_consuming_descr() {
320 self.incrmt();
321 }
322 unsafe {
323 self.desc_ring.indexes.deallocate(buff_id.into());
324 }
325
326 Some((tkn, write_len))
327 }
328
329 fn incrmt(&mut self) {
330 let mut desc = self.desc_ring.poll_index;
331
332 if desc.desc_event_off() + 1 == self.modulo {
333 let wrap = desc.desc_event_wrap() ^ 1;
334 desc.set_desc_event_wrap(wrap);
335 }
336
337 let off = (desc.desc_event_off() + 1) % self.modulo;
338 desc.set_desc_event_off(off);
339
340 self.desc_ring.poll_index = desc;
341
342 self.position = desc.desc_event_off();
343
344 assert!(self.desc_ring.capacity <= u16::try_from(self.desc_ring.ring.len()).unwrap());
346 self.desc_ring.capacity += 1;
347 }
348}
349
350struct WriteCtrl<'a> {
354 start: u16,
358 position: u16,
361 modulo: u16,
362 first_flags: DescF,
364 buff_id: u16,
366
367 desc_ring: &'a mut DescriptorRing,
368}
369
370impl WriteCtrl<'_> {
371 fn incrmt(&mut self) {
379 assert!(self.desc_ring.capacity != 0);
381 self.desc_ring.capacity -= 1;
382
383 let mut desc = self.desc_ring.write_index;
384
385 if self.position + 1 == self.modulo {
388 let wrap = desc.desc_event_wrap() ^ 1;
389 desc.set_desc_event_wrap(wrap);
390 }
391
392 let off = (desc.desc_event_off() + 1) % self.modulo;
394 desc.set_desc_event_off(off);
395
396 self.desc_ring.write_index = desc;
397
398 self.position = (self.position + 1) % self.modulo;
399 }
400
401 fn write_desc(&mut self, mut incomplete_desc: pvirtq::Desc) {
403 incomplete_desc.id = self.buff_id.into();
404 if self.start == self.position {
405 self.first_flags = self.desc_ring.to_marked_avail(incomplete_desc.flags);
409 } else {
410 incomplete_desc.flags = self.desc_ring.to_marked_avail(incomplete_desc.flags);
412 }
413 self.desc_ring.ring[usize::from(self.position)] = incomplete_desc;
414 self.incrmt();
415 }
416
417 fn make_avail(&mut self, raw_tkn: TransferToken<pvirtq::Desc>) {
418 assert!(self.start != self.position);
420 self.desc_ring
421 .make_avail_with_state(raw_tkn, self.start, self.buff_id, self.first_flags);
422 }
423}
424
425struct DrvNotif {
430 f_notif_idx: bool,
432 raw: &'static mut pvirtq::EventSuppress,
434}
435
436struct DevNotif {
441 f_notif_idx: bool,
443 raw: &'static mut pvirtq::EventSuppress,
445 order_platform: bool,
447}
448
449impl DrvNotif {
450 fn enable_notif(&mut self) {
453 self.raw.flags = EventSuppressFlags::new().with_desc_event_flags(RingEventFlags::Enable);
454 }
455
456 fn disable_notif(&mut self) {
459 self.raw.flags = EventSuppressFlags::new().with_desc_event_flags(RingEventFlags::Disable);
460 }
461
462 fn enable_specific(&mut self, desc: EventSuppressDesc) {
464 if self.f_notif_idx {
466 self.raw.flags = EventSuppressFlags::new().with_desc_event_flags(RingEventFlags::Desc);
467 self.raw.desc = desc;
468 }
469 }
470}
471
472impl DevNotif {
473 #[expect(dead_code)]
475 pub fn enable_notif_specific(&mut self) {
476 self.f_notif_idx = true;
477 }
478
479 #[inline]
482 fn order_against_avail(&self) {
483 super::virtio_mem_barrier(BarrierType::General, self.order_platform);
484 }
485
486 fn is_notif(&self) -> bool {
489 self.order_against_avail();
490
491 self.raw.flags.desc_event_flags() == RingEventFlags::Enable
492 }
493
494 fn notif_specific(&self) -> Option<EventSuppressDesc> {
495 if !self.f_notif_idx {
496 return None;
497 }
498
499 self.order_against_avail();
500
501 if self.raw.flags.desc_event_flags() != RingEventFlags::Desc {
502 return None;
503 }
504
505 Some(self.raw.desc)
506 }
507}
508
509pub struct PackedVq {
512 descr_ring: DescriptorRing,
515 drv_event: DrvNotif,
517 dev_event: DevNotif,
519 notif_ctrl: NotifCtrl,
521 size: u16,
524 index: u16,
527 last_next: Cell<EventSuppressDesc>,
528}
529
530impl Virtq for PackedVq {
533 fn enable_notifs(&mut self) {
534 self.drv_event.enable_notif();
535 }
536
537 fn disable_notifs(&mut self) {
538 self.drv_event.disable_notif();
539 }
540
541 fn try_recv(&mut self) -> Result<UsedBufferToken, VirtqError> {
542 self.descr_ring.try_recv()
543 }
544
545 fn dispatch_batch(
546 &mut self,
547 buffer_tkns: Vec<(AvailBufferToken, BufferType)>,
548 notif: bool,
549 ) -> Result<(), VirtqError> {
550 assert!(!buffer_tkns.is_empty());
552
553 let transfer_tkns = buffer_tkns.into_iter().map(|(buffer_tkn, buffer_type)| {
554 Self::transfer_token_from_buffer_token(buffer_tkn, buffer_type)
555 });
556
557 let next_idx = self.descr_ring.push_batch(transfer_tkns)?;
558
559 if notif {
560 self.drv_event.enable_specific(next_idx);
561 }
562
563 let range = self.last_next.get()..next_idx;
564 let notif_specific = self
565 .dev_event
566 .notif_specific()
567 .is_some_and(|idx| range.wrapping_contains(&idx));
568
569 if self.dev_event.is_notif() || notif_specific {
570 let notification_data = NotificationData::new()
571 .with_vq_notif_config_data(self.index)
572 .with_next_off(next_idx.desc_event_off())
573 .with_next_wrap(next_idx.desc_event_wrap());
574 self.notif_ctrl.notify_dev(notification_data);
575 self.last_next.set(next_idx);
576 }
577 Ok(())
578 }
579
580 fn dispatch_batch_await(
581 &mut self,
582 buffer_tkns: Vec<(AvailBufferToken, BufferType)>,
583 notif: bool,
584 ) -> Result<(), VirtqError> {
585 assert!(!buffer_tkns.is_empty());
587
588 let transfer_tkns = buffer_tkns.into_iter().map(|(buffer_tkn, buffer_type)| {
589 Self::transfer_token_from_buffer_token(buffer_tkn, buffer_type)
590 });
591
592 let next_idx = self.descr_ring.push_batch(transfer_tkns)?;
593
594 if notif {
595 self.drv_event.enable_specific(next_idx);
596 }
597
598 let range = self.last_next.get()..next_idx;
599 let notif_specific = self
600 .dev_event
601 .notif_specific()
602 .is_some_and(|idx| range.wrapping_contains(&idx));
603
604 if self.dev_event.is_notif() | notif_specific {
605 let notification_data = NotificationData::new()
606 .with_vq_notif_config_data(self.index)
607 .with_next_off(next_idx.desc_event_off())
608 .with_next_wrap(next_idx.desc_event_wrap());
609 self.notif_ctrl.notify_dev(notification_data);
610 self.last_next.set(next_idx);
611 }
612 Ok(())
613 }
614
615 fn dispatch(
616 &mut self,
617 buffer_tkn: AvailBufferToken,
618 notif: bool,
619 buffer_type: BufferType,
620 ) -> Result<(), VirtqError> {
621 let transfer_tkn = Self::transfer_token_from_buffer_token(buffer_tkn, buffer_type);
622 let next_idx = self.descr_ring.push(transfer_tkn)?;
623
624 if notif {
625 self.drv_event.enable_specific(next_idx);
626 }
627
628 let notif_specific = self
630 .dev_event
631 .notif_specific()
632 .map(EventSuppressDesc::into_bits)
633 == Some(self.last_next.get().into_bits());
634
635 if self.dev_event.is_notif() || notif_specific {
636 let notification_data = NotificationData::new()
637 .with_vq_notif_config_data(self.index)
638 .with_next_off(next_idx.desc_event_off())
639 .with_next_wrap(next_idx.desc_event_wrap());
640 self.notif_ctrl.notify_dev(notification_data);
641 self.last_next.set(next_idx);
642 }
643 Ok(())
644 }
645
646 fn index(&self) -> u16 {
647 self.index
648 }
649
650 fn size(&self) -> u16 {
651 self.size
652 }
653
654 fn has_used_buffers(&self) -> bool {
655 let desc = &self.descr_ring.ring[usize::from(self.descr_ring.poll_index.desc_event_off())];
656 self.descr_ring.is_marked_used(desc.flags)
657 }
658}
659
660impl VirtqPrivate for PackedVq {
661 type Descriptor = pvirtq::Desc;
662
663 fn create_indirect_ctrl(
664 buffer_tkn: &AvailBufferToken,
665 ) -> Result<Box<[Self::Descriptor]>, VirtqError> {
666 Ok(Self::descriptor_iter(buffer_tkn)?
667 .collect::<Vec<_>>()
668 .into_boxed_slice())
669 }
670}
671
672impl PackedVq {
673 #[allow(dead_code)]
674 pub(crate) fn new(
675 com_cfg: &mut ComCfg,
676 notif_cfg: &NotifCfg,
677 max_size: u16,
678 index: u16,
679 features: virtio::F,
680 ) -> Result<Self, VirtqError> {
681 if features.contains(virtio::F::IN_ORDER) {
689 info!("PackedVq has no support for VIRTIO_F_IN_ORDER. Aborting...");
690 return Err(VirtqError::FeatureNotSupported(virtio::F::IN_ORDER));
691 }
692
693 let mut vq_handler = com_cfg
695 .select_vq(index)
696 .ok_or(VirtqError::QueueNotExisting(index))?;
697
698 let vq_size = if (max_size == 0) | (max_size > 0x8000) {
703 return Err(VirtqError::QueueSizeNotAllowed(max_size));
704 } else {
705 vq_handler.set_vq_size(max_size)
706 };
707
708 let order_platform = features.contains(virtio::F::ORDER_PLATFORM);
709
710 let mut descr_ring = DescriptorRing::new(vq_size, order_platform);
711 let _mem_len = size_of::<pvirtq::EventSuppress>().align_up(BasePageSize::SIZE as usize);
713
714 let drv_event = Box::<pvirtq::EventSuppress, _>::new_zeroed_in(DeviceAlloc);
715 let dev_event = Box::<pvirtq::EventSuppress, _>::new_zeroed_in(DeviceAlloc);
716 let drv_event = unsafe { drv_event.assume_init() };
718 let dev_event = unsafe { dev_event.assume_init() };
719 let drv_event = Box::leak(drv_event);
720 let dev_event = Box::leak(dev_event);
721
722 vq_handler.set_ring_addr(DeviceAlloc.phys_addr_from(descr_ring.as_mut_ptr()));
724 vq_handler.set_drv_ctrl_addr(DeviceAlloc.phys_addr_from(drv_event));
726 vq_handler.set_dev_ctrl_addr(DeviceAlloc.phys_addr_from(dev_event));
727
728 let mut drv_event = DrvNotif {
729 f_notif_idx: false,
730 raw: drv_event,
731 };
732
733 let dev_event = DevNotif {
734 order_platform,
735 f_notif_idx: false,
736 raw: dev_event,
737 };
738
739 let mut notif_ctrl = NotifCtrl::new(notif_cfg.notification_location(&mut vq_handler));
740
741 if features.contains(virtio::F::NOTIFICATION_DATA) {
742 notif_ctrl.enable_notif_data();
743 }
744
745 if features.contains(virtio::F::EVENT_IDX) {
746 drv_event.f_notif_idx = true;
747 }
748
749 vq_handler.enable_queue();
750
751 info!("Created PackedVq: idx={index}, size={vq_size}");
752
753 Ok(PackedVq {
754 descr_ring,
755 drv_event,
756 dev_event,
757 notif_ctrl,
758 size: vq_size,
759 index,
760 last_next: Cell::default(),
761 })
762 }
763}