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}