axvirtio_common/queue/used.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 used ring element structure.
13///
14/// This structure represents the memory layout of a single element in the
15/// used ring array according to the VirtIO specification. Each element
16/// records information about a completed descriptor chain.
17///
18/// This structure is used by `UsedRing` to read/write individual used
19/// elements in guest memory through the guest memory accessor.
20#[repr(C)]
21#[derive(Debug, Clone, Copy)]
22pub struct VirtqUsedElem {
23 /// Index of start of used descriptor chain
24 pub id: u32,
25 /// Total length of the descriptor chain which was used
26 pub len: u32,
27}
28
29impl VirtqUsedElem {
30 /// Create a new used element
31 pub fn new(id: u32, len: u32) -> Self {
32 Self { id, len }
33 }
34}
35
36/// VirtIO used ring header structure.
37///
38/// This structure represents the memory layout of the used ring header
39/// in guest memory according to the VirtIO specification. It is a simple
40/// C-compatible data structure that directly maps to guest memory.
41///
42/// The complete used ring in guest memory consists of:
43/// 1. This header structure (VirtQueueUsed)
44/// 2. An array of used elements (ring[\queue_size], each VirtqUsedElem)
45/// 3. An optional avail_event field (if VIRTIO_F_EVENT_IDX is negotiated)
46///
47/// This structure is used by `UsedRing` to read/write the header portion
48/// of the used ring through guest memory accessor.
49#[repr(C)]
50#[derive(Debug, Clone, Copy, Default)]
51pub struct VirtQueueUsed {
52 /// Flags
53 pub flags: u16,
54 /// Index of the next used element
55 pub idx: u16,
56 // Ring of used elements (variable length)
57}
58
59impl VirtQueueUsed {
60 /// Create a new used ring header
61 pub fn new() -> Self {
62 Self { flags: 0, idx: 0 }
63 }
64
65 /// Check if notifications are disabled
66 pub fn no_notify(&self) -> bool {
67 (self.flags & VIRTQ_USED_F_NO_NOTIFY) != 0
68 }
69
70 /// Set the no notify flag
71 pub fn set_no_notify(&mut self, no_notify: bool) {
72 if no_notify {
73 self.flags |= VIRTQ_USED_F_NO_NOTIFY;
74 } else {
75 self.flags &= !VIRTQ_USED_F_NO_NOTIFY;
76 }
77 }
78}
79
80/// Used ring management structure.
81///
82/// This structure provides a high-level interface for managing the VirtIO
83/// used ring in guest memory. It wraps the guest memory accessor and
84/// provides methods to read/write various parts of the used ring:
85/// - The header (VirtQueueUsed structure)
86/// - The ring array of used elements (VirtqUsedElem structures)
87/// - The avail_event field (if VIRTIO_F_EVENT_IDX is negotiated)
88///
89/// Relationship with VirtQueueUsed and VirtqUsedElem:
90/// - VirtQueueUsed defines the memory layout of the used ring header
91/// - VirtqUsedElem defines the memory layout of each ring element
92/// - UsedRing uses both structures to access the complete used ring in guest memory
93/// - UsedRing manages the entire used ring structure and provides high-level operations
94///
95/// Memory Layout:
96/// ```text
97/// base_addr -> +-------------------+
98/// | VirtQueueUsed | (flags + idx)
99/// +-------------------+
100/// | ring[0] | (VirtqUsedElem: id + len)
101/// | ring[1] | (VirtqUsedElem: id + len)
102/// | ... |
103/// | ring[queue_size-1]| (VirtqUsedElem: id + len)
104/// +-------------------+
105/// | avail_event | (optional, if event_idx enabled)
106/// +-------------------+
107/// ```
108#[derive(Debug, Clone)]
109pub struct UsedRing<T: GuestMemoryAccessor + Clone> {
110 /// Base address of the used ring
111 pub base_addr: GuestPhysAddr,
112 /// Queue size
113 pub size: u16,
114 /// Current used index
115 pub used_idx: u16,
116 /// Guest memory accessor
117 accessor: Arc<T>,
118}
119
120impl<T: GuestMemoryAccessor + Clone> UsedRing<T> {
121 /// Create a new used ring
122 pub fn new(base_addr: GuestPhysAddr, size: u16, accessor: Arc<T>) -> Self {
123 Self {
124 base_addr,
125 size,
126 used_idx: 0,
127 accessor,
128 }
129 }
130
131 /// Get the address of the used ring header
132 pub fn header_addr(&self) -> GuestPhysAddr {
133 self.base_addr
134 }
135
136 /// Get the address of the ring array
137 pub fn ring_addr(&self) -> GuestPhysAddr {
138 self.base_addr + core::mem::size_of::<VirtQueueUsed>()
139 }
140
141 /// Get the address of a specific ring entry
142 pub fn ring_entry_addr(&self, index: u16) -> Option<GuestPhysAddr> {
143 if index >= self.size {
144 return None;
145 }
146
147 let offset = core::mem::size_of::<VirtQueueUsed>()
148 + (index as usize * core::mem::size_of::<VirtqUsedElem>());
149 Some(self.base_addr + offset)
150 }
151
152 /// Get the address of the available event field (if event_idx is enabled)
153 pub fn avail_event_addr(&self) -> GuestPhysAddr {
154 // Header + ring array fill the region up to 2 bytes before its end;
155 // the `avail_event` footer is always part of the region (see
156 // `layout_size`).
157 self.base_addr + Self::layout_size(self.size) - 2
158 }
159
160 /// Size in bytes of the complete used ring for a queue of `size` entries,
161 /// including the trailing 2-byte `avail_event` field.
162 ///
163 /// The footer is always counted: a driver that negotiated
164 /// `VIRTIO_F_RING_EVENT_IDX` reads `avail_event` from there, and layout
165 /// validation must not depend on negotiation state.
166 pub(crate) const fn layout_size(size: u16) -> usize {
167 core::mem::size_of::<VirtQueueUsed>()
168 + (size as usize) * core::mem::size_of::<VirtqUsedElem>()
169 + 2
170 }
171
172 /// The size in bytes this ring occupies in guest memory, always including
173 /// the trailing 2-byte event-index footer (see
174 /// [`layout_size`](Self::layout_size)).
175 pub fn total_size(&self) -> usize {
176 Self::layout_size(self.size)
177 }
178
179 /// Check if the used ring is valid
180 pub fn is_valid(&self) -> bool {
181 self.base_addr.as_usize() != 0 && self.size > 0
182 }
183
184 /// Add a used element to the ring
185 pub fn add_used(&mut self, id: u32, len: u32) -> VirtioResult<()> {
186 let accessor = self.accessor.clone();
187 let mut memory = crate::AddressSpaceMemory::new(&*accessor);
188 self.add_used_with_memory(id, len, &mut memory)
189 }
190
191 /// Adds a used element with a scoped memory capability.
192 pub fn add_used_with_memory(
193 &mut self,
194 id: u32,
195 len: u32,
196 memory: &mut dyn GuestMemory,
197 ) -> VirtioResult<()> {
198 if !self.is_valid() {
199 return Err(VirtioError::QueueNotReady);
200 }
201
202 // Calculate the address of the used element to write
203 let ring_index = self.used_idx % self.size;
204 let elem_addr = self
205 .ring_entry_addr(ring_index)
206 .ok_or(VirtioError::InvalidQueue)?;
207
208 // Create the used element
209 let used_elem = VirtqUsedElem::new(id, len);
210
211 // Write the used element to guest memory using injected memory accessor
212 let mut bytes = [0u8; 8];
213 bytes[0..4].copy_from_slice(&used_elem.id.to_le_bytes());
214 bytes[4..8].copy_from_slice(&used_elem.len.to_le_bytes());
215 memory.write(elem_addr, &bytes)?;
216
217 // Update the used index
218 self.used_idx = self.used_idx.wrapping_add(1);
219
220 // Update the used ring header index
221 self.write_used_idx_with_memory(memory)?;
222
223 Ok(())
224 }
225
226 /// Write the used index to the used ring header
227 pub fn write_used_idx(&self) -> VirtioResult<()> {
228 let mut memory = crate::AddressSpaceMemory::new(&*self.accessor);
229 self.write_used_idx_with_memory(&mut memory)
230 }
231
232 /// Writes the used index with a scoped memory capability.
233 pub fn write_used_idx_with_memory(&self, memory: &mut dyn GuestMemory) -> VirtioResult<()> {
234 if !self.is_valid() {
235 return Err(VirtioError::QueueNotReady);
236 }
237
238 // Write the used index to the header (offset 2 bytes for flags)
239 let idx_addr = self.base_addr + 2;
240 memory.write(idx_addr, &self.used_idx.to_le_bytes())?;
241
242 Ok(())
243 }
244
245 /// Read the used ring header
246 pub fn read_used_header(&self) -> VirtioResult<VirtQueueUsed> {
247 if !self.is_valid() {
248 return Err(VirtioError::QueueNotReady);
249 }
250
251 self.accessor
252 .read_obj(self.base_addr)
253 .map_err(|_| VirtioError::InvalidAddress)
254 }
255
256 /// Write the used ring header
257 pub fn write_used_header(&self, header: &VirtQueueUsed) -> VirtioResult<()> {
258 if !self.is_valid() {
259 return Err(VirtioError::QueueNotReady);
260 }
261
262 self.accessor
263 .write_obj(self.base_addr, *header)
264 .map_err(|_| VirtioError::InvalidAddress)
265 }
266
267 /// Get the current used index
268 pub fn get_used_idx(&self) -> u16 {
269 self.used_idx
270 }
271
272 /// Set the used index
273 pub fn set_used_idx(&mut self, idx: u16) {
274 self.used_idx = idx;
275 }
276
277 /// Check if notifications should be suppressed
278 pub fn should_notify(&self) -> VirtioResult<bool> {
279 if !self.is_valid() {
280 return Err(VirtioError::QueueNotReady);
281 }
282
283 let header = self.read_used_header()?;
284 Ok(!header.no_notify())
285 }
286
287 /// Set notification suppression
288 pub fn set_notification(&self, suppress: bool) -> VirtioResult<()> {
289 if !self.is_valid() {
290 return Err(VirtioError::QueueNotReady);
291 }
292
293 let mut header = self.read_used_header()?;
294 header.set_no_notify(suppress);
295 self.write_used_header(&header)?;
296
297 Ok(())
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304 use crate::NoGuestMemoryAccessor;
305
306 #[test]
307 fn layout_size_counts_header_elements_and_footer() {
308 // header (4) + 4 elements * 8 bytes + avail_event footer (2) = 38.
309 assert_eq!(UsedRing::<NoGuestMemoryAccessor>::layout_size(4), 38);
310 // Boundary: the largest queue size still counts the footer.
311 assert_eq!(
312 UsedRing::<NoGuestMemoryAccessor>::layout_size(256),
313 4 + 256 * 8 + 2
314 );
315 }
316}