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}