virtio/queues/split_queue.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2023.
4
5//! VirtIO Split Virtqueue implementation.
6//!
7//! This module contains an implementation of a Split Virtqueue, as defined in
8//! 2.6 Split Virtqueues of the [Virtual I/O Device (VIRTIO) Specification,
9//! Version
10//! 1.1](https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html).
11//! This implementation can be used in conjunction with the VirtIO transports
12//! defined in [`transports`](`super::super::transports`) and
13//! [`devices`](`super::super::devices`) to interact with VirtIO-compatible
14//! devices.
15
16use core::cell::Cell;
17use core::cmp;
18use core::marker::PhantomData;
19use core::ptr::NonNull;
20use core::slice;
21
22use kernel::utilities::cells::OptionalCell;
23use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
24use kernel::utilities::registers::{register_bitfields, InMemoryRegister};
25use kernel::ErrorCode;
26
27use super::super::queues::{Virtqueue, VirtqueueAddresses};
28use super::super::transports::VirtIOTransport;
29
30pub const DESCRIPTOR_ALIGNMENT: usize = 16;
31pub const AVAILABLE_RING_ALIGNMENT: usize = 2;
32pub const USED_RING_ALIGNMENT: usize = 4;
33
34register_bitfields![u16,
35 DescriptorFlags [
36 Next OFFSET(0) NUMBITS(1) [],
37 WriteOnly OFFSET(1) NUMBITS(1) [],
38 Indirect OFFSET(2) NUMBITS(1) []
39 ],
40 AvailableRingFlags [
41 NoInterrupt OFFSET(0) NUMBITS(1) []
42 ],
43 UsedRingFlags [
44 NoNotify OFFSET(0) NUMBITS(1) []
45 ],
46];
47
48// This is an unsafe workaround of an unsafe workaround.
49//
50// Unfortunately, the Rust core library defines defaults on arrays not in a
51// generic way, but only for arrays up to 32 elements. Hence, for arrays using a
52// const-generic argument for their length, `Default::<[$SOMETHING_NON_DEFAULT;
53// CONST_GENERIC_USIZE]>::default()` does not work in Rust (as of
54// 2022-11-26). Instead, we need to use this horrible and unsafe hack as
55// documented here, which initializes an array of `MaybeUninit`s and transmutes:
56// (https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element)
57//
58// However, Rust is also unable to transmute from a `MaybeUninit<T>` to a
59// generic `T`, even though the former is explicitly layout-compatible with the
60// latter. Hence, we have to use another hack described in the following
61// comment:
62// https://github.com/rust-lang/rust/issues/62875#issuecomment-513834029
63//
64// This function encapsulates all of this unsafe code and should be safe to use
65// until Rust adds support for constructing arrays over a const-generic length
66// from the contained types' default values.
67//
68// In large parts, this function is a verbatim copy of Rust's example for
69// element-wise array initialization using `MaybeUninit`:
70// https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element
71fn init_constgeneric_default_array<const N: usize, T: Default>() -> [T; N] {
72 // Create an uninitialized array of `MaybeUninit`. The `assume_init` is safe
73 // because the type we are claiming to have initialized here is a bunch of
74 // `MaybeUninit`s, which do not require initialization.
75 let mut uninit_arr: [core::mem::MaybeUninit<T>; N] =
76 unsafe { core::mem::MaybeUninit::uninit().assume_init() };
77
78 // Dropping a `MaybeUninit` does nothing, so if there is a panic during this
79 // loop, we have a memory leak, but there is no memory safety issue.
80 for elem in &mut uninit_arr[..] {
81 elem.write(T::default());
82 }
83
84 // Everything is initialized. We'd like to transmute the `[MaybeUnit<T>; N]`
85 // array into a `[T; N]`, but transmuting `MaybeUninit<T>` to `T` (where T
86 // is generic) is not (yet) supported. Hence we need to take a pointer to
87 // this array, cast it to the correct type, read the pointer back and forget
88 // the original `[MaybeUnit<T>; N]` array, as described here:
89 // https://github.com/rust-lang/rust/issues/62875#issuecomment-513834029
90 let uninit_arr_ptr: *mut [core::mem::MaybeUninit<T>; N] = &mut uninit_arr as *mut _;
91 let transmuted: [T; N] = unsafe { core::ptr::read(uninit_arr_ptr.cast::<[T; N]>()) };
92
93 // With the original value forgotten and new value recreated from its
94 // pointer, return it:
95 transmuted
96}
97
98/// A single Virtqueue descriptor.
99///
100/// Implements the memory layout of a single Virtqueue descriptor of a
101/// split-virtqueue, to be placed into the queue's descriptor table, as defined
102/// in section 2.6.5 of the spec.
103#[repr(C)]
104pub struct VirtqueueDescriptor {
105 /// Guest physical address of the buffer to share
106 addr: InMemoryRegister<u64>,
107 /// Length of the shared buffer
108 len: InMemoryRegister<u32>,
109 /// Descriptor flags
110 flags: InMemoryRegister<u16, DescriptorFlags::Register>,
111 /// Pointer to the next entry in the descriptor queue (if two
112 /// buffers are chained)
113 next: InMemoryRegister<u16>,
114}
115
116impl Default for VirtqueueDescriptor {
117 fn default() -> VirtqueueDescriptor {
118 VirtqueueDescriptor {
119 addr: InMemoryRegister::new(0),
120 len: InMemoryRegister::new(0),
121 flags: InMemoryRegister::new(0),
122 next: InMemoryRegister::new(0),
123 }
124 }
125}
126
127/// The Virtqueue descriptor table.
128///
129/// This table is provided to the VirtIO device (host) as a means to communicate
130/// information about shared buffers, maintained in the individual
131/// [`VirtqueueDescriptor`] elements. Elements in this table are referenced by
132/// the [`VirtqueueAvailableRing`] and [`VirtqueueUsedRing`] for exposing them
133/// to the VirtIO device in order, and receiving exposed ("used") buffers back
134/// from the device.
135///
136/// Multiple entries of the descriptor table can be chained in order to treat
137/// disjoint regions of memory as a single buffer through the
138/// `VirtqueueDescriptor::next` field, where the value of this field indexes
139/// into this table.
140#[repr(C, align(16))]
141pub struct VirtqueueDescriptors<const MAX_QUEUE_SIZE: usize>([VirtqueueDescriptor; MAX_QUEUE_SIZE]);
142
143impl<const MAX_QUEUE_SIZE: usize> Default for VirtqueueDescriptors<MAX_QUEUE_SIZE> {
144 fn default() -> Self {
145 VirtqueueDescriptors(init_constgeneric_default_array())
146 }
147}
148
149// This is required to be able to implement Default and hence to
150// initialize an entire array of default values with size specified by
151// a constant.
152#[repr(transparent)]
153pub struct VirtqueueAvailableElement(InMemoryRegister<u16>);
154
155/// The Virtqueue available ring.
156///
157/// This struct is exposed to the VirtIO device as a means to share buffers
158/// (pointed to by descriptors of the [`VirtqueueDescriptors`] descriptors
159/// table) with the VirtIO device (host). It avoids the need for explicit
160/// locking by using two distinct rings, each undirectionally exchanging
161/// information about used buffers. When a new buffer is placed into the
162/// available ring, the VirtIO driver (guest) must increment `idx` to the index
163/// where it would place the next available descriptor pointer in the ring
164/// field. After such an update, the queue must inform the device about this
165/// change through a call to [`VirtIOTransport::queue_notify`]. Given that
166/// volatile writes cannot be reordered with respect to each other, changes to
167/// the available ring are guaranteed to be visible to the VirtIO device (host).
168#[repr(C, align(2))]
169pub struct VirtqueueAvailableRing<const MAX_QUEUE_SIZE: usize> {
170 /// Virtqueue available ring flags.
171 flags: InMemoryRegister<u16, AvailableRingFlags::Register>,
172 /// Incrementing index, pointing to where the driver would put the next
173 /// descriptor entry in the ring (modulo the queue size).
174 ///
175 /// The driver must not decrement this field. There is no way to "unexpose"
176 /// buffers.
177 idx: InMemoryRegister<u16>,
178 /// Ring containing the shared buffers (indices into the
179 /// [`VirtqueueDescriptors`] descriptor table).
180 ring: [VirtqueueAvailableElement; MAX_QUEUE_SIZE],
181 /// "Used event" queue notification suppression mechanism.
182 ///
183 /// This field is only honored by the VirtIO device if the EventIdx feature
184 /// was negotiated.
185 ///
186 /// The driver can set this field to a target `idx` value of the
187 /// [`VirtqueueUsedRing`] to indicate to the device that notifications are
188 /// unnecessary until the device writes a buffer with the corresponding
189 /// index into the used ring.
190 used_event: InMemoryRegister<u16>,
191}
192
193impl Default for VirtqueueAvailableElement {
194 fn default() -> VirtqueueAvailableElement {
195 VirtqueueAvailableElement(InMemoryRegister::new(0))
196 }
197}
198
199impl<const MAX_QUEUE_SIZE: usize> Default for VirtqueueAvailableRing<MAX_QUEUE_SIZE> {
200 fn default() -> Self {
201 VirtqueueAvailableRing {
202 flags: InMemoryRegister::new(0),
203 idx: InMemoryRegister::new(0),
204 ring: init_constgeneric_default_array(),
205 used_event: InMemoryRegister::new(0),
206 }
207 }
208}
209
210/// The Virtqueue used ring.
211///
212/// This struct is exposed to the VirtIO device for the device to indicate which
213/// shared buffers (through the [`VirtqueueAvailableRing`] have been processed.
214/// It works similar to the available ring, but must never be written by the
215/// VirtIO driver (guest) after it has been shared with the device, and as long
216/// as the device is initialized.
217#[repr(C, align(4))]
218pub struct VirtqueueUsedRing<const MAX_QUEUE_SIZE: usize> {
219 /// Virtqueue used ring flags.
220 flags: InMemoryRegister<u16, UsedRingFlags::Register>,
221 /// Incrementing index, pointing to where the device would put the next
222 /// descriptor entry in the ring (modulo the queue size).
223 ///
224 /// The device must not decrement this field. There is no way to "take back"
225 /// buffers.
226 idx: InMemoryRegister<u16>,
227 /// Ring containing the used buffers (indices into the
228 /// [`VirtqueueDescriptors`] descriptor table).
229 ring: [VirtqueueUsedElement; MAX_QUEUE_SIZE],
230 /// "Available event" queue notification suppression mechanism.
231 ///
232 /// This field must only be honored by the VirtIO driver if the EventIdx
233 /// feature was negotiated.
234 ///
235 /// The device can set this field to a target `idx` value of the
236 /// [`VirtqueueAvailableRing`] to indicate to the driver that notifications
237 /// are unnecessary until the driver writes a buffer with the corresponding
238 /// index into the available ring.
239 avail_event: InMemoryRegister<u16>,
240}
241
242impl<const MAX_QUEUE_SIZE: usize> Default for VirtqueueUsedRing<MAX_QUEUE_SIZE> {
243 fn default() -> Self {
244 VirtqueueUsedRing {
245 flags: InMemoryRegister::new(0),
246 idx: InMemoryRegister::new(0),
247 ring: init_constgeneric_default_array(),
248 avail_event: InMemoryRegister::new(0),
249 }
250 }
251}
252
253/// A single element of the [`VirtqueueUsedRing`].
254#[repr(C)]
255pub struct VirtqueueUsedElement {
256 /// Index into the [`VirtqueueDescriptors`] descriptor table indicating the
257 /// head element of the returned descriptor chain.
258 id: InMemoryRegister<u32>,
259 /// Total length of the descriptor chain which was used by the device.
260 ///
261 /// Commonly this is used as a mechanism to communicate how much data the
262 /// device has written to a shared buffer.
263 len: InMemoryRegister<u32>,
264}
265
266impl Default for VirtqueueUsedElement {
267 fn default() -> VirtqueueUsedElement {
268 VirtqueueUsedElement {
269 id: InMemoryRegister::new(0),
270 len: InMemoryRegister::new(0),
271 }
272 }
273}
274
275/// A helper struct to manage the state of the Virtqueue available ring.
276///
277/// This struct reduces the complexity of the [`SplitVirtqueue`] implementation
278/// by encapsulating operations which depend on and modify the state of the
279/// driver-controlled available ring of the Virtqueue. It is essentially a
280/// glorified ring-buffer state machine, following the semantics as defined by
281/// VirtIO for the Virtqueue's available ring.
282struct AvailableRingHelper {
283 max_elements: Cell<usize>,
284 start: Cell<u16>,
285 end: Cell<u16>,
286 empty: Cell<bool>,
287}
288
289impl AvailableRingHelper {
290 pub fn new(max_elements: usize) -> AvailableRingHelper {
291 AvailableRingHelper {
292 max_elements: Cell::new(max_elements),
293 start: Cell::new(0),
294 end: Cell::new(0),
295 empty: Cell::new(true),
296 }
297 }
298
299 fn ring_wrapping_add(&self, a: u16, b: u16) -> u16 {
300 if self.max_elements.get() - (a as usize) - 1 < (b as usize) {
301 b - (self.max_elements.get() - a as usize) as u16
302 } else {
303 a + b
304 }
305 }
306
307 /// Reset the state of the available ring.
308 ///
309 /// This must be called before signaling to the device that the driver is
310 /// initialized. It takes the maximum queue elements as the `max_elements`
311 /// parameter, as negotiated with the device.
312 pub fn reset(&self, max_elements: usize) {
313 self.max_elements.set(max_elements);
314 self.start.set(0);
315 self.end.set(0);
316 self.empty.set(true);
317 }
318
319 /// Whether the available ring of the Virtqueue is empty.
320 pub fn is_empty(&self) -> bool {
321 self.empty.get()
322 }
323
324 /// Whether the available ring of the Virtqueue is full.
325 pub fn is_full(&self) -> bool {
326 !self.empty.get() && self.start.get() == self.end.get()
327 }
328
329 /// Try to insert an element into the Virtqueue available ring.
330 ///
331 /// If there is space in the Virtqueue's available ring, this increments the
332 /// internal state and returns the index of the element to be
333 /// written. Otherwise, it returns `None`.
334 pub fn insert(&self) -> Option<u16> {
335 if !self.is_full() {
336 let pos = self.end.get();
337 self.end.set(self.ring_wrapping_add(pos, 1));
338 self.empty.set(false);
339 Some(pos)
340 } else {
341 None
342 }
343 }
344
345 /// Try to remove an element from the Virtqueue available ring.
346 ///
347 /// If there is an element in the Virtqueue's available ring, this removes
348 /// it from its internal state and returns the index of that element.
349 pub fn pop(&self) -> Option<u16> {
350 if !self.is_empty() {
351 let pos = self.start.get();
352 self.start.set(self.ring_wrapping_add(pos, 1));
353 if self.start.get() == self.end.get() {
354 self.empty.set(true);
355 }
356 Some(pos)
357 } else {
358 None
359 }
360 }
361}
362
363/// Internal representation of a slice of memory passed held in the Virtqueue.
364///
365/// Because of Tock's architecture combined with Rust's reference lifetime
366/// rules, buffers are generally passed around as `&mut [u8]` slices with a
367/// `'static` lifetime. Thus, clients pass a mutable static reference into the
368/// Virtqueue, losing access. When the device has processed the provided buffer,
369/// access is restored by passing the slice back to the client.
370///
371/// However, clients may not wish to expose the full slice length to the
372/// device. They cannot simply subslice the slice, as that would mean that
373/// clients loose access to the "sliced off" portion of the slice. Instead,
374/// clients pass a seperate `length` parameter when inserting a buffer into a
375/// virtqueue, by means of the [`VirtqueueBuffer`] struct. This information is
376/// then written to the VirtIO descriptor.
377///
378/// Yet, to be able to reconstruct the entire buffer and hand it back to the
379/// client, information such as the original length must be recorded. We cannot
380/// retain the `&'static mut [u8]` in scope though, as the VirtIO device (host)
381/// writing to it would violate Rust's memory aliasing rules. Thus, we convert a
382/// slice into this struct (converting the slice into its raw parts) for
383/// safekeeping, until we reconstruct a byte-slice from it to hand it back to
384/// the client.
385///
386/// While we technically retain an identical pointer in the
387/// [`VirtqueueDescriptors`] descriptor table, we record it here nonetheless, as
388/// a method to sanity check internal buffer management consistency.
389struct SharedDescriptorBuffer<'b> {
390 ptr: NonNull<u8>,
391 len: usize,
392 _lt: PhantomData<&'b mut [u8]>,
393}
394
395impl<'b> SharedDescriptorBuffer<'b> {
396 pub fn from_slice(slice: &'b mut [u8]) -> SharedDescriptorBuffer<'b> {
397 SharedDescriptorBuffer {
398 ptr: NonNull::new(slice.as_mut_ptr()).unwrap(),
399 len: slice.len(),
400 _lt: PhantomData,
401 }
402 }
403
404 pub fn into_slice(self) -> &'b mut [u8] {
405 // SAFETY: This is guaranteed to be safe because this struct can only be
406 // using the `from_slice()` constructor, `ptr` and `len` cannot be
407 // modified after this struct is created, and this method consumes the
408 // struct.
409 unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
410 }
411}
412
413/// A slice of memory to be shared with a VirtIO device.
414///
415/// The [`VirtqueueBuffer`] allows to limit the portion of the passed slice to
416/// be shared with the device through the `len` field. Furthermore, the device
417/// can be asked to not write to the shared buffer by setting `device_writeable`
418/// to `false`.
419///
420/// The [`SplitVirtqueue`] does not actually enfore that a VirtIO device adheres
421/// to the `device_writeable` flag, although compliant devices should.
422pub struct VirtqueueBuffer<'b> {
423 pub buf: &'b mut [u8],
424 pub len: usize,
425 pub device_writeable: bool,
426}
427
428/// A VirtIO split Virtqueue.
429///
430/// For documentation on Virtqueues in general, please see the [`Virtqueue`
431/// trait documentation](Virtqueue).
432///
433/// A split Virtqueue is split into separate memory areas, namely:
434///
435/// - a **descriptor table** (VirtIO driver / guest writeable,
436/// [`VirtqueueDescriptors`])
437///
438/// - an **available ring** (VirtIO driver / guest writeable,
439/// [`VirtqueueAvailableRing`])
440///
441/// - a **used ring** (VirtIO device / host writeable, [`VirtqueueUsedRing`])
442///
443/// Each of these areas must be located physically-contiguous in guest-memory
444/// and have different alignment constraints.
445///
446/// This is in constrast to _packed Virtqueues_, which use memory regions that
447/// are read and written by both the VirtIO device (host) and VirtIO driver
448/// (guest).
449pub struct SplitVirtqueue<'a, 'b, const MAX_QUEUE_SIZE: usize> {
450 descriptors: &'a mut VirtqueueDescriptors<MAX_QUEUE_SIZE>,
451 available_ring: &'a mut VirtqueueAvailableRing<MAX_QUEUE_SIZE>,
452 used_ring: &'a mut VirtqueueUsedRing<MAX_QUEUE_SIZE>,
453
454 available_ring_state: AvailableRingHelper,
455 last_used_idx: Cell<u16>,
456
457 transport: OptionalCell<&'a dyn VirtIOTransport>,
458
459 initialized: Cell<bool>,
460 queue_number: Cell<u32>,
461 max_elements: Cell<usize>,
462
463 descriptor_buffers: [OptionalCell<SharedDescriptorBuffer<'b>>; MAX_QUEUE_SIZE],
464
465 client: OptionalCell<&'a dyn SplitVirtqueueClient<'b>>,
466 used_callbacks_enabled: Cell<bool>,
467}
468
469impl<'a, 'b, const MAX_QUEUE_SIZE: usize> SplitVirtqueue<'a, 'b, MAX_QUEUE_SIZE> {
470 pub fn new(
471 descriptors: &'a mut VirtqueueDescriptors<MAX_QUEUE_SIZE>,
472 available_ring: &'a mut VirtqueueAvailableRing<MAX_QUEUE_SIZE>,
473 used_ring: &'a mut VirtqueueUsedRing<MAX_QUEUE_SIZE>,
474 ) -> Self {
475 assert!(core::ptr::from_ref(descriptors) as usize % DESCRIPTOR_ALIGNMENT == 0);
476 assert!(core::ptr::from_ref(available_ring) as usize % AVAILABLE_RING_ALIGNMENT == 0);
477 assert!(core::ptr::from_ref(used_ring) as usize % USED_RING_ALIGNMENT == 0);
478
479 SplitVirtqueue {
480 descriptors,
481 available_ring,
482 used_ring,
483
484 available_ring_state: AvailableRingHelper::new(MAX_QUEUE_SIZE),
485 last_used_idx: Cell::new(0),
486
487 transport: OptionalCell::empty(),
488
489 initialized: Cell::new(false),
490 queue_number: Cell::new(0),
491 max_elements: Cell::new(MAX_QUEUE_SIZE),
492
493 descriptor_buffers: init_constgeneric_default_array(),
494
495 client: OptionalCell::empty(),
496 used_callbacks_enabled: Cell::new(false),
497 }
498 }
499
500 /// Set the [`SplitVirtqueueClient`].
501 pub fn set_client(&self, client: &'a dyn SplitVirtqueueClient<'b>) {
502 self.client.set(client);
503 }
504
505 /// Set the underlying [`VirtIOTransport`]. This must be done prior to
506 /// initialization.
507 pub fn set_transport(&self, transport: &'a dyn VirtIOTransport) {
508 assert!(!self.initialized.get());
509 self.transport.set(transport);
510 }
511
512 /// Get the queue number associated with this Virtqueue.
513 ///
514 /// Prior to initialization the SplitVirtqueue does not have an associated
515 /// queue number and will return `None`.
516 pub fn queue_number(&self) -> Option<u32> {
517 if self.initialized.get() {
518 Some(self.queue_number.get())
519 } else {
520 None
521 }
522 }
523
524 /// Get the number of free descriptor slots in the descriptor table.
525 ///
526 /// This takes into account the negotiated maximum queue length.
527 pub fn free_descriptor_count(&self) -> usize {
528 assert!(self.initialized.get());
529 self.descriptor_buffers
530 .iter()
531 .take(self.max_elements.get())
532 .fold(0, |count, descbuf_entry| {
533 if descbuf_entry.is_none() {
534 count + 1
535 } else {
536 count
537 }
538 })
539 }
540
541 /// Get the number of (unprocessed) descriptor chains in the Virtqueue's
542 /// used ring.
543 pub fn used_descriptor_chains_count(&self) -> usize {
544 let pending_chains = self
545 .used_ring
546 .idx
547 .get()
548 .wrapping_sub(self.last_used_idx.get());
549
550 // If we ever have more than max_elements pending descriptors,
551 // the used ring increased too fast and has overwritten data
552 assert!(pending_chains as usize <= self.max_elements.get());
553
554 pending_chains as usize
555 }
556
557 /// Remove an element from the Virtqueue's used ring.q
558 ///
559 /// If `self.last_used_idx.get() == self.used_ring.idx.get()` (e.g. we don't
560 /// have an unprocessed used buffer chain) this will return
561 /// `None`. Otherwise it will return the remove ring element's index, as
562 /// well as the number of processed bytes as reported by the VirtIO device.
563 ///
564 /// This will update `self.last_used_idx`.
565 ///
566 /// The caller is responsible for keeping the available ring in sync,
567 /// freeing one entry if a used buffer was removed through this method.
568 fn remove_used_chain(&self) -> Option<(usize, usize)> {
569 assert!(self.initialized.get());
570
571 let pending_chains = self.used_descriptor_chains_count();
572
573 if pending_chains > 0 {
574 let last_used_idx = self.last_used_idx.get();
575
576 // Remove the element one below the index (as 0 indicates
577 // _no_ buffer has been written), hence the index points
578 // to the next element to be written
579 let ring_pos = (last_used_idx as usize) % self.max_elements.get();
580 let chain_top_idx = self.used_ring.ring[ring_pos].id.get();
581 let written_len = self.used_ring.ring[ring_pos].len.get();
582
583 // Increment our local idx counter
584 self.last_used_idx.set(last_used_idx.wrapping_add(1));
585
586 Some((chain_top_idx as usize, written_len as usize))
587 } else {
588 None
589 }
590 }
591
592 /// Add an element to the available queue.
593 ///
594 /// Returns either the inserted ring index or `None` if the Virtqueue's
595 /// available ring is fully occupied.
596 ///
597 /// This will update the available ring's `idx` field.
598 ///
599 /// The caller is responsible for notifying the device about any inserted
600 /// available buffers.
601 fn add_available_descriptor(&self, descriptor_chain_head: usize) -> Option<usize> {
602 assert!(self.initialized.get());
603
604 if let Some(element_pos) = self.available_ring_state.insert() {
605 // Write the element
606 self.available_ring.ring[element_pos as usize]
607 .0
608 .set(descriptor_chain_head as u16);
609
610 // TODO: Perform a suitable memory barrier using a method exposed by
611 // the transport. For now, we don't negotiate
612 // VIRTIO_F_ORDER_PLATFORM, which means that any device which
613 // requires proper memory barriers (read: not implemented in
614 // software, like QEMU) should refuse operation. We use volatile
615 // memory accesses, so read/write reordering by the compiler is not
616 // an issue.
617
618 // Update the idx
619 self.available_ring
620 .idx
621 .set(self.available_ring.idx.get().wrapping_add(1));
622
623 Some(element_pos as usize)
624 } else {
625 None
626 }
627 }
628
629 fn add_descriptor_chain(
630 &self,
631 buffer_chain: &mut [Option<VirtqueueBuffer<'b>>],
632 ) -> Result<usize, ErrorCode> {
633 assert!(self.initialized.get());
634
635 // Get size of actual chain, until the first None
636 let queue_length = buffer_chain
637 .iter()
638 .take_while(|elem| elem.is_some())
639 .count();
640
641 // Make sure we have sufficient space available
642 //
643 // This takes into account the negotiated max size and will
644 // only list free iterators within that range
645 if self.free_descriptor_count() < queue_length {
646 return Err(ErrorCode::NOMEM);
647 }
648
649 // Walk over the descriptor table & buffer chain in parallel,
650 // inserting where empty
651 //
652 // We don't need to do any bounds checking here, if we run
653 // over the boundary it's safe to panic as something is
654 // seriously wrong with `free_descriptor_count`
655 let mut i = 0;
656 let mut previous_descriptor: Option<usize> = None;
657 let mut head = None;
658 let queuebuf_iter = buffer_chain.iter_mut().peekable();
659 for queuebuf in queuebuf_iter.take_while(|queuebuf| queuebuf.is_some()) {
660 // Take the queuebuf out of the caller array
661 let taken_queuebuf = queuebuf.take().expect("queuebuf is None");
662
663 // Sanity check the buffer: the subslice length may never
664 // exceed the slice length
665 assert!(taken_queuebuf.buf.len() >= taken_queuebuf.len);
666
667 while self.descriptor_buffers[i].is_some() {
668 i += 1;
669
670 // We should never run over the end, as we should have
671 // sufficient free descriptors
672 assert!(i < self.descriptor_buffers.len());
673 }
674
675 // Alright, we found a slot to insert the descriptor
676 //
677 // Check if it's the first one and store it's index as head
678 if head.is_none() {
679 head = Some(i);
680 }
681
682 // Write out the descriptor
683 let desc = &self.descriptors.0[i];
684 desc.len.set(taken_queuebuf.len as u32);
685 assert!(desc.len.get() > 0);
686 desc.addr.set(taken_queuebuf.buf.as_ptr() as u64);
687 desc.flags.write(if taken_queuebuf.device_writeable {
688 DescriptorFlags::WriteOnly::SET
689 } else {
690 DescriptorFlags::WriteOnly::CLEAR
691 });
692
693 // Now that we know our descriptor position, check whether
694 // we must chain ourself to a previous descriptor
695 if let Some(prev_index) = previous_descriptor {
696 self.descriptors.0[prev_index]
697 .flags
698 .modify(DescriptorFlags::Next::SET);
699 self.descriptors.0[prev_index].next.set(i as u16);
700 }
701
702 // Finally, store the full slice for reference. We don't store a
703 // proper Rust slice reference, as this would violate aliasing
704 // requirements: while the buffer is in the chain, it may be written
705 // by the VirtIO device.
706 //
707 // This can be changed to something slightly more elegant, once the
708 // NonNull functions around slices have been stabilized:
709 // https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.slice_from_raw_parts
710 self.descriptor_buffers[i]
711 .replace(SharedDescriptorBuffer::from_slice(taken_queuebuf.buf));
712
713 // Set ourself as the previous descriptor, as we know the position
714 // of `next` only in the next loop iteration.
715 previous_descriptor = Some(i);
716
717 // Increment the counter to not check the current
718 // descriptor entry again
719 i += 1;
720 }
721
722 Ok(head.expect("No head added to the descriptor table"))
723 }
724
725 fn remove_descriptor_chain(
726 &self,
727 top_descriptor_index: usize,
728 ) -> [Option<VirtqueueBuffer<'b>>; MAX_QUEUE_SIZE] {
729 assert!(self.initialized.get());
730
731 let mut res: [Option<VirtqueueBuffer<'b>>; MAX_QUEUE_SIZE] =
732 init_constgeneric_default_array();
733
734 let mut i = 0;
735 let mut next_index: Option<usize> = Some(top_descriptor_index);
736
737 while let Some(current_index) = next_index {
738 // Get a reference over the current descriptor
739 let current_desc = &self.descriptors.0[current_index];
740
741 // Check whether we have a chained descriptor and store that in next_index
742 if current_desc.flags.is_set(DescriptorFlags::Next) {
743 next_index = Some(current_desc.next.get() as usize);
744 } else {
745 next_index = None;
746 }
747
748 // Recover the slice originally associated with this
749 // descriptor & delete it from the buffers array
750 //
751 // The caller may have provided us a larger Rust slice,
752 // but indicated to only provide a subslice to VirtIO,
753 // hence we'll use the stored original slice and also
754 // return the subslice length
755 let supplied_slice = self.descriptor_buffers[current_index]
756 .take()
757 .expect("Virtqueue descriptors and slices out of sync")
758 .into_slice();
759 assert!(supplied_slice.as_mut_ptr() as u64 == current_desc.addr.get());
760
761 // Reconstruct the input VirtqueueBuffer to hand it back
762 res[i] = Some(VirtqueueBuffer {
763 buf: supplied_slice,
764 len: current_desc.len.get() as usize,
765 device_writeable: current_desc.flags.is_set(DescriptorFlags::WriteOnly),
766 });
767
768 // Zero the descriptor
769 current_desc.addr.set(0);
770 current_desc.len.set(0);
771 current_desc.flags.set(0);
772 current_desc.next.set(0);
773
774 // Increment the loop iterator
775 i += 1;
776 }
777
778 res
779 }
780
781 /// Provide a single chain of buffers to the device.
782 ///
783 /// This method will iterate over the passed slice until it encounters the
784 /// first `None`. It will first validate that the number of buffers can be
785 /// inserted into its descriptor table, and if not return
786 /// `Err(ErrorCode::NOMEM)`. If sufficient space is available, it takes the
787 /// passed buffers out of the provided `Option`s until encountering the
788 /// first `None` and shares this buffer chain with the device.
789 ///
790 /// When the device has finished processing the passed buffer chain, it is
791 /// returned to the client either through the
792 /// [`SplitVirtqueueClient::buffer_chain_ready`] callback, or can be
793 /// retrieved through the [`SplitVirtqueue::pop_used_buffer_chain`] method.
794 pub fn provide_buffer_chain(
795 &self,
796 buffer_chain: &mut [Option<VirtqueueBuffer<'b>>],
797 ) -> Result<(), ErrorCode> {
798 assert!(self.initialized.get());
799
800 // Try to add the chain into the descriptor array
801 let descriptor_chain_head = self.add_descriptor_chain(buffer_chain)?;
802
803 // Now make it available to the device. If there was sufficient space
804 // available to add the chain's descriptors (of which there may be
805 // multiple), there should also be sufficient space in the available
806 // ring (where a multi-descriptor chain will occupy only one elements).
807 self.add_available_descriptor(descriptor_chain_head)
808 .expect("Insufficient space in available ring");
809
810 // Notify the queue. This must not fail, given that the SplitVirtqueue
811 // requires a transport to be set prior to initialization.
812 self.transport
813 .map(|t| t.queue_notify(self.queue_number.get()))
814 .unwrap();
815
816 Ok(())
817 }
818
819 /// Attempt to take a buffer chain out of the Virtqueue used ring.
820 ///
821 /// Returns `None` if the used ring is empty.
822 pub fn pop_used_buffer_chain(
823 &self,
824 ) -> Option<([Option<VirtqueueBuffer<'b>>; MAX_QUEUE_SIZE], usize)> {
825 assert!(self.initialized.get());
826
827 self.remove_used_chain()
828 .map(|(descriptor_idx, bytes_used)| {
829 // Get the descriptor chain
830 let chain = self.remove_descriptor_chain(descriptor_idx);
831
832 // Remove the first entry of the available ring, since we
833 // got a single buffer back and can therefore make another
834 // buffer available to the device without risking an
835 // overflow of the used ring
836 self.available_ring_state.pop();
837
838 (chain, bytes_used)
839 })
840 }
841
842 /// Disable callback delivery for the
843 /// [`SplitVirtqueueClient::buffer_chain_ready`] method on the registered
844 /// client.
845 pub fn enable_used_callbacks(&self) {
846 self.used_callbacks_enabled.set(true);
847 }
848
849 /// Enable callback delivery for the
850 /// [`SplitVirtqueueClient::buffer_chain_ready`] method on the registered
851 /// client.
852 ///
853 /// Callback delivery is enabled by default. If this is not desired, call
854 /// this method prior to registering a client.
855 pub fn disable_used_callbacks(&self) {
856 self.used_callbacks_enabled.set(false);
857 }
858}
859
860impl<const MAX_QUEUE_SIZE: usize> Virtqueue for SplitVirtqueue<'_, '_, MAX_QUEUE_SIZE> {
861 fn used_interrupt(&self) {
862 assert!(self.initialized.get());
863 // A buffer MAY have been put into the used in by the device
864 //
865 // Try to extract all pending used buffers and return them to
866 // the clients via callbacks
867
868 while self.used_callbacks_enabled.get() {
869 if let Some((mut chain, bytes_used)) = self.pop_used_buffer_chain() {
870 self.client.map(move |client| {
871 client.buffer_chain_ready(self.queue_number.get(), chain.as_mut(), bytes_used)
872 });
873 } else {
874 break;
875 }
876 }
877 }
878
879 fn physical_addresses(&self) -> VirtqueueAddresses {
880 VirtqueueAddresses {
881 descriptor_area: core::ptr::from_ref(self.descriptors) as u64,
882 driver_area: core::ptr::from_ref(self.available_ring) as u64,
883 device_area: core::ptr::from_ref(self.used_ring) as u64,
884 }
885 }
886
887 fn negotiate_queue_size(&self, max_elements: usize) -> usize {
888 assert!(!self.initialized.get());
889 let negotiated = cmp::min(MAX_QUEUE_SIZE, max_elements);
890 self.max_elements.set(negotiated);
891 self.available_ring_state.reset(negotiated);
892 negotiated
893 }
894
895 fn initialize(&self, queue_number: u32, _queue_elements: usize) {
896 assert!(!self.initialized.get());
897
898 // The transport must be set prior to initialization:
899 assert!(self.transport.is_some());
900
901 // TODO: Zero the queue
902 //
903 // For now we assume all passed queue buffers are already
904 // zeroed
905
906 self.queue_number.set(queue_number);
907 self.initialized.set(true);
908 }
909}
910
911pub trait SplitVirtqueueClient<'b> {
912 fn buffer_chain_ready(
913 &self,
914 queue_number: u32,
915 buffer_chain: &mut [Option<VirtqueueBuffer<'b>>],
916 bytes_used: usize,
917 );
918}