Skip to main content

axvirtio_common/queue/
available.rs

1use alloc::sync::Arc;
2
3use axaddrspace::GuestMemoryAccessor;
4use axvm_types::GuestPhysAddr;
5
6use crate::{
7    constants::*,
8    error::{VirtioError, VirtioResult},
9    memory::GuestMemory,
10};
11
12/// VirtIO available ring header structure.
13///
14/// This structure represents the memory layout of the available ring header
15/// in guest memory according to the VirtIO specification. It is a simple
16/// C-compatible data structure that directly maps to guest memory.
17///
18/// The complete available ring in guest memory consists of:
19/// 1. This header structure (VirtQueueAvail)
20/// 2. An array of descriptor indices (ring[\queue_size])
21/// 3. An optional used_event field (if VIRTIO_F_EVENT_IDX is negotiated)
22///
23/// This structure is used by `AvailableRing` to read/write the header portion
24/// of the available ring through guest memory accessor.
25#[repr(C)]
26#[derive(Debug, Clone, Copy, Default)]
27pub struct VirtQueueAvail {
28    /// Flags
29    pub flags: u16,
30    /// Index of the next available descriptor
31    pub idx: u16,
32}
33
34impl VirtQueueAvail {
35    /// Create a new available ring header
36    pub fn new() -> Self {
37        Self { flags: 0, idx: 0 }
38    }
39
40    /// Check if interrupts are disabled
41    pub fn no_interrupt(&self) -> bool {
42        (self.flags & VIRTQ_AVAIL_F_NO_INTERRUPT) != 0
43    }
44
45    /// Set the no interrupt flag
46    pub fn set_no_interrupt(&mut self, no_interrupt: bool) {
47        if no_interrupt {
48            self.flags |= VIRTQ_AVAIL_F_NO_INTERRUPT;
49        } else {
50            self.flags &= !VIRTQ_AVAIL_F_NO_INTERRUPT;
51        }
52    }
53}
54
55/// Available ring management structure.
56///
57/// This structure provides a high-level interface for managing the VirtIO
58/// available ring in guest memory. It wraps the guest memory accessor and
59/// provides methods to read/write various parts of the available ring:
60/// - The header (VirtQueueAvail structure)
61/// - The ring array of descriptor indices
62/// - The used_event field (if VIRTIO_F_EVENT_IDX is negotiated)
63///
64/// Relationship with VirtQueueAvail:
65/// - VirtQueueAvail defines the memory layout of the available ring header
66/// - AvailableRing uses VirtQueueAvail to access the header in guest memory
67/// - AvailableRing manages the entire available ring structure, not just the header
68///
69/// Memory Layout:
70/// ```text
71/// base_addr -> +-------------------+
72///              | VirtQueueAvail    |  (flags + idx)
73///              +-------------------+
74///              | ring[0]           |  (descriptor index)
75///              | ring[1]           |
76///              | ...               |
77///              | ring[queue_size-1]|
78///              +-------------------+
79///              | used_event        |  (optional, if event_idx enabled)
80///              +-------------------+
81/// ```
82#[derive(Debug, Clone)]
83pub struct AvailableRing<T: GuestMemoryAccessor + Clone> {
84    /// Base address of the available ring
85    pub base_addr: GuestPhysAddr,
86    /// Queue size
87    pub size: u16,
88    /// Last seen available index
89    pub last_avail_idx: u16,
90    /// Guest memory accessor
91    accessor: Arc<T>,
92}
93
94impl<T: GuestMemoryAccessor + Clone> AvailableRing<T> {
95    /// Create a new available ring
96    pub fn new(base_addr: GuestPhysAddr, size: u16, accessor: Arc<T>) -> Self {
97        Self {
98            base_addr,
99            size,
100            last_avail_idx: 0,
101            accessor,
102        }
103    }
104
105    /// Get the address of the available ring header
106    pub fn header_addr(&self) -> GuestPhysAddr {
107        self.base_addr
108    }
109
110    /// Get the address of the ring array
111    pub fn ring_addr(&self) -> GuestPhysAddr {
112        self.base_addr + core::mem::size_of::<VirtQueueAvail>()
113    }
114
115    /// Get the address of a specific ring entry
116    pub fn ring_entry_addr(&self, index: u16) -> Option<GuestPhysAddr> {
117        if index >= self.size {
118            return None;
119        }
120
121        let offset = core::mem::size_of::<VirtQueueAvail>() + (index as usize * 2);
122        Some(self.base_addr + offset)
123    }
124
125    /// Get the address of the used event field (if event_idx is enabled)
126    pub fn used_event_addr(&self) -> GuestPhysAddr {
127        // Header + ring array fill the region up to 2 bytes before its end;
128        // the `used_event` footer is always part of the region (see
129        // `layout_size`).
130        self.base_addr + Self::layout_size(self.size) - 2
131    }
132
133    /// Size in bytes of the complete available ring for a queue of `size`
134    /// entries, including the trailing 2-byte `used_event` field.
135    ///
136    /// The footer is always counted: a driver that negotiated
137    /// `VIRTIO_F_RING_EVENT_IDX` writes `used_event` there, and layout
138    /// validation must not depend on negotiation state.
139    pub(crate) const fn layout_size(size: u16) -> usize {
140        core::mem::size_of::<VirtQueueAvail>() + (size as usize) * 2 + 2
141    }
142
143    /// The size in bytes this ring occupies in guest memory, always including
144    /// the trailing 2-byte event-index footer (see
145    /// [`layout_size`](Self::layout_size)).
146    pub fn total_size(&self) -> usize {
147        Self::layout_size(self.size)
148    }
149
150    /// Check if the available ring is valid
151    pub fn is_valid(&self) -> bool {
152        self.base_addr.as_usize() != 0 && self.size > 0
153    }
154
155    /// Check if there are new available descriptors
156    pub fn has_new_avail(&self, current_idx: u16) -> bool {
157        current_idx != self.last_avail_idx
158    }
159
160    /// Update the last seen available index
161    pub fn update_last_avail_idx(&mut self, idx: u16) {
162        self.last_avail_idx = idx;
163    }
164
165    /// Read the available ring header
166    pub fn read_avail_header(&self) -> VirtioResult<VirtQueueAvail> {
167        if !self.is_valid() {
168            return Err(VirtioError::QueueNotReady);
169        }
170
171        self.accessor
172            .read_obj(self.base_addr)
173            .map_err(|_| VirtioError::InvalidAddress)
174    }
175
176    /// Write the available ring header
177    pub fn write_avail_header(&self, header: &VirtQueueAvail) -> VirtioResult<()> {
178        if !self.is_valid() {
179            return Err(VirtioError::QueueNotReady);
180        }
181
182        self.accessor
183            .write_obj(self.base_addr, header)
184            .map_err(|_| VirtioError::InvalidAddress)
185    }
186
187    /// Read the current available index from guest memory
188    pub fn read_avail_idx(&self) -> VirtioResult<u16> {
189        let mut memory = crate::AddressSpaceMemory::new(&*self.accessor);
190        self.read_avail_idx_with_memory(&mut memory)
191    }
192
193    /// Reads the available index with a scoped memory capability.
194    pub fn read_avail_idx_with_memory(&self, memory: &mut dyn GuestMemory) -> VirtioResult<u16> {
195        if !self.is_valid() {
196            return Err(VirtioError::QueueNotReady);
197        }
198
199        // Read the idx field from the header (offset 2 bytes for flags)
200        let idx_addr = self.base_addr + 2;
201        let mut bytes = [0u8; 2];
202        memory.read(idx_addr, &mut bytes)?;
203        Ok(u16::from_le_bytes(bytes))
204    }
205
206    /// Get the available index for external access
207    pub fn get_avail_idx(&self) -> VirtioResult<u16> {
208        self.read_avail_idx()
209    }
210
211    /// Read a descriptor index from the available ring
212    pub fn read_avail_ring_entry(&self, ring_index: u16) -> VirtioResult<u16> {
213        let mut memory = crate::AddressSpaceMemory::new(&*self.accessor);
214        self.read_avail_ring_entry_with_memory(ring_index, &mut memory)
215    }
216
217    /// Reads one available-ring entry with a scoped memory capability.
218    pub fn read_avail_ring_entry_with_memory(
219        &self,
220        ring_index: u16,
221        memory: &mut dyn GuestMemory,
222    ) -> VirtioResult<u16> {
223        if !self.is_valid() {
224            return Err(VirtioError::QueueNotReady);
225        }
226
227        let entry_addr = self
228            .ring_entry_addr(ring_index % self.size)
229            .ok_or(VirtioError::InvalidQueue)?;
230
231        let mut bytes = [0u8; 2];
232        memory.read(entry_addr, &mut bytes)?;
233        Ok(u16::from_le_bytes(bytes))
234    }
235
236    /// Write a descriptor index to the available ring
237    pub fn write_avail_ring_entry(&self, ring_index: u16, desc_index: u16) -> VirtioResult<()> {
238        if !self.is_valid() {
239            return Err(VirtioError::QueueNotReady);
240        }
241
242        let entry_addr = self
243            .ring_entry_addr(ring_index % self.size)
244            .ok_or(VirtioError::InvalidQueue)?;
245
246        self.accessor
247            .write_obj(entry_addr, desc_index)
248            .map_err(|_| VirtioError::InvalidAddress)?;
249
250        Ok(())
251    }
252
253    /// Get the number of available descriptors since last check
254    pub fn get_available_count(&self) -> VirtioResult<u16> {
255        let current_idx = self.read_avail_idx()?;
256        Ok(current_idx.wrapping_sub(self.last_avail_idx))
257    }
258
259    /// Check if interrupts are suppressed
260    pub fn interrupts_suppressed(&self) -> VirtioResult<bool> {
261        let header = self.read_avail_header()?;
262        Ok(header.no_interrupt())
263    }
264
265    /// Checks interrupt suppression with a scoped memory capability.
266    pub fn interrupts_suppressed_with_memory(
267        &self,
268        memory: &mut dyn GuestMemory,
269    ) -> VirtioResult<bool> {
270        if !self.is_valid() {
271            return Err(VirtioError::QueueNotReady);
272        }
273        let mut bytes = [0u8; 2];
274        memory.read(self.base_addr, &mut bytes)?;
275        Ok(u16::from_le_bytes(bytes) & VIRTQ_AVAIL_F_NO_INTERRUPT != 0)
276    }
277
278    /// Set interrupt suppression
279    pub fn set_interrupt_suppression(&self, suppress: bool) -> VirtioResult<()> {
280        let mut header = self.read_avail_header()?;
281        header.set_no_interrupt(suppress);
282        self.write_avail_header(&header)?;
283        Ok(())
284    }
285
286    /// Read the used event field (for event_idx feature)
287    pub fn read_used_event(&self) -> VirtioResult<u16> {
288        if !self.is_valid() {
289            return Err(VirtioError::QueueNotReady);
290        }
291
292        let event_addr = self.used_event_addr();
293        self.accessor
294            .read_obj(event_addr)
295            .map_err(|_| VirtioError::InvalidAddress)
296    }
297
298    /// Reads the used event field with a scoped memory capability.
299    pub(crate) fn read_used_event_with_memory(
300        &self,
301        memory: &mut dyn GuestMemory,
302    ) -> VirtioResult<u16> {
303        if !self.is_valid() {
304            return Err(VirtioError::QueueNotReady);
305        }
306        let mut bytes = [0u8; 2];
307        memory.read(self.used_event_addr(), &mut bytes)?;
308        Ok(u16::from_le_bytes(bytes))
309    }
310
311    /// Write the used event field (for event_idx feature)
312    pub fn write_used_event(&self, event: u16) -> VirtioResult<()> {
313        if !self.is_valid() {
314            return Err(VirtioError::QueueNotReady);
315        }
316
317        let event_addr = self.used_event_addr();
318        self.accessor
319            .write_obj(event_addr, event)
320            .map_err(|_| VirtioError::InvalidAddress)
321    }
322}
323
324#[cfg(test)]
325mod tests {
326    use super::*;
327    use crate::NoGuestMemoryAccessor;
328
329    #[test]
330    fn layout_size_counts_header_entries_and_footer() {
331        // header (4) + 4 entries * 2 bytes + used_event footer (2) = 14.
332        assert_eq!(AvailableRing::<NoGuestMemoryAccessor>::layout_size(4), 14);
333        // Boundary: the largest queue size still counts the footer.
334        assert_eq!(
335            AvailableRing::<NoGuestMemoryAccessor>::layout_size(256),
336            4 + 256 * 2 + 2
337        );
338    }
339}