axvirtio_common/queue/
used.rs

1use crate::constants::*;
2use crate::error::{VirtioError, VirtioResult};
3use alloc::sync::Arc;
4use axaddrspace::{GuestMemoryAccessor, GuestPhysAddr};
5
6/// VirtIO used ring element structure.
7///
8/// This structure represents the memory layout of a single element in the
9/// used ring array according to the VirtIO specification. Each element
10/// records information about a completed descriptor chain.
11///
12/// This structure is used by `UsedRing` to read/write individual used
13/// elements in guest memory through the guest memory accessor.
14#[repr(C)]
15#[derive(Debug, Clone, Copy)]
16pub struct VirtqUsedElem {
17    /// Index of start of used descriptor chain
18    pub id: u32,
19    /// Total length of the descriptor chain which was used
20    pub len: u32,
21}
22
23impl VirtqUsedElem {
24    /// Create a new used element
25    pub fn new(id: u32, len: u32) -> Self {
26        Self { id, len }
27    }
28}
29
30/// VirtIO used ring header structure.
31///
32/// This structure represents the memory layout of the used ring header
33/// in guest memory according to the VirtIO specification. It is a simple
34/// C-compatible data structure that directly maps to guest memory.
35///
36/// The complete used ring in guest memory consists of:
37/// 1. This header structure (VirtQueueUsed)
38/// 2. An array of used elements (ring[\queue_size], each VirtqUsedElem)
39/// 3. An optional avail_event field (if VIRTIO_F_EVENT_IDX is negotiated)
40///
41/// This structure is used by `UsedRing` to read/write the header portion
42/// of the used ring through guest memory accessor.
43#[repr(C)]
44#[derive(Debug, Clone, Copy, Default)]
45pub struct VirtQueueUsed {
46    /// Flags
47    pub flags: u16,
48    /// Index of the next used element
49    pub idx: u16,
50    // Ring of used elements (variable length)
51}
52
53impl VirtQueueUsed {
54    /// Create a new used ring header
55    pub fn new() -> Self {
56        Self { flags: 0, idx: 0 }
57    }
58
59    /// Check if notifications are disabled
60    pub fn no_notify(&self) -> bool {
61        (self.flags & VIRTQ_USED_F_NO_NOTIFY) != 0
62    }
63
64    /// Set the no notify flag
65    pub fn set_no_notify(&mut self, no_notify: bool) {
66        if no_notify {
67            self.flags |= VIRTQ_USED_F_NO_NOTIFY;
68        } else {
69            self.flags &= !VIRTQ_USED_F_NO_NOTIFY;
70        }
71    }
72}
73
74/// Used ring management structure.
75///
76/// This structure provides a high-level interface for managing the VirtIO
77/// used ring in guest memory. It wraps the guest memory accessor and
78/// provides methods to read/write various parts of the used ring:
79/// - The header (VirtQueueUsed structure)
80/// - The ring array of used elements (VirtqUsedElem structures)
81/// - The avail_event field (if VIRTIO_F_EVENT_IDX is negotiated)
82///
83/// Relationship with VirtQueueUsed and VirtqUsedElem:
84/// - VirtQueueUsed defines the memory layout of the used ring header
85/// - VirtqUsedElem defines the memory layout of each ring element
86/// - UsedRing uses both structures to access the complete used ring in guest memory
87/// - UsedRing manages the entire used ring structure and provides high-level operations
88///
89/// Memory Layout:
90/// ```text
91/// base_addr -> +-------------------+
92///              | VirtQueueUsed     |  (flags + idx)
93///              +-------------------+
94///              | ring[0]           |  (VirtqUsedElem: id + len)
95///              | ring[1]           |  (VirtqUsedElem: id + len)
96///              | ...               |
97///              | ring[queue_size-1]|  (VirtqUsedElem: id + len)
98///              +-------------------+
99///              | avail_event       |  (optional, if event_idx enabled)
100///              +-------------------+
101/// ```
102#[derive(Debug, Clone)]
103pub struct UsedRing<T: GuestMemoryAccessor + Clone> {
104    /// Base address of the used ring
105    pub base_addr: GuestPhysAddr,
106    /// Queue size
107    pub size: u16,
108    /// Current used index
109    pub used_idx: u16,
110    /// Guest memory accessor
111    accessor: Arc<T>,
112}
113
114impl<T: GuestMemoryAccessor + Clone> UsedRing<T> {
115    /// Create a new used ring
116    pub fn new(base_addr: GuestPhysAddr, size: u16, accessor: Arc<T>) -> Self {
117        Self {
118            base_addr,
119            size,
120            used_idx: 0,
121            accessor,
122        }
123    }
124
125    /// Get the address of the used ring header
126    pub fn header_addr(&self) -> GuestPhysAddr {
127        self.base_addr
128    }
129
130    /// Get the address of the ring array
131    pub fn ring_addr(&self) -> GuestPhysAddr {
132        self.base_addr + core::mem::size_of::<VirtQueueUsed>()
133    }
134
135    /// Get the address of a specific ring entry
136    pub fn ring_entry_addr(&self, index: u16) -> Option<GuestPhysAddr> {
137        if index >= self.size {
138            return None;
139        }
140
141        let offset = core::mem::size_of::<VirtQueueUsed>()
142            + (index as usize * core::mem::size_of::<VirtqUsedElem>());
143        Some(self.base_addr + offset)
144    }
145
146    /// Get the address of the available event field (if event_idx is enabled)
147    pub fn avail_event_addr(&self) -> GuestPhysAddr {
148        let offset = core::mem::size_of::<VirtQueueUsed>()
149            + (self.size as usize * core::mem::size_of::<VirtqUsedElem>());
150        self.base_addr + offset
151    }
152
153    /// Calculate the total size of the used ring
154    pub fn total_size(&self) -> usize {
155        core::mem::size_of::<VirtQueueUsed>()
156            + (self.size as usize * core::mem::size_of::<VirtqUsedElem>())
157            + 2
158    }
159
160    /// Check if the used ring is valid
161    pub fn is_valid(&self) -> bool {
162        self.base_addr.as_usize() != 0 && self.size > 0
163    }
164
165    /// Add a used element to the ring
166    pub fn add_used(&mut self, id: u32, len: u32) -> VirtioResult<()> {
167        if !self.is_valid() {
168            return Err(VirtioError::QueueNotReady);
169        }
170
171        // Calculate the address of the used element to write
172        let ring_index = self.used_idx % self.size;
173        let elem_addr = self
174            .ring_entry_addr(ring_index)
175            .ok_or(VirtioError::InvalidQueue)?;
176
177        // Create the used element
178        let used_elem = VirtqUsedElem::new(id, len);
179
180        // Write the used element to guest memory using injected memory accessor
181        self.accessor
182            .write_obj(elem_addr, used_elem)
183            .map_err(|_| VirtioError::InvalidAddress)?;
184
185        // Update the used index
186        self.used_idx = self.used_idx.wrapping_add(1);
187
188        // Update the used ring header index
189        self.write_used_idx()?;
190
191        Ok(())
192    }
193
194    /// Write the used index to the used ring header
195    pub fn write_used_idx(&self) -> VirtioResult<()> {
196        if !self.is_valid() {
197            return Err(VirtioError::QueueNotReady);
198        }
199
200        // Write the used index to the header (offset 2 bytes for flags)
201        let idx_addr = self.base_addr + 2;
202        self.accessor
203            .write_obj(idx_addr, self.used_idx)
204            .map_err(|_| VirtioError::InvalidAddress)?;
205
206        Ok(())
207    }
208
209    /// Read the used ring header
210    pub fn read_used_header(&self) -> VirtioResult<VirtQueueUsed> {
211        if !self.is_valid() {
212            return Err(VirtioError::QueueNotReady);
213        }
214
215        self.accessor
216            .read_obj(self.base_addr)
217            .map_err(|_| VirtioError::InvalidAddress)
218    }
219
220    /// Write the used ring header
221    pub fn write_used_header(&self, header: &VirtQueueUsed) -> VirtioResult<()> {
222        if !self.is_valid() {
223            return Err(VirtioError::QueueNotReady);
224        }
225
226        self.accessor
227            .write_obj(self.base_addr, *header)
228            .map_err(|_| VirtioError::InvalidAddress)
229    }
230
231    /// Get the current used index
232    pub fn get_used_idx(&self) -> u16 {
233        self.used_idx
234    }
235
236    /// Set the used index
237    pub fn set_used_idx(&mut self, idx: u16) {
238        self.used_idx = idx;
239    }
240
241    /// Check if notifications should be suppressed
242    pub fn should_notify(&self) -> VirtioResult<bool> {
243        if !self.is_valid() {
244            return Err(VirtioError::QueueNotReady);
245        }
246
247        let header = self.read_used_header()?;
248        Ok(!header.no_notify())
249    }
250
251    /// Set notification suppression
252    pub fn set_notification(&self, suppress: bool) -> VirtioResult<()> {
253        if !self.is_valid() {
254            return Err(VirtioError::QueueNotReady);
255        }
256
257        let mut header = self.read_used_header()?;
258        header.set_no_notify(suppress);
259        self.write_used_header(&header)?;
260
261        Ok(())
262    }
263}