Skip to main content

axvirtio_common/queue/
mod.rs

1mod available;
2mod descriptor;
3mod used;
4
5use alloc::{sync::Arc, vec::Vec};
6use core::sync::atomic::{AtomicBool, AtomicU16, Ordering};
7
8pub use available::{AvailableRing, VirtQueueAvail};
9use axaddrspace::GuestMemoryAccessor;
10use axvm_types::GuestPhysAddr;
11pub use descriptor::{DescriptorChain, DescriptorTable, VirtQueueDesc};
12use log::{trace, warn};
13use mbarrier::mb;
14pub use used::{UsedRing, VirtQueueUsed, VirtqUsedElem};
15
16use crate::{
17    VirtioDeviceID,
18    constants::{VIRTQ_AVAIL_ALIGN, VIRTQ_DESC_ALIGN, VIRTQ_USED_ALIGN},
19    error::{VirtioError, VirtioResult},
20};
21
22/// VirtIO queue implementation
23#[derive(Debug)]
24pub struct VirtioQueue<T: GuestMemoryAccessor + Clone> {
25    /// Queue index
26    pub index: u16,
27    /// Queue size
28    pub size: u16,
29    /// Descriptor table
30    pub desc_table: Option<DescriptorTable>,
31    /// Available ring
32    avail_ring: Option<AvailableRing<T>>,
33    /// Used ring
34    used_ring: Option<UsedRing<T>>,
35    /// Guest memory accessor
36    accessor: Arc<T>,
37    /// Maximum queue size
38    pub max_size: u16,
39    /// Queue ready flag
40    pub ready: bool,
41    /// A lock-external queue-ready validation transaction is in progress.
42    preparing: bool,
43    /// Descriptor table address (guest physical)
44    pub desc_table_addr: GuestPhysAddr,
45    /// Available ring address (guest physical)
46    pub avail_ring_addr: GuestPhysAddr,
47    /// Used ring address (guest physical)
48    pub used_ring_addr: GuestPhysAddr,
49    /// Next available index
50    next_avail: u16,
51    /// Next used index
52    next_used: u16,
53    /// Used index at the previous notification-suppression check.
54    notification_old_used: AtomicU16,
55    /// Event index enabled.
56    ///
57    /// Set after the driver accepts `VIRTIO_F_RING_EVENT_IDX` and seals
58    /// `FEATURES_OK`. Layout validation deliberately does not depend on it:
59    /// ring regions always include the 2-byte event-index footer.
60    pub event_idx_enabled: bool,
61    /// Set when a runtime ring/descriptor validation failure occurs; the queue
62    /// rejects `pop`/`complete` and the guest-data paths until
63    /// [`reset`](Self::reset) clears it.
64    ///
65    /// A single bool that latches only `false -> true` (no lost-update
66    /// concern): the `Release` store in `latch_fault` publishes the fault to
67    /// any thread that later queries `is_faulted` with an `Acquire` load.
68    /// The queue is expected to be guarded by the owning device's lock, but
69    /// unlike a `Cell` the atomic stays sound even if the queue is aliased
70    /// across threads.
71    faulted: AtomicBool,
72    /// Set once per configuration cycle after the layout-rejection warning has
73    /// been emitted. A guest can re-trigger the `QUEUE_READY` memory probe
74    /// unboundedly, so repeats are logged at `trace!` instead of `warn!`;
75    /// [`reset`](Self::reset) clears the latch for the next configuration.
76    layout_warn_emitted: AtomicBool,
77}
78
79impl<T: GuestMemoryAccessor + Clone> Clone for VirtioQueue<T> {
80    /// Clones the queue configuration, snapshotting the notification baseline,
81    /// faulted state and layout-warning latch into fresh atomics (the clone does
82    /// not share those states with the original).
83    fn clone(&self) -> Self {
84        Self {
85            index: self.index,
86            size: self.size,
87            desc_table: self.desc_table.clone(),
88            avail_ring: self.avail_ring.clone(),
89            used_ring: self.used_ring.clone(),
90            accessor: self.accessor.clone(),
91            max_size: self.max_size,
92            ready: self.ready,
93            preparing: self.preparing,
94            desc_table_addr: self.desc_table_addr,
95            avail_ring_addr: self.avail_ring_addr,
96            used_ring_addr: self.used_ring_addr,
97            next_avail: self.next_avail,
98            next_used: self.next_used,
99            notification_old_used: AtomicU16::new(
100                self.notification_old_used.load(Ordering::Acquire),
101            ),
102            event_idx_enabled: self.event_idx_enabled,
103            faulted: AtomicBool::new(self.faulted.load(Ordering::Acquire)),
104            layout_warn_emitted: AtomicBool::new(self.layout_warn_emitted.load(Ordering::Acquire)),
105        }
106    }
107}
108
109impl<T: GuestMemoryAccessor + Clone> VirtioQueue<T> {
110    /// Create a new VirtIO queue
111    pub fn new(index: u16, size: u16, accessor: Arc<T>) -> Self {
112        Self {
113            index,
114            size,
115            desc_table: None,
116            avail_ring: None,
117            used_ring: None,
118            accessor,
119            max_size: size,
120            ready: false,
121            preparing: false,
122            desc_table_addr: GuestPhysAddr::from(0),
123            avail_ring_addr: GuestPhysAddr::from(0),
124            used_ring_addr: GuestPhysAddr::from(0),
125            next_avail: 0,
126            next_used: 0,
127            notification_old_used: AtomicU16::new(0),
128            event_idx_enabled: false,
129            faulted: AtomicBool::new(false),
130            layout_warn_emitted: AtomicBool::new(false),
131        }
132    }
133
134    /// Set queue size
135    ///
136    /// Rejected once any ring address is programmed or the queue is ready: the
137    /// ring objects snapshot the size when their address is set, so a later
138    /// resize would leave layout validation and runtime ring accesses derived
139    /// from different sizes, letting the queue serve requests outside the
140    /// validated regions.
141    pub fn set_size(&mut self, size: u16) -> VirtioResult<()> {
142        if size == 0 || size > self.max_size || (size & (size - 1)) != 0 {
143            return Err(VirtioError::InvalidQueue);
144        }
145        if self.ready || self.is_configured() {
146            return Err(VirtioError::InvalidQueue);
147        }
148        self.size = size;
149        Ok(())
150    }
151
152    /// Set descriptor table address
153    pub fn set_desc_table_addr(&mut self, addr: GuestPhysAddr) -> VirtioResult<()> {
154        // Overwrite semantics: VirtIO MMIO programs a 64-bit address via separate
155        // LOW/HIGH 32-bit writes, so the setter accepts repeated updates and keeps
156        // the latest combined value rather than rejecting the second write.
157        self.desc_table_addr = addr;
158        if addr.as_usize() != 0 {
159            self.desc_table = Some(DescriptorTable::new(addr, self.size));
160        } else {
161            self.desc_table = None;
162        }
163        Ok(())
164    }
165
166    /// Set available ring address
167    pub fn set_avail_ring_addr(&mut self, addr: GuestPhysAddr) -> VirtioResult<()> {
168        self.avail_ring_addr = addr;
169        if addr.as_usize() != 0 {
170            self.avail_ring = Some(AvailableRing::new(addr, self.size, self.accessor.clone()));
171        } else {
172            self.avail_ring = None;
173        }
174        Ok(())
175    }
176
177    /// Set used ring address
178    pub fn set_used_ring_addr(&mut self, addr: GuestPhysAddr) -> VirtioResult<()> {
179        self.used_ring_addr = addr;
180        // UsedRing::new starts its producer index at zero, so rebuilding it
181        // also starts a new notification-suppression epoch at zero.
182        self.next_used = 0;
183        self.notification_old_used.store(0, Ordering::Release);
184        if addr.as_usize() != 0 {
185            self.used_ring = Some(UsedRing::new(addr, self.size, self.accessor.clone()));
186        } else {
187            self.used_ring = None;
188        }
189        Ok(())
190    }
191
192    /// Mark queue as ready
193    pub fn set_ready(&mut self, ready: bool) {
194        self.ready = ready;
195    }
196
197    /// Whether the three ring addresses have all been programmed.
198    ///
199    /// Address `0` is the "unconfigured" sentinel: a driver that has not
200    /// finished programming a ring must never be able to make the queue ready.
201    pub fn is_configured(&self) -> bool {
202        self.desc_table_addr.as_usize() != 0
203            && self.avail_ring_addr.as_usize() != 0
204            && self.used_ring_addr.as_usize() != 0
205    }
206
207    /// Whether two queue snapshots describe the same programmable layout.
208    pub(crate) fn has_same_configuration(&self, other: &Self) -> bool {
209        self.index == other.index
210            && self.size == other.size
211            && self.desc_table_addr == other.desc_table_addr
212            && self.avail_ring_addr == other.avail_ring_addr
213            && self.used_ring_addr == other.used_ring_addr
214            && self.event_idx_enabled == other.event_idx_enabled
215    }
216
217    /// Starts one queue-ready preparation transaction.
218    pub(crate) fn begin_ready_preparation(&mut self) -> Option<Self> {
219        if self.ready || self.preparing || !self.is_configured() {
220            return None;
221        }
222        self.preparing = true;
223        Some(self.clone())
224    }
225
226    /// Commits or rejects a completed queue-ready preparation transaction,
227    /// including any warning latch raised while validating its snapshot.
228    pub(crate) fn finish_ready_preparation(&mut self, snapshot: &Self, prepared: bool) {
229        if !self.preparing {
230            return;
231        }
232        if self.has_same_configuration(snapshot) {
233            if snapshot.layout_warn_emitted.load(Ordering::Acquire) {
234                self.layout_warn_emitted.store(true, Ordering::Release);
235            }
236            self.ready = prepared;
237        }
238        self.preparing = false;
239    }
240
241    /// Cancels any queue-ready preparation and makes the queue unavailable.
242    pub(crate) fn cancel_ready_preparation(&mut self) {
243        self.ready = false;
244        self.preparing = false;
245    }
246
247    /// The guest-memory accessor used by the non-`_with_memory` operations.
248    pub fn accessor(&self) -> &Arc<T> {
249        &self.accessor
250    }
251
252    /// Validate the three ring layouts against the VirtIO split-ring
253    /// requirements. This is a pure query and does not change queue state.
254    ///
255    /// Checks, per VirtIO 1.x ยง2.7:
256    /// - all three ring addresses are non-zero;
257    /// - the descriptor table is 16-byte aligned, the available ring 2-byte and
258    ///   the used ring 4-byte aligned;
259    /// - `addr + size * elem_size` does not overflow the guest address space
260    ///   for any ring;
261    /// - the three regions do not overlap (overlap would let a used-element
262    ///   write corrupt descriptors the device is about to read).
263    ///
264    /// The available and used regions always include their 2-byte event-index
265    /// footer (`used_event` / `avail_event`), even when
266    /// `VIRTIO_F_RING_EVENT_IDX` is not negotiated: a driver that negotiated
267    /// it writes into those bytes, and the ring types' own `total_size`
268    /// counts them. Always covering the footer is the conservative,
269    /// negotiation-independent envelope.
270    ///
271    /// The transport is expected to call this from its single "queue becomes
272    /// usable" enforcement point (MMIO: the `QUEUE_READY` write; PCI: layout
273    /// programmed in the queue config registers) and to refuse to mark the
274    /// queue ready when it fails.
275    pub fn validate_layout(&self) -> VirtioResult<()> {
276        let regions = self.ring_regions();
277        regions
278            .iter()
279            .all(|region| region.base.as_usize() != 0)
280            .then_some(())
281            .ok_or(VirtioError::InvalidRingLayout)?;
282        if !self
283            .desc_table_addr
284            .as_usize()
285            .is_multiple_of(VIRTQ_DESC_ALIGN)
286            || !self
287                .avail_ring_addr
288                .as_usize()
289                .is_multiple_of(VIRTQ_AVAIL_ALIGN)
290            || !self
291                .used_ring_addr
292                .as_usize()
293                .is_multiple_of(VIRTQ_USED_ALIGN)
294        {
295            return Err(VirtioError::RingMisaligned);
296        }
297        for (index, region) in regions.iter().enumerate() {
298            if region.end().is_none() {
299                return Err(VirtioError::InvalidRingLayout);
300            }
301            if regions[index + 1..]
302                .iter()
303                .any(|other| region.overlaps(other))
304            {
305                return Err(VirtioError::RingOverlap);
306            }
307        }
308        Ok(())
309    }
310
311    /// Validates the ring layout like [`validate_layout`](Self::validate_layout)
312    /// and additionally screens the ring regions against `memory`: the first
313    /// byte and the last byte (`end - 1`) of every region must be readable
314    /// through `memory`.
315    ///
316    /// `memory` must be backed by the same accessor the queue uses for its
317    /// runtime accesses; passing a capability over different memory makes the
318    /// check vacuous. Only the two boundary bytes per region are probed on
319    /// purpose: this is a best-effort enable-time screen, and the per-byte
320    /// runtime accesses are what ultimately verify mid-region mapping.
321    ///
322    /// An accessor that cannot translate any guest address (such as
323    /// [`NoGuestMemoryAccessor`](crate::memory::NoGuestMemoryAccessor), whose
324    /// `translate_and_get_limit` always returns `None`) fails every probe and
325    /// therefore cannot satisfy this check; such layouts are rejected (the
326    /// first rejection per configuration cycle is warned, later ones only
327    /// traced), so the MMIO transport requires an accessor backed by real
328    /// guest memory. Memory-screening failures are reported as
329    /// [`VirtioError::InvalidRingLayout`], while pure layout errors keep their
330    /// specific variants ([`RingMisaligned`](VirtioError::RingMisaligned),
331    /// [`RingOverlap`](VirtioError::RingOverlap)); any `Err` means the layout
332    /// was rejected, so the caller only needs to distinguish "layout rejected"
333    /// from "queue ready".
334    pub fn validate_layout_with_memory(
335        &self,
336        memory: &mut dyn crate::GuestMemory,
337    ) -> VirtioResult<()> {
338        self.validate_layout()?;
339        for region in self.ring_regions() {
340            let end = region.end().ok_or(VirtioError::InvalidRingLayout)?;
341            if memory.read(region.base, &mut [0u8; 1]).is_err()
342                || memory
343                    .read(GuestPhysAddr::from(end - 1), &mut [0u8; 1])
344                    .is_err()
345            {
346                // A guest can write QUEUE_READY unboundedly, so warn only once
347                // per configuration cycle; later rejections are traced.
348                if self.layout_warn_emitted.swap(true, Ordering::AcqRel) {
349                    trace!(
350                        "virtqueue {}: ring region 0x{:x}..0x{:x} is still not fully mapped; \
351                         rejecting layout again",
352                        self.index,
353                        region.base.as_usize(),
354                        end,
355                    );
356                } else {
357                    warn!(
358                        "virtqueue {}: ring region 0x{:x}..0x{:x} is not fully mapped in guest \
359                         memory; rejecting layout",
360                        self.index,
361                        region.base.as_usize(),
362                        end,
363                    );
364                }
365                return Err(VirtioError::InvalidRingLayout);
366            }
367        }
368        Ok(())
369    }
370
371    /// The three ring regions derived from the current layout.
372    ///
373    /// The available and used regions always include their 2-byte event-index
374    /// footer, regardless of whether `VIRTIO_F_RING_EVENT_IDX` is negotiated;
375    /// the size math is owned by `DescriptorTable::layout_size`,
376    /// `AvailableRing::layout_size` and `UsedRing::layout_size` so the footer
377    /// cannot be forgotten here.
378    fn ring_regions(&self) -> [RingRegion; 3] {
379        [
380            RingRegion::new(
381                self.desc_table_addr,
382                DescriptorTable::layout_size(self.size),
383            ),
384            RingRegion::new(
385                self.avail_ring_addr,
386                AvailableRing::<T>::layout_size(self.size),
387            ),
388            RingRegion::new(self.used_ring_addr, UsedRing::<T>::layout_size(self.size)),
389        ]
390    }
391
392    /// Whether the queue is in the faulted state and must be reset before
393    /// further `pop`/`complete` calls.
394    ///
395    /// While faulted, the guest-serving data paths (`pop`/`complete`, chain
396    /// walks and data access) reject with [`VirtioError::QueueFaulted`]. The
397    /// configuration setters remain usable so a driver can re-program the
398    /// queue, and [`reset`](Self::reset) is the only operation that clears
399    /// the fault.
400    pub fn is_faulted(&self) -> bool {
401        self.faulted.load(Ordering::Acquire)
402    }
403
404    /// Check if queue is valid and ready
405    pub fn is_valid(&self) -> bool {
406        self.ready
407            && self.desc_table_addr.as_usize() != 0
408            && self.avail_ring_addr.as_usize() != 0
409            && self.used_ring_addr.as_usize() != 0
410            && self.validate_layout().is_ok()
411    }
412
413    /// Latch the queue into the faulted state after a runtime validation
414    /// failure; `pop`/`complete` are rejected until [`reset`](Self::reset).
415    fn latch_fault(&self) {
416        self.faulted.store(true, Ordering::Release);
417        trace!("virtqueue {}: latched faulted state", self.index);
418    }
419
420    /// Reset the queue: clears the ready flag, the faulted state and the
421    /// layout-warning latch, and discards the programmed ring addresses,
422    /// indices and ring objects, so the driver must re-program the queue
423    /// before it can be used again.
424    ///
425    /// While faulted, the guest-serving data paths reject with
426    /// [`VirtioError::QueueFaulted`] but the configuration setters remain
427    /// usable; `reset` is the only operation that clears the fault.
428    pub fn reset(&mut self) {
429        self.ready = false;
430        self.preparing = false;
431        self.desc_table_addr = GuestPhysAddr::from(0);
432        self.avail_ring_addr = GuestPhysAddr::from(0);
433        self.used_ring_addr = GuestPhysAddr::from(0);
434        self.next_avail = 0;
435        self.next_used = 0;
436        self.notification_old_used.store(0, Ordering::Release);
437        self.event_idx_enabled = false;
438        self.desc_table = None;
439        self.avail_ring = None;
440        self.used_ring = None;
441        self.faulted.store(false, Ordering::Release);
442        self.layout_warn_emitted.store(false, Ordering::Release);
443    }
444
445    /// Read available ring index through the queue's own accessor.
446    ///
447    /// Returns [`VirtioError::QueueFaulted`] when the queue is faulted and
448    /// [`VirtioError::QueueNotReady`] when the available ring is not
449    /// configured (not a runtime failure, so the queue is not faulted). A read
450    /// failure of a configured ring is a runtime failure and latches the
451    /// fault, matching the other avail-ring pre-read paths.
452    pub fn read_avail_idx(&self) -> VirtioResult<u16> {
453        let accessor = self.accessor.clone();
454        let mut memory = crate::AddressSpaceMemory::new(&*accessor);
455        self.read_avail_idx_with_memory(&mut memory)
456    }
457
458    /// Reads the available index with a scoped memory capability.
459    ///
460    /// Returns [`VirtioError::QueueFaulted`] when the queue is faulted and
461    /// [`VirtioError::QueueNotReady`] when the available ring is not
462    /// configured (not a runtime failure, so the queue is not faulted). A read
463    /// failure of a configured ring is a runtime failure and latches the
464    /// fault, matching the other avail-ring pre-read paths.
465    pub fn read_avail_idx_with_memory(
466        &self,
467        memory: &mut dyn crate::GuestMemory,
468    ) -> VirtioResult<u16> {
469        if self.faulted.load(Ordering::Acquire) {
470            return Err(VirtioError::QueueFaulted);
471        }
472        let Some(avail_ring) = self.avail_ring.as_ref() else {
473            return Err(VirtioError::QueueNotReady);
474        };
475        let result = avail_ring.read_avail_idx_with_memory(memory);
476        if result.is_err() {
477            self.latch_fault();
478        }
479        result
480    }
481
482    /// Add a used buffer to the used ring.
483    ///
484    /// Returns [`VirtioError::QueueNotReady`] when the queue is not ready or
485    /// the used ring is not configured: a missing used ring is never silently
486    /// accepted as a success (the historical fallback did exactly that).
487    /// Being unconfigured is not a runtime failure, so the queue is not
488    /// faulted; a guest-memory write failure on a configured ring does latch
489    /// the fault.
490    pub fn add_used(&mut self, desc_index: u16, len: u32) -> VirtioResult<()> {
491        if self.faulted.load(Ordering::Acquire) {
492            return Err(VirtioError::QueueFaulted);
493        }
494        if !self.is_valid() {
495            return Err(VirtioError::QueueNotReady);
496        }
497
498        let Some(used_ring) = self.used_ring.as_mut() else {
499            // No used ring is configured: completing into nothing would be
500            // an "error success" against this crate's fault contract, so
501            // report QueueNotReady without latching the fault.
502            return Err(VirtioError::QueueNotReady);
503        };
504        let result = used_ring.add_used(desc_index as u32, len);
505        if result.is_ok() {
506            self.next_used = used_ring.get_used_idx();
507        } else {
508            // A guest-memory write failure on a configured queue is a runtime
509            // failure: latch the fault so no "error success" completion can
510            // follow, mirroring `complete_with_memory`.
511            self.latch_fault();
512        }
513        result
514    }
515
516    /// Consume one available-ring head index, or `None` if the queue is empty.
517    ///
518    /// Advances `last_avail_idx` by one (wrapping at `u16::MAX`). Returns
519    /// [`VirtioError::InvalidQueue`] when the guest's `avail.idx` is ahead by
520    /// more than `size`, which indicates a corrupted available ring.
521    pub fn pop_available_head(&mut self) -> VirtioResult<Option<u16>> {
522        let accessor = self.accessor.clone();
523        let mut memory = crate::AddressSpaceMemory::new(&*accessor);
524        self.pop_available_head_with_memory(&mut memory)
525    }
526
527    /// Consumes one available head with a scoped memory capability.
528    pub fn pop_available_head_with_memory(
529        &mut self,
530        memory: &mut dyn crate::GuestMemory,
531    ) -> VirtioResult<Option<u16>> {
532        if self.faulted.load(Ordering::Acquire) {
533            return Err(VirtioError::QueueFaulted);
534        }
535        if !self.is_valid() {
536            return Err(VirtioError::QueueNotReady);
537        }
538        let avail_idx = self.read_avail_idx_with_memory(memory)?;
539        let last = self.get_last_avail_idx();
540        let pending = avail_idx.wrapping_sub(last);
541        if pending > self.size {
542            // Corrupted available ring: more entries pending than the queue
543            // can hold. Latch the fault so the queue stops serving until reset.
544            self.latch_fault();
545            return Err(VirtioError::InvalidQueue);
546        }
547        if pending == 0 {
548            return Ok(None);
549        }
550        let head = match self
551            .avail_ring
552            .as_ref()
553            .map(|ring| ring.read_avail_ring_entry_with_memory(last % self.size, memory))
554        {
555            Some(Ok(head)) => head,
556            Some(Err(error)) => {
557                // A guest-memory read failure on a configured queue is a
558                // runtime failure; latch the fault like the other runtime
559                // paths do.
560                self.latch_fault();
561                return Err(error);
562            }
563            None => return Err(VirtioError::QueueNotReady),
564        };
565        self.update_last_avail_idx(last.wrapping_add(1));
566        Ok(Some(head))
567    }
568
569    /// Rearms driver-to-device notifications after the available ring is drained.
570    ///
571    /// When event index was negotiated, the device publishes the next available
572    /// index it expects, executes a full memory barrier, then rechecks `avail.idx`.
573    /// A `true` result means a driver publication raced with rearming and must be
574    /// consumed before the device waits for another notification.
575    pub fn rearm_available_event(&mut self) -> VirtioResult<bool> {
576        let accessor = self.accessor.clone();
577        let mut memory = crate::AddressSpaceMemory::new(&*accessor);
578        self.rearm_available_event_with_memory(&mut memory)
579    }
580
581    /// Rearms event-index notifications with a scoped guest-memory capability.
582    pub fn rearm_available_event_with_memory(
583        &mut self,
584        memory: &mut dyn crate::GuestMemory,
585    ) -> VirtioResult<bool> {
586        if !self.event_idx_enabled {
587            return Ok(false);
588        }
589        if self.faulted.load(Ordering::Acquire) {
590            return Err(VirtioError::QueueFaulted);
591        }
592        if !self.is_valid() {
593            return Err(VirtioError::QueueNotReady);
594        }
595
596        let result = (|| {
597            let next_avail = self.get_last_avail_idx();
598            let used_ring = self.used_ring.as_ref().ok_or(VirtioError::QueueNotReady)?;
599            used_ring.set_notification_with_memory(false, memory)?;
600            used_ring.write_avail_event_with_memory(next_avail, memory)?;
601            mb();
602
603            let avail_idx = self.read_avail_idx_with_memory(memory)?;
604            let pending = avail_idx.wrapping_sub(next_avail);
605            if pending > self.size {
606                return Err(VirtioError::InvalidQueue);
607            }
608            Ok(pending != 0)
609        })();
610        if result.is_err() {
611            self.latch_fault();
612        }
613        result
614    }
615
616    /// Consume one available head and return a validated [`DescriptorChain`].
617    ///
618    /// Returns `Ok(None)` when the queue is empty. The head is consumed *before*
619    /// the chain is validated; on a validation error the head is already
620    /// advanced (so the queue is not stalled) and the caller should complete
621    /// that head with length 0. To recover the head on error, use
622    /// [`pop_available_head`](Self::pop_available_head) plus
623    /// [`descriptor_chain`](Self::descriptor_chain) directly.
624    pub fn pop_available(&mut self) -> VirtioResult<Option<DescriptorChain>> {
625        let head = match self.pop_available_head()? {
626            Some(h) => h,
627            None => return Ok(None),
628        };
629        Ok(Some(self.descriptor_chain(head)?))
630    }
631
632    /// Build a validated [`DescriptorChain`] for an already-consumed head index.
633    pub fn descriptor_chain(&self, head: u16) -> VirtioResult<DescriptorChain> {
634        let mut memory = crate::AddressSpaceMemory::new(&*self.accessor);
635        self.descriptor_chain_with_memory(head, &mut memory)
636    }
637
638    /// Builds a validated descriptor chain using a scoped memory capability.
639    ///
640    /// Returns [`VirtioError::QueueNotReady`] when the descriptor table is not
641    /// configured (not a runtime failure, so the queue is not faulted) and
642    /// [`VirtioError::QueueFaulted`] when the queue is already faulted.
643    pub fn descriptor_chain_with_memory(
644        &self,
645        head: u16,
646        memory: &mut dyn crate::GuestMemory,
647    ) -> VirtioResult<DescriptorChain> {
648        if self.faulted.load(Ordering::Acquire) {
649            return Err(VirtioError::QueueFaulted);
650        }
651        let Some(ref desc_table) = self.desc_table else {
652            return Err(VirtioError::QueueNotReady);
653        };
654        let result = desc_table.descriptor_chain(head, memory);
655        if result.is_err() {
656            // The descriptor table is configured, so a chain failure is a
657            // runtime validation failure: latch the fault.
658            self.latch_fault();
659        }
660        result
661    }
662
663    /// Complete a descriptor chain: append a used element for `head` with the
664    /// given written length, then report whether the driver should be notified.
665    ///
666    /// `written_len` is the number of bytes the device wrote into guest-writable
667    /// buffers (RX bytes, or 0 for TX / discarded / error completions).
668    pub fn complete(&mut self, head: u16, written_len: u32) -> VirtioResult<bool> {
669        self.add_used(head, written_len)?;
670        self.should_notify()
671    }
672
673    /// Completes a chain with a scoped memory capability.
674    ///
675    /// Returns [`VirtioError::QueueNotReady`] when a ring is not configured
676    /// (not a runtime failure, so the queue is not faulted) and
677    /// [`VirtioError::QueueFaulted`] when the queue is already faulted. Any
678    /// failure while writing the used ring or reading the available flags is
679    /// treated as a runtime failure and latches the fault.
680    pub fn complete_with_memory(
681        &mut self,
682        head: u16,
683        written_len: u32,
684        memory: &mut dyn crate::GuestMemory,
685    ) -> VirtioResult<bool> {
686        if self.faulted.load(Ordering::Acquire) {
687            return Err(VirtioError::QueueFaulted);
688        }
689        let result = (|| {
690            let used_ring = self.used_ring.as_mut().ok_or(VirtioError::QueueNotReady)?;
691            used_ring.add_used_with_memory(head as u32, written_len, memory)?;
692            self.next_used = used_ring.get_used_idx();
693            let avail_ring = self.avail_ring.as_ref().ok_or(VirtioError::QueueNotReady)?;
694            // Expose used.idx before checking the driver's notification
695            // suppression fields, as required by the split-ring protocol.
696            mb();
697            if self.event_idx_enabled {
698                let event = avail_ring.read_used_event_with_memory(memory)?;
699                Ok(event_idx_should_notify(
700                    event,
701                    self.next_used,
702                    self.notification_old_used.load(Ordering::Acquire),
703                ))
704            } else {
705                Ok(!avail_ring.interrupts_suppressed_with_memory(memory)?)
706            }
707        })();
708        if result.is_err() {
709            self.latch_fault();
710        } else {
711            self.notification_old_used
712                .store(self.next_used, Ordering::Release);
713        }
714        result
715    }
716
717    /// Get the used ring reference
718    pub fn get_used_ring(&self) -> Option<&UsedRing<T>> {
719        self.used_ring.as_ref()
720    }
721
722    /// Get the used ring mutable reference
723    pub fn get_used_ring_mut(&mut self) -> Option<&mut UsedRing<T>> {
724        self.used_ring.as_mut()
725    }
726
727    /// Get the available ring reference
728    pub fn get_avail_ring(&self) -> Option<&AvailableRing<T>> {
729        self.avail_ring.as_ref()
730    }
731
732    /// Get the descriptor table reference
733    pub fn get_desc_table(&self) -> Option<&DescriptorTable> {
734        self.desc_table.as_ref()
735    }
736
737    /// Read available ring entry through the queue's own accessor.
738    ///
739    /// Returns [`VirtioError::QueueFaulted`] when the queue is faulted and
740    /// [`VirtioError::QueueNotReady`] when the available ring is not
741    /// configured (not a runtime failure, so the queue is not faulted). A read
742    /// failure of a configured ring is a runtime failure and latches the
743    /// fault, matching the other avail-ring pre-read paths.
744    pub fn read_avail_entry(&self, ring_index: u16) -> VirtioResult<u16> {
745        let accessor = self.accessor.clone();
746        let mut memory = crate::AddressSpaceMemory::new(&*accessor);
747        self.read_avail_entry_with_memory(ring_index, &mut memory)
748    }
749
750    /// Reads an available-ring entry with a scoped memory capability.
751    ///
752    /// Returns [`VirtioError::QueueFaulted`] when the queue is faulted and
753    /// [`VirtioError::QueueNotReady`] when the available ring is not
754    /// configured (not a runtime failure, so the queue is not faulted). A read
755    /// failure of a configured ring is a runtime failure and latches the
756    /// fault, matching the other avail-ring pre-read paths.
757    pub fn read_avail_entry_with_memory(
758        &self,
759        ring_index: u16,
760        memory: &mut dyn crate::GuestMemory,
761    ) -> VirtioResult<u16> {
762        if self.faulted.load(Ordering::Acquire) {
763            return Err(VirtioError::QueueFaulted);
764        }
765        let Some(avail_ring) = self.avail_ring.as_ref() else {
766            return Err(VirtioError::QueueNotReady);
767        };
768        let result = avail_ring.read_avail_ring_entry_with_memory(ring_index, memory);
769        if result.is_err() {
770            self.latch_fault();
771        }
772        result
773    }
774
775    /// Update last available index
776    pub fn update_last_avail_idx(&mut self, idx: u16) {
777        if let Some(ref mut avail_ring) = self.avail_ring {
778            avail_ring.update_last_avail_idx(idx);
779        } else {
780            self.next_avail = idx % self.size;
781        }
782    }
783
784    /// Get last available index
785    pub fn get_last_avail_idx(&self) -> u16 {
786        if let Some(avail_ring) = &self.avail_ring {
787            avail_ring.last_avail_idx
788        } else {
789            self.next_avail
790        }
791    }
792
793    /// Validate VirtIO block chain
794    ///
795    /// Returns [`VirtioError::QueueNotReady`] when the descriptor table is not
796    /// configured and [`VirtioError::QueueFaulted`] when the queue is faulted.
797    /// Any validation or guest-memory failure on a configured queue latches
798    /// the fault, matching the other descriptor-chain walk entry points.
799    pub fn validate_virtio_block_chain(
800        &self,
801        head_index: u16,
802        min_length: usize,
803    ) -> VirtioResult<bool> {
804        if self.faulted.load(Ordering::Acquire) {
805            return Err(VirtioError::QueueFaulted);
806        }
807        let Some(ref desc_table) = self.desc_table else {
808            return Err(VirtioError::QueueNotReady);
809        };
810        let mut memory = crate::AddressSpaceMemory::new(&*self.accessor);
811        let result = desc_table
812            .follow_chain(head_index, &mut memory)
813            .map(|descriptors| descriptors.len() >= min_length);
814        if result.is_err() {
815            self.latch_fault();
816        }
817        result
818    }
819
820    /// Get data buffers from descriptor chain
821    ///
822    /// Returns [`VirtioError::QueueNotReady`] when the descriptor table is not
823    /// configured and [`VirtioError::QueueFaulted`] when the queue is faulted.
824    /// Any guest-memory failure on a configured queue latches the fault.
825    pub fn get_data_buffers(
826        &self,
827        head_index: u16,
828        device_type: VirtioDeviceID,
829    ) -> VirtioResult<Vec<(GuestPhysAddr, usize, bool)>> {
830        if self.faulted.load(Ordering::Acquire) {
831            return Err(VirtioError::QueueFaulted);
832        }
833        let Some(ref desc_table) = self.desc_table else {
834            return Err(VirtioError::QueueNotReady);
835        };
836        let mut memory = crate::AddressSpaceMemory::new(&*self.accessor);
837        let result = desc_table.get_data_buffers(head_index, device_type, &mut memory);
838        if result.is_err() {
839            self.latch_fault();
840        }
841        result
842    }
843
844    /// Get status address from descriptor chain
845    ///
846    /// Returns [`VirtioError::QueueNotReady`] when the descriptor table is not
847    /// configured and [`VirtioError::QueueFaulted`] when the queue is faulted.
848    /// Any guest-memory failure on a configured queue latches the fault.
849    pub fn get_status_addr(&self, head_index: u16) -> VirtioResult<GuestPhysAddr> {
850        if self.faulted.load(Ordering::Acquire) {
851            return Err(VirtioError::QueueFaulted);
852        }
853        let Some(ref desc_table) = self.desc_table else {
854            return Err(VirtioError::QueueNotReady);
855        };
856        let mut memory = crate::AddressSpaceMemory::new(&*self.accessor);
857        let result = desc_table.get_status_addr(head_index, &mut memory);
858        if result.is_err() {
859            self.latch_fault();
860        }
861        result
862    }
863
864    /// Whether the device should interrupt the driver after updating the used ring.
865    ///
866    /// Per the VirtIO specification the device honors the *available* ring's
867    /// `VIRTQ_AVAIL_F_NO_INTERRUPT` flag. The used ring's `VIRTQ_USED_F_NO_NOTIFY`
868    /// flag is the opposite direction (the driver reads it to decide whether to
869    /// kick the device), so it must not gate device-to-driver interrupts.
870    ///
871    /// Returns [`VirtioError::QueueFaulted`] when the queue is faulted,
872    /// [`VirtioError::QueueNotReady`] when the available ring is not
873    /// configured (not a runtime failure, so the queue is not faulted), and
874    /// latches the fault on a read failure of a configured ring.
875    pub fn should_notify(&self) -> VirtioResult<bool> {
876        if self.faulted.load(Ordering::Acquire) {
877            return Err(VirtioError::QueueFaulted);
878        }
879        let Some(ref avail_ring) = self.avail_ring else {
880            return Err(VirtioError::QueueNotReady);
881        };
882        mb();
883        let result = if self.event_idx_enabled {
884            avail_ring.read_used_event().map(|event| {
885                event_idx_should_notify(
886                    event,
887                    self.next_used,
888                    self.notification_old_used.load(Ordering::Acquire),
889                )
890            })
891        } else {
892            avail_ring
893                .interrupts_suppressed()
894                .map(|suppressed| !suppressed)
895        };
896        if result.is_err() {
897            self.latch_fault();
898        } else {
899            self.notification_old_used
900                .store(self.next_used, Ordering::Release);
901        }
902        result
903    }
904
905    /// Write status byte to the status buffer of a descriptor chain
906    ///
907    /// This method writes the status byte to the last descriptor in the chain,
908    /// which should be a write-only descriptor according to VirtIO specification.
909    ///
910    /// Returns [`VirtioError::QueueNotReady`] when the descriptor table is not
911    /// configured and [`VirtioError::QueueFaulted`] when the queue is faulted;
912    /// a faulted queue never writes guest memory.
913    pub fn write_status_byte(&self, head_index: u16, status: u8) -> VirtioResult<()> {
914        if self.faulted.load(Ordering::Acquire) {
915            return Err(VirtioError::QueueFaulted);
916        }
917        // Get the status descriptor address (last descriptor in chain)
918        let status_addr_guest = self.get_status_addr(head_index)?;
919
920        trace!(
921            "Writing status byte {} to guest address 0x{:x} for descriptor chain {}",
922            status,
923            status_addr_guest.as_usize(),
924            head_index
925        );
926
927        // Write the status byte to guest memory using the new memory access interface
928        self.accessor
929            .write_obj(status_addr_guest, status)
930            .map_err(|_| VirtioError::InvalidAddress)?;
931
932        Ok(())
933    }
934}
935
936const fn event_idx_should_notify(event: u16, new: u16, old: u16) -> bool {
937    new.wrapping_sub(event).wrapping_sub(1) < new.wrapping_sub(old)
938}
939
940#[cfg(test)]
941mod event_idx_tests {
942    use super::event_idx_should_notify;
943
944    #[test]
945    fn notification_formula_handles_used_index_wraparound() {
946        assert!(event_idx_should_notify(u16::MAX, 0, u16::MAX));
947    }
948}
949
950/// One half-open guest region `[base, base + size)` used by ring-layout
951/// validation. Deliberately uses `usize` arithmetic like the rest of the queue
952/// layer so the checks match the arithmetic actually performed on ring
953/// accesses.
954#[derive(Clone, Copy)]
955struct RingRegion {
956    base: GuestPhysAddr,
957    size: usize,
958}
959
960impl RingRegion {
961    fn new(base: GuestPhysAddr, size: usize) -> Self {
962        Self { base, size }
963    }
964
965    /// The exclusive end address, or `None` if `base + size` overflows the
966    /// guest address space.
967    fn end(&self) -> Option<usize> {
968        self.base.as_usize().checked_add(self.size)
969    }
970
971    /// Whether the two regions share any byte.
972    ///
973    /// A region whose end overflows the address space is unbounded; treating
974    /// it as non-overlapping would let a wrap-around ring alias the memory of
975    /// a neighbouring ring, so an overflowing region always overlaps.
976    fn overlaps(&self, other: &Self) -> bool {
977        let Some(self_end) = self.end() else {
978            return true;
979        };
980        let Some(other_end) = other.end() else {
981            return true;
982        };
983        self.base.as_usize() < other_end && other.base.as_usize() < self_end
984    }
985}
986
987#[cfg(test)]
988mod ready_preparation_tests {
989    use super::*;
990    use crate::{GuestMemory, NoGuestMemoryAccessor};
991
992    struct UnmappedMemory;
993
994    impl GuestMemory for UnmappedMemory {
995        fn read(&mut self, _guest_addr: GuestPhysAddr, _data: &mut [u8]) -> VirtioResult<()> {
996            Err(VirtioError::InvalidAddress)
997        }
998
999        fn write(&mut self, _guest_addr: GuestPhysAddr, _data: &[u8]) -> VirtioResult<()> {
1000            Err(VirtioError::InvalidAddress)
1001        }
1002    }
1003
1004    #[test]
1005    fn rejected_ready_preparation_preserves_layout_warning_latch() {
1006        let mut queue = VirtioQueue::new(0, 4, Arc::new(NoGuestMemoryAccessor));
1007        queue
1008            .set_desc_table_addr(GuestPhysAddr::from(0x1000))
1009            .unwrap();
1010        queue
1011            .set_avail_ring_addr(GuestPhysAddr::from(0x2000))
1012            .unwrap();
1013        queue
1014            .set_used_ring_addr(GuestPhysAddr::from(0x3000))
1015            .unwrap();
1016
1017        let first_snapshot = queue.begin_ready_preparation().unwrap();
1018        assert_eq!(
1019            first_snapshot.validate_layout_with_memory(&mut UnmappedMemory),
1020            Err(VirtioError::InvalidRingLayout)
1021        );
1022        queue.finish_ready_preparation(&first_snapshot, false);
1023
1024        let second_snapshot = queue.begin_ready_preparation().unwrap();
1025        assert!(
1026            second_snapshot.layout_warn_emitted.load(Ordering::Acquire),
1027            "a repeated QUEUE_READY attempt must inherit the warning latch"
1028        );
1029    }
1030}