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.
422#[derive(Debug)]
423pub struct VirtqueueBuffer<'b> {
424    pub buf: &'b mut [u8],
425    pub len: usize,
426    pub device_writeable: bool,
427}
428
429/// A VirtIO split Virtqueue.
430///
431/// For documentation on Virtqueues in general, please see the [`Virtqueue`
432/// trait documentation](Virtqueue).
433///
434/// A split Virtqueue is split into separate memory areas, namely:
435///
436/// - a **descriptor table** (VirtIO driver / guest writeable,
437///   [`VirtqueueDescriptors`])
438///
439/// - an **available ring** (VirtIO driver / guest writeable,
440///   [`VirtqueueAvailableRing`])
441///
442/// - a **used ring** (VirtIO device / host writeable, [`VirtqueueUsedRing`])
443///
444/// Each of these areas must be located physically-contiguous in guest-memory
445/// and have different alignment constraints.
446///
447/// This is in constrast to _packed Virtqueues_, which use memory regions that
448/// are read and written by both the VirtIO device (host) and VirtIO driver
449/// (guest).
450pub struct SplitVirtqueue<'a, 'b, const MAX_QUEUE_SIZE: usize> {
451    descriptors: &'a mut VirtqueueDescriptors<MAX_QUEUE_SIZE>,
452    available_ring: &'a mut VirtqueueAvailableRing<MAX_QUEUE_SIZE>,
453    used_ring: &'a mut VirtqueueUsedRing<MAX_QUEUE_SIZE>,
454
455    available_ring_state: AvailableRingHelper,
456    last_used_idx: Cell<u16>,
457
458    transport: OptionalCell<&'a dyn VirtIOTransport>,
459
460    initialized: Cell<bool>,
461    queue_number: Cell<u32>,
462    max_elements: Cell<usize>,
463
464    descriptor_buffers: [OptionalCell<SharedDescriptorBuffer<'b>>; MAX_QUEUE_SIZE],
465
466    client: OptionalCell<&'a dyn SplitVirtqueueClient<'b>>,
467    used_callbacks_enabled: Cell<bool>,
468}
469
470impl<'a, 'b, const MAX_QUEUE_SIZE: usize> SplitVirtqueue<'a, 'b, MAX_QUEUE_SIZE> {
471    pub fn new(
472        descriptors: &'a mut VirtqueueDescriptors<MAX_QUEUE_SIZE>,
473        available_ring: &'a mut VirtqueueAvailableRing<MAX_QUEUE_SIZE>,
474        used_ring: &'a mut VirtqueueUsedRing<MAX_QUEUE_SIZE>,
475    ) -> Self {
476        assert!(core::ptr::from_ref(descriptors) as usize % DESCRIPTOR_ALIGNMENT == 0);
477        assert!(core::ptr::from_ref(available_ring) as usize % AVAILABLE_RING_ALIGNMENT == 0);
478        assert!(core::ptr::from_ref(used_ring) as usize % USED_RING_ALIGNMENT == 0);
479
480        SplitVirtqueue {
481            descriptors,
482            available_ring,
483            used_ring,
484
485            available_ring_state: AvailableRingHelper::new(MAX_QUEUE_SIZE),
486            last_used_idx: Cell::new(0),
487
488            transport: OptionalCell::empty(),
489
490            initialized: Cell::new(false),
491            queue_number: Cell::new(0),
492            max_elements: Cell::new(MAX_QUEUE_SIZE),
493
494            descriptor_buffers: init_constgeneric_default_array(),
495
496            client: OptionalCell::empty(),
497            used_callbacks_enabled: Cell::new(false),
498        }
499    }
500
501    /// Set the [`SplitVirtqueueClient`].
502    pub fn set_client(&self, client: &'a dyn SplitVirtqueueClient<'b>) {
503        self.client.set(client);
504    }
505
506    /// Set the underlying [`VirtIOTransport`]. This must be done prior to
507    /// initialization.
508    pub fn set_transport(&self, transport: &'a dyn VirtIOTransport) {
509        assert!(!self.initialized.get());
510        self.transport.set(transport);
511    }
512
513    /// Get the queue number associated with this Virtqueue.
514    ///
515    /// Prior to initialization the SplitVirtqueue does not have an associated
516    /// queue number and will return `None`.
517    pub fn queue_number(&self) -> Option<u32> {
518        if self.initialized.get() {
519            Some(self.queue_number.get())
520        } else {
521            None
522        }
523    }
524
525    /// Get the number of free descriptor slots in the descriptor table.
526    ///
527    /// This takes into account the negotiated maximum queue length.
528    pub fn free_descriptor_count(&self) -> usize {
529        assert!(self.initialized.get());
530        self.descriptor_buffers
531            .iter()
532            .take(self.max_elements.get())
533            .fold(0, |count, descbuf_entry| {
534                if descbuf_entry.is_none() {
535                    count + 1
536                } else {
537                    count
538                }
539            })
540    }
541
542    /// Get the number of (unprocessed) descriptor chains in the Virtqueue's
543    /// used ring.
544    pub fn used_descriptor_chains_count(&self) -> usize {
545        let pending_chains = self
546            .used_ring
547            .idx
548            .get()
549            .wrapping_sub(self.last_used_idx.get());
550
551        // If we ever have more than max_elements pending descriptors,
552        // the used ring increased too fast and has overwritten data
553        assert!(pending_chains as usize <= self.max_elements.get());
554
555        pending_chains as usize
556    }
557
558    /// Remove an element from the Virtqueue's used ring.
559    ///
560    /// If `self.last_used_idx.get() == self.used_ring.idx.get()` (e.g. we don't
561    /// have an unprocessed used buffer chain) this will return
562    /// `None`. Otherwise it will return the remove ring element's index, as
563    /// well as the number of processed bytes as reported by the VirtIO device.
564    ///
565    /// This will update `self.last_used_idx`.
566    ///
567    /// The caller is responsible for keeping the available ring in sync,
568    /// freeing one entry if a used buffer was removed through this method.
569    fn remove_used_chain(&self) -> Option<(usize, usize)> {
570        assert!(self.initialized.get());
571
572        let pending_chains = self.used_descriptor_chains_count();
573
574        if pending_chains > 0 {
575            let last_used_idx = self.last_used_idx.get();
576
577            // Remove the element one below the index (as 0 indicates
578            // _no_ buffer has been written), hence the index points
579            // to the next element to be written
580            let ring_pos = (last_used_idx as usize) % self.max_elements.get();
581            let chain_top_idx = self.used_ring.ring[ring_pos].id.get();
582            let written_len = self.used_ring.ring[ring_pos].len.get();
583
584            // Increment our local idx counter
585            self.last_used_idx.set(last_used_idx.wrapping_add(1));
586
587            Some((chain_top_idx as usize, written_len as usize))
588        } else {
589            None
590        }
591    }
592
593    /// Add an element to the available queue.
594    ///
595    /// Returns either the inserted ring index or `None` if the Virtqueue's
596    /// available ring is fully occupied.
597    ///
598    /// This will update the available ring's `idx` field.
599    ///
600    /// The caller is responsible for notifying the device about any inserted
601    /// available buffers.
602    fn add_available_descriptor(&self, descriptor_chain_head: usize) -> Option<usize> {
603        assert!(self.initialized.get());
604
605        if let Some(element_pos) = self.available_ring_state.insert() {
606            // Write the element
607            self.available_ring.ring[element_pos as usize]
608                .0
609                .set(descriptor_chain_head as u16);
610
611            // TODO: Perform a suitable memory barrier using a method exposed by
612            // the transport. For now, we don't negotiate
613            // VIRTIO_F_ORDER_PLATFORM, which means that any device which
614            // requires proper memory barriers (read: not implemented in
615            // software, like QEMU) should refuse operation. We use volatile
616            // memory accesses, so read/write reordering by the compiler is not
617            // an issue.
618
619            // Update the idx
620            self.available_ring
621                .idx
622                .set(self.available_ring.idx.get().wrapping_add(1));
623
624            Some(element_pos as usize)
625        } else {
626            None
627        }
628    }
629
630    fn add_descriptor_chain(
631        &self,
632        buffer_chain: &mut [Option<VirtqueueBuffer<'b>>],
633    ) -> Result<usize, ErrorCode> {
634        assert!(self.initialized.get());
635
636        // Get size of actual chain, until the first None
637        let queue_length = buffer_chain
638            .iter()
639            .take_while(|elem| elem.is_some())
640            .count();
641
642        // Make sure we have sufficient space available
643        //
644        // This takes into account the negotiated max size and will
645        // only list free iterators within that range
646        if self.free_descriptor_count() < queue_length {
647            return Err(ErrorCode::NOMEM);
648        }
649
650        // Walk over the descriptor table & buffer chain in parallel,
651        // inserting where empty
652        //
653        // We don't need to do any bounds checking here, if we run
654        // over the boundary it's safe to panic as something is
655        // seriously wrong with `free_descriptor_count`
656        let mut i = 0;
657        let mut previous_descriptor: Option<usize> = None;
658        let mut head = None;
659        let queuebuf_iter = buffer_chain.iter_mut().peekable();
660        for queuebuf in queuebuf_iter.take_while(|queuebuf| queuebuf.is_some()) {
661            // Take the queuebuf out of the caller array
662            let taken_queuebuf = queuebuf.take().expect("queuebuf is None");
663
664            // Sanity check the buffer: the subslice length may never
665            // exceed the slice length
666            assert!(taken_queuebuf.buf.len() >= taken_queuebuf.len);
667
668            while self.descriptor_buffers[i].is_some() {
669                i += 1;
670
671                // We should never run over the end, as we should have
672                // sufficient free descriptors
673                assert!(i < self.descriptor_buffers.len());
674            }
675
676            // Alright, we found a slot to insert the descriptor
677            //
678            // Check if it's the first one and store it's index as head
679            if head.is_none() {
680                head = Some(i);
681            }
682
683            // Write out the descriptor
684            let desc = &self.descriptors.0[i];
685            desc.len.set(taken_queuebuf.len as u32);
686            assert!(desc.len.get() > 0);
687            desc.addr.set(taken_queuebuf.buf.as_ptr() as u64);
688            desc.flags.write(if taken_queuebuf.device_writeable {
689                DescriptorFlags::WriteOnly::SET
690            } else {
691                DescriptorFlags::WriteOnly::CLEAR
692            });
693
694            // Now that we know our descriptor position, check whether
695            // we must chain ourself to a previous descriptor
696            if let Some(prev_index) = previous_descriptor {
697                self.descriptors.0[prev_index]
698                    .flags
699                    .modify(DescriptorFlags::Next::SET);
700                self.descriptors.0[prev_index].next.set(i as u16);
701            }
702
703            // Finally, store the full slice for reference. We don't store a
704            // proper Rust slice reference, as this would violate aliasing
705            // requirements: while the buffer is in the chain, it may be written
706            // by the VirtIO device.
707            //
708            // This can be changed to something slightly more elegant, once the
709            // NonNull functions around slices have been stabilized:
710            // https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.slice_from_raw_parts
711            self.descriptor_buffers[i]
712                .replace(SharedDescriptorBuffer::from_slice(taken_queuebuf.buf));
713
714            // Set ourself as the previous descriptor, as we know the position
715            // of `next` only in the next loop iteration.
716            previous_descriptor = Some(i);
717
718            // Increment the counter to not check the current
719            // descriptor entry again
720            i += 1;
721        }
722
723        Ok(head.expect("No head added to the descriptor table"))
724    }
725
726    fn remove_descriptor_chain(
727        &self,
728        top_descriptor_index: usize,
729    ) -> [Option<VirtqueueBuffer<'b>>; MAX_QUEUE_SIZE] {
730        assert!(self.initialized.get());
731
732        let mut res: [Option<VirtqueueBuffer<'b>>; MAX_QUEUE_SIZE] =
733            init_constgeneric_default_array();
734
735        let mut i = 0;
736        let mut next_index: Option<usize> = Some(top_descriptor_index);
737
738        while let Some(current_index) = next_index {
739            // Get a reference over the current descriptor
740            let current_desc = &self.descriptors.0[current_index];
741
742            // Check whether we have a chained descriptor and store that in next_index
743            if current_desc.flags.is_set(DescriptorFlags::Next) {
744                next_index = Some(current_desc.next.get() as usize);
745            } else {
746                next_index = None;
747            }
748
749            // Recover the slice originally associated with this
750            // descriptor & delete it from the buffers array
751            //
752            // The caller may have provided us a larger Rust slice,
753            // but indicated to only provide a subslice to VirtIO,
754            // hence we'll use the stored original slice and also
755            // return the subslice length
756            let supplied_slice = self.descriptor_buffers[current_index]
757                .take()
758                .expect("Virtqueue descriptors and slices out of sync")
759                .into_slice();
760            assert!(supplied_slice.as_mut_ptr() as u64 == current_desc.addr.get());
761
762            // Reconstruct the input VirtqueueBuffer to hand it back
763            res[i] = Some(VirtqueueBuffer {
764                buf: supplied_slice,
765                len: current_desc.len.get() as usize,
766                device_writeable: current_desc.flags.is_set(DescriptorFlags::WriteOnly),
767            });
768
769            // Zero the descriptor
770            current_desc.addr.set(0);
771            current_desc.len.set(0);
772            current_desc.flags.set(0);
773            current_desc.next.set(0);
774
775            // Increment the loop iterator
776            i += 1;
777        }
778
779        res
780    }
781
782    /// Provide a single chain of buffers to the device.
783    ///
784    /// This method will iterate over the passed slice until it encounters the
785    /// first `None`. It will first validate that the number of buffers can be
786    /// inserted into its descriptor table, and if not return
787    /// `Err(ErrorCode::NOMEM)`. If sufficient space is available, it takes the
788    /// passed buffers out of the provided `Option`s until encountering the
789    /// first `None` and shares this buffer chain with the device.
790    ///
791    /// When the device has finished processing the passed buffer chain, it is
792    /// returned to the client either through the
793    /// [`SplitVirtqueueClient::buffer_chain_ready`] callback, or can be
794    /// retrieved through the [`SplitVirtqueue::pop_used_buffer_chain`] method.
795    pub fn provide_buffer_chain(
796        &self,
797        buffer_chain: &mut [Option<VirtqueueBuffer<'b>>],
798    ) -> Result<(), ErrorCode> {
799        assert!(self.initialized.get());
800
801        // Try to add the chain into the descriptor array
802        let descriptor_chain_head = self.add_descriptor_chain(buffer_chain)?;
803
804        // Now make it available to the device. If there was sufficient space
805        // available to add the chain's descriptors (of which there may be
806        // multiple), there should also be sufficient space in the available
807        // ring (where a multi-descriptor chain will occupy only one elements).
808        self.add_available_descriptor(descriptor_chain_head)
809            .expect("Insufficient space in available ring");
810
811        // Notify the queue. This must not fail, given that the SplitVirtqueue
812        // requires a transport to be set prior to initialization.
813        self.transport
814            .map(|t| t.queue_notify(self.queue_number.get()))
815            .unwrap();
816
817        Ok(())
818    }
819
820    /// Attempt to take a buffer chain out of the Virtqueue used ring.
821    ///
822    /// Returns `None` if the used ring is empty.
823    pub fn pop_used_buffer_chain(
824        &self,
825    ) -> Option<([Option<VirtqueueBuffer<'b>>; MAX_QUEUE_SIZE], usize)> {
826        assert!(self.initialized.get());
827
828        self.remove_used_chain()
829            .map(|(descriptor_idx, bytes_used)| {
830                // Get the descriptor chain
831                let chain = self.remove_descriptor_chain(descriptor_idx);
832
833                // Remove the first entry of the available ring, since we
834                // got a single buffer back and can therefore make another
835                // buffer available to the device without risking an
836                // overflow of the used ring
837                self.available_ring_state.pop();
838
839                (chain, bytes_used)
840            })
841    }
842
843    /// Disable callback delivery for the
844    /// [`SplitVirtqueueClient::buffer_chain_ready`] method on the registered
845    /// client.
846    pub fn enable_used_callbacks(&self) {
847        self.used_callbacks_enabled.set(true);
848    }
849
850    /// Enable callback delivery for the
851    /// [`SplitVirtqueueClient::buffer_chain_ready`] method on the registered
852    /// client.
853    ///
854    /// Callback delivery is enabled by default. If this is not desired, call
855    /// this method prior to registering a client.
856    pub fn disable_used_callbacks(&self) {
857        self.used_callbacks_enabled.set(false);
858    }
859}
860
861impl<const MAX_QUEUE_SIZE: usize> Virtqueue for SplitVirtqueue<'_, '_, MAX_QUEUE_SIZE> {
862    fn used_interrupt(&self) {
863        assert!(self.initialized.get());
864        // A buffer MAY have been put into the used in by the device
865        //
866        // Try to extract all pending used buffers and return them to
867        // the clients via callbacks
868
869        while self.used_callbacks_enabled.get() {
870            if let Some((mut chain, bytes_used)) = self.pop_used_buffer_chain() {
871                self.client.map(move |client| {
872                    client.buffer_chain_ready(self.queue_number.get(), chain.as_mut(), bytes_used)
873                });
874            } else {
875                break;
876            }
877        }
878    }
879
880    fn physical_addresses(&self) -> VirtqueueAddresses {
881        VirtqueueAddresses {
882            descriptor_area: core::ptr::from_ref(self.descriptors) as u64,
883            driver_area: core::ptr::from_ref(self.available_ring) as u64,
884            device_area: core::ptr::from_ref(self.used_ring) as u64,
885        }
886    }
887
888    fn negotiate_queue_size(&self, max_elements: usize) -> usize {
889        assert!(!self.initialized.get());
890        let negotiated = cmp::min(MAX_QUEUE_SIZE, max_elements);
891        self.max_elements.set(negotiated);
892        self.available_ring_state.reset(negotiated);
893        negotiated
894    }
895
896    fn initialize(&self, queue_number: u32, _queue_elements: usize) {
897        assert!(!self.initialized.get());
898
899        // The transport must be set prior to initialization:
900        assert!(self.transport.is_some());
901
902        // TODO: Zero the queue
903        //
904        // For now we assume all passed queue buffers are already
905        // zeroed
906
907        self.queue_number.set(queue_number);
908        self.initialized.set(true);
909    }
910}
911
912pub trait SplitVirtqueueClient<'b> {
913    fn buffer_chain_ready(
914        &self,
915        queue_number: u32,
916        buffer_chain: &mut [Option<VirtqueueBuffer<'b>>],
917        bytes_used: usize,
918    );
919}