Skip to main content

hermit/drivers/virtio/virtqueue/
packed.rs

1//! `pvirtq` infrastructure.
2//!
3//! The main type of this module is [`PackedVq`].
4//!
5//! For details on packed virtqueues, see [Packed Virtqueues].
6//! For details on the Rust definitions, see [`virtio::pvirtq`].
7//!
8//! [Packed Virtqueues]: https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-720008
9
10use 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
59/// Structure which allows to control raw ring and operate easily on it
60struct DescriptorRing {
61	ring: Box<[pvirtq::Desc], DeviceAlloc>,
62	tkn_ref_ring: Box<[Option<TransferToken<pvirtq::Desc>>]>,
63
64	// Controlling variables for the ring
65	//
66	/// where to insert available descriptors next
67	/// See Virtio specification v1.1. - 2.7.1
68	write_index: EventSuppressDesc,
69	/// How much descriptors can be inserted
70	capacity: u16,
71	/// Where to expect the next used descriptor by the device
72	///
73	/// See Virtio specification v1.1. - 2.7.1
74	poll_index: EventSuppressDesc,
75	/// This allocates available descriptors.
76	indexes: IndexAlloc,
77	/// Whether `VIRTIO_F_ORDER_PLATFORM` was negotiated, which decides
78	/// whether the barriers have to reach beyond the CPUs.
79	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		// `Box` is not Clone, so neither is `None::<Box<_>>`. Hence, we need to produce `None`s with a closure.
87		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	/// Polls poll index and sets the state of any finished TransferTokens.
110	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		// Catch empty push, in order to allow zero initialized first_ctrl_settings struct
125		// which will be overwritten in the first iteration of the for-loop
126
127		let mut tkn_iterator = tkn_lst.into_iter();
128		let Some(first_tkn) = tkn_iterator.next() else {
129			// Empty batches are an error
130			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		// Push the remaining tokens (if any)
138		for tkn in tkn_iterator {
139			ctrl.make_avail(tkn);
140		}
141
142		// Manually make the first buffer available lastly
143		//
144		// Providing the first buffer in the list manually
145		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		// create an counter that wrappes to the first element
168		// after reaching a the end of the ring
169		let mut ctrl = self.get_write_ctrler()?;
170
171		// Importance here is:
172		// * distinguish between Indirect and direct buffers
173		// * make them available in the right order (the first descriptor last) (VIRTIO Spec. v1.2 section 2.8.6)
174
175		// The buffer uses indirect descriptors if the ctrl_desc field is Some.
176		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	/// Returns an initialized write controller in order
192	/// to write the queue correctly.
193	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	/// Returns an initialized read controller in order
207	/// to read the queue correctly.
208	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		// provide reference, in order to let TransferToken know upon finish.
225		self.tkn_ref_ring[usize::from(buff_id)] = Some(raw_tkn);
226		// The driver performs a suitable memory barrier to ensure the device sees the updated descriptor table and available ring before the next step.
227		// See Virtio specification v1.1. - 2.7.21
228		super::virtio_mem_barrier(BarrierType::Write, self.order_platform);
229		self.ring[usize::from(start)].flags = first_flags;
230	}
231
232	/// Returns the [DescF] with the avail and used flags set in accordance
233	/// with the VIRTIO specification v1.2 - 2.8.1 (i.e. avail flag set to match
234	/// the driver wrap counter and the used flag set to NOT match the wrap counter).
235	///
236	/// This function is defined on the whole ring rather than only the
237	/// wrap counter to ensure that it is not called on the incorrect
238	/// wrap counter (i.e. device wrap counter) by accident.
239	///
240	/// A copy of the flag is taken instead of a mutable reference
241	/// for the cases in which the modification of the flag needs to be
242	/// deferred (e.g. patched dispatches, chained buffers).
243	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	/// Checks the avail and used flags to see if the descriptor is marked
251	/// as used by the device in accordance with the
252	/// VIRTIO specification v1.2 - 2.8.1 (i.e. they match the device wrap counter)
253	///
254	/// This function is defined on the whole ring rather than only the
255	/// wrap counter to ensure that it is not called on the incorrect
256	/// wrap counter (i.e. driver wrap counter) by accident.
257	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	/// Poll index of the ring at init of ReadCtrl
268	position: u16,
269	modulo: u16,
270
271	desc_ring: &'a mut DescriptorRing,
272}
273
274impl ReadCtrl<'_> {
275	/// Polls the ring for a new finished buffer. If buffer is marked as finished, takes care of
276	/// updating the queue and returns the respective TransferToken.
277	fn poll_next(&mut self) -> Option<(TransferToken<pvirtq::Desc>, u32)> {
278		// Check if descriptor has been marked used.
279		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		// Seeing the used mark does not yet permit reading the rest of the
285		// descriptor: the device writes the buffer id and the written length
286		// before it marks the descriptor, but without a barrier this side may
287		// still observe the older values.
288		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		// Retrieve if any has been written to the queue. If this is the case, we calculate the overall length
299		// This is necessary in order to provide the drivers with the correct access, to usable data.
300		//
301		// According to the standard the device signals solely via the first written descriptor if anything has been written to
302		// the write descriptors of a buffer.
303		// See Virtio specification v1.1. - 2.7.4
304		//                                - 2.7.5
305		//                                - 2.7.6
306		// let mut write_len = if self.desc_ring.ring[self.position].flags & DescrFlags::VIRTQ_DESC_F_WRITE == DescrFlags::VIRTQ_DESC_F_WRITE {
307		//      self.desc_ring.ring[self.position].len
308		//  } else {
309		//      0
310		//  };
311		//
312		// INFO:
313		// Due to the behavior of the currently used devices and the virtio code from the linux kernel, we assume, that device do NOT set this
314		// flag correctly upon writes. Hence we omit it, in order to receive data.
315
316		// We need to read the written length before advancing the position.
317		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		// Increment capacity as we have one more free now!
345		assert!(self.desc_ring.capacity <= u16::try_from(self.desc_ring.ring.len()).unwrap());
346		self.desc_ring.capacity += 1;
347	}
348}
349
350/// Convenient struct that allows to conveniently write descriptors into the queue.
351/// The struct takes care of updating the state of the queue correctly and to write
352/// the correct flags.
353struct WriteCtrl<'a> {
354	/// Where did the write of the buffer start in the descriptor ring
355	/// This is important, as we must make this descriptor available
356	/// lastly.
357	start: u16,
358	/// Where to write next. This should always be equal to the Rings
359	/// write_next field.
360	position: u16,
361	modulo: u16,
362	/// The [pvirtq::Desc::flags] value for the first descriptor, the write of which is deferred.
363	first_flags: DescF,
364	/// Buff ID of this write
365	buff_id: u16,
366
367	desc_ring: &'a mut DescriptorRing,
368}
369
370impl WriteCtrl<'_> {
371	/// **This function MUST only be used within the WriteCtrl.write_desc() function!**
372	///
373	/// Incrementing index by one. The index wrappes around to zero when
374	/// reaching (modulo -1).
375	///
376	/// Also takes care of wrapping the wrap counter of the associated
377	/// DescriptorRing.
378	fn incrmt(&mut self) {
379		// Firstly check if we are at all allowed to write a descriptor
380		assert!(self.desc_ring.capacity != 0);
381		self.desc_ring.capacity -= 1;
382
383		let mut desc = self.desc_ring.write_index;
384
385		// check if increment wrapped around end of ring
386		// then also wrap the wrap counter.
387		if self.position + 1 == self.modulo {
388			let wrap = desc.desc_event_wrap() ^ 1;
389			desc.set_desc_event_wrap(wrap);
390		}
391
392		// Also update the write_index
393		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	/// Completes the descriptor flags and id, and writes into the queue at the correct position.
402	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			// We save what the flags value for the first descriptor will be to be able
406			// to write it later when all the other descriptors are written (so that
407			// the device does not see an incomplete chain).
408			self.first_flags = self.desc_ring.to_marked_avail(incomplete_desc.flags);
409		} else {
410			// Set avail and used according to the current wrap counter.
411			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		// We fail if one wants to make a buffer available without inserting one element!
419		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
425/// A type in order to implement the correct functionality upon
426/// the `EventSuppr` structure for driver notifications settings.
427/// The Driver Event Suppression structure is read-only by the device
428/// and controls the used buffer notifications sent by the device to the driver.
429struct DrvNotif {
430	/// Indicates if VIRTIO_F_RING_EVENT_IDX has been negotiated
431	f_notif_idx: bool,
432	/// Actual structure to read from, if device wants notifs
433	raw: &'static mut pvirtq::EventSuppress,
434}
435
436/// A type in order to implement the correct functionality upon
437/// the `EventSuppr` structure for device notifications settings.
438/// The Device Event Suppression structure is read-only by the driver
439/// and controls the available buffer notifica- tions sent by the driver to the device.
440struct DevNotif {
441	/// Indicates if VIRTIO_F_RING_EVENT_IDX has been negotiated
442	f_notif_idx: bool,
443	/// Actual structure to read from, if device wants notifs
444	raw: &'static mut pvirtq::EventSuppress,
445	/// Whether `VIRTIO_F_ORDER_PLATFORM` was negotiated.
446	order_platform: bool,
447}
448
449impl DrvNotif {
450	/// Enables notifications by unsetting the LSB.
451	/// See Virito specification v1.1. - 2.7.10
452	fn enable_notif(&mut self) {
453		self.raw.flags = EventSuppressFlags::new().with_desc_event_flags(RingEventFlags::Enable);
454	}
455
456	/// Disables notifications by setting the LSB.
457	/// See Virtio specification v1.1. - 2.7.10
458	fn disable_notif(&mut self) {
459		self.raw.flags = EventSuppressFlags::new().with_desc_event_flags(RingEventFlags::Disable);
460	}
461
462	/// Enables a notification by the device for a specific descriptor.
463	fn enable_specific(&mut self, desc: EventSuppressDesc) {
464		// Check if VIRTIO_F_RING_EVENT_IDX has been negotiated
465		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	/// Enables the notification capability for a specific buffer.
474	#[expect(dead_code)]
475	pub fn enable_notif_specific(&mut self) {
476		self.f_notif_idx = true;
477	}
478
479	/// Orders a descriptor that was just made available against the reads
480	/// below.
481	#[inline]
482	fn order_against_avail(&self) {
483		super::virtio_mem_barrier(BarrierType::General, self.order_platform);
484	}
485
486	/// Reads notification bit (i.e. LSB) and returns value.
487	/// If notifications are enabled returns true, else false.
488	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
509/// Packed virtqueue which provides the functionilaty as described in the
510/// virtio specification v1.1. - 2.7
511pub struct PackedVq {
512	/// Ring which allows easy access to the raw ring structure of the
513	/// specification
514	descr_ring: DescriptorRing,
515	/// Allows to tell the device if notifications are wanted
516	drv_event: DrvNotif,
517	/// Allows to check, if the device wants a notification
518	dev_event: DevNotif,
519	/// Actually notify device about avail buffers
520	notif_ctrl: NotifCtrl,
521	/// The size of the queue, equals the number of descriptors which can
522	/// be used
523	size: u16,
524	/// The virtqueues index. This identifies the virtqueue to the
525	/// device and is unique on a per device basis.
526	index: u16,
527	last_next: Cell<EventSuppressDesc>,
528}
529
530// Public interface of PackedVq
531// This interface is also public in order to allow people to use the PackedVq directly!
532impl 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		// Zero transfers are not allowed
551		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		// Zero transfers are not allowed
586		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		// FIXME: impl PartialEq for EventSuppressDesc in virtio-spec instead of converting into bits.
629		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		// Currently we do not have support for in order use.
682		// This steems from the fact, that the packedVq ReadCtrl currently is not
683		// able to derive other finished transfer from a used-buffer notification.
684		// In order to allow this, the queue MUST track the sequence in which
685		// TransferTokens are inserted into the queue. Furthermore the Queue should
686		// carry a feature u64 in order to check which features are used currently
687		// and adjust its ReadCtrl accordingly.
688		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		// Get a handler to the queues configuration area.
694		let mut vq_handler = com_cfg
695			.select_vq(index)
696			.ok_or(VirtqError::QueueNotExisting(index))?;
697
698		// Must catch zero size as it is not allowed for packed queues.
699		// Must catch size larger 0x8000 (2^15) as it is not allowed for packed queues.
700		//
701		// See Virtio specification v1.1. - 4.1.4.3.2
702		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		// Allocate heap memory via a vec, leak and cast
712		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		// TODO: make this safe using zerocopy
717		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		// Provide memory areas of the queues data structures to the device
723		vq_handler.set_ring_addr(DeviceAlloc.phys_addr_from(descr_ring.as_mut_ptr()));
724		// As usize is safe here, as the *mut EventSuppr raw pointer is a thin pointer of size usize
725		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}