axvirtio_common/queue/
used.rs1use alloc::sync::Arc;
2
3use axaddrspace::GuestMemoryAccessor;
4use axvm_types::GuestPhysAddr;
5use mbarrier::mb;
6
7use crate::{
8 constants::*,
9 error::{VirtioError, VirtioResult},
10 memory::GuestMemory,
11};
12
13#[repr(C)]
22#[derive(Debug, Clone, Copy)]
23pub struct VirtqUsedElem {
24 pub id: u32,
26 pub len: u32,
28}
29
30impl VirtqUsedElem {
31 pub fn new(id: u32, len: u32) -> Self {
33 Self { id, len }
34 }
35}
36
37#[repr(C)]
51#[derive(Debug, Clone, Copy, Default)]
52pub struct VirtQueueUsed {
53 pub flags: u16,
55 pub idx: u16,
57 }
59
60impl VirtQueueUsed {
61 pub fn new() -> Self {
63 Self { flags: 0, idx: 0 }
64 }
65
66 pub fn no_notify(&self) -> bool {
68 (self.flags & VIRTQ_USED_F_NO_NOTIFY) != 0
69 }
70
71 pub fn set_no_notify(&mut self, no_notify: bool) {
73 if no_notify {
74 self.flags |= VIRTQ_USED_F_NO_NOTIFY;
75 } else {
76 self.flags &= !VIRTQ_USED_F_NO_NOTIFY;
77 }
78 }
79}
80
81#[derive(Debug, Clone)]
110pub struct UsedRing<T: GuestMemoryAccessor + Clone> {
111 pub base_addr: GuestPhysAddr,
113 pub size: u16,
115 pub used_idx: u16,
117 accessor: Arc<T>,
119}
120
121impl<T: GuestMemoryAccessor + Clone> UsedRing<T> {
122 pub fn new(base_addr: GuestPhysAddr, size: u16, accessor: Arc<T>) -> Self {
124 Self {
125 base_addr,
126 size,
127 used_idx: 0,
128 accessor,
129 }
130 }
131
132 pub fn header_addr(&self) -> GuestPhysAddr {
134 self.base_addr
135 }
136
137 pub fn ring_addr(&self) -> GuestPhysAddr {
139 self.base_addr + core::mem::size_of::<VirtQueueUsed>()
140 }
141
142 pub fn ring_entry_addr(&self, index: u16) -> Option<GuestPhysAddr> {
144 if index >= self.size {
145 return None;
146 }
147
148 let offset = core::mem::size_of::<VirtQueueUsed>()
149 + (index as usize * core::mem::size_of::<VirtqUsedElem>());
150 Some(self.base_addr + offset)
151 }
152
153 pub fn avail_event_addr(&self) -> GuestPhysAddr {
155 self.base_addr + Self::layout_size(self.size) - 2
159 }
160
161 pub(crate) const fn layout_size(size: u16) -> usize {
168 core::mem::size_of::<VirtQueueUsed>()
169 + (size as usize) * core::mem::size_of::<VirtqUsedElem>()
170 + 2
171 }
172
173 pub fn total_size(&self) -> usize {
177 Self::layout_size(self.size)
178 }
179
180 pub fn is_valid(&self) -> bool {
182 self.base_addr.as_usize() != 0 && self.size > 0
183 }
184
185 pub fn add_used(&mut self, id: u32, len: u32) -> VirtioResult<()> {
187 let accessor = self.accessor.clone();
188 let mut memory = crate::AddressSpaceMemory::new(&*accessor);
189 self.add_used_with_memory(id, len, &mut memory)
190 }
191
192 pub fn add_used_with_memory(
194 &mut self,
195 id: u32,
196 len: u32,
197 memory: &mut dyn GuestMemory,
198 ) -> VirtioResult<()> {
199 self.add_used_with_memory_and_barrier(id, len, memory, mb)
200 }
201
202 fn add_used_with_memory_and_barrier(
203 &mut self,
204 id: u32,
205 len: u32,
206 memory: &mut dyn GuestMemory,
207 barrier: impl FnOnce(),
208 ) -> VirtioResult<()> {
209 if !self.is_valid() {
210 return Err(VirtioError::QueueNotReady);
211 }
212
213 let ring_index = self.used_idx % self.size;
215 let elem_addr = self
216 .ring_entry_addr(ring_index)
217 .ok_or(VirtioError::InvalidQueue)?;
218
219 let used_elem = VirtqUsedElem::new(id, len);
221
222 let mut bytes = [0u8; 8];
224 bytes[0..4].copy_from_slice(&used_elem.id.to_le_bytes());
225 bytes[4..8].copy_from_slice(&used_elem.len.to_le_bytes());
226 memory.write(elem_addr, &bytes)?;
227
228 self.used_idx = self.used_idx.wrapping_add(1);
230
231 barrier();
233
234 self.write_used_idx_with_memory(memory)?;
236
237 Ok(())
238 }
239
240 pub fn write_used_idx(&self) -> VirtioResult<()> {
242 let mut memory = crate::AddressSpaceMemory::new(&*self.accessor);
243 self.write_used_idx_with_memory(&mut memory)
244 }
245
246 pub fn write_used_idx_with_memory(&self, memory: &mut dyn GuestMemory) -> VirtioResult<()> {
248 if !self.is_valid() {
249 return Err(VirtioError::QueueNotReady);
250 }
251
252 let idx_addr = self.base_addr + 2;
254 memory.write(idx_addr, &self.used_idx.to_le_bytes())?;
255
256 Ok(())
257 }
258
259 pub fn read_used_header(&self) -> VirtioResult<VirtQueueUsed> {
261 if !self.is_valid() {
262 return Err(VirtioError::QueueNotReady);
263 }
264
265 self.accessor
266 .read_obj(self.base_addr)
267 .map_err(|_| VirtioError::InvalidAddress)
268 }
269
270 pub fn write_used_header(&self, header: &VirtQueueUsed) -> VirtioResult<()> {
272 if !self.is_valid() {
273 return Err(VirtioError::QueueNotReady);
274 }
275
276 self.accessor
277 .write_obj(self.base_addr, *header)
278 .map_err(|_| VirtioError::InvalidAddress)
279 }
280
281 pub fn get_used_idx(&self) -> u16 {
283 self.used_idx
284 }
285
286 pub fn set_used_idx(&mut self, idx: u16) {
288 self.used_idx = idx;
289 }
290
291 pub fn should_notify(&self) -> VirtioResult<bool> {
293 if !self.is_valid() {
294 return Err(VirtioError::QueueNotReady);
295 }
296
297 let header = self.read_used_header()?;
298 Ok(!header.no_notify())
299 }
300
301 pub fn set_notification(&self, suppress: bool) -> VirtioResult<()> {
303 if !self.is_valid() {
304 return Err(VirtioError::QueueNotReady);
305 }
306
307 let mut header = self.read_used_header()?;
308 header.set_no_notify(suppress);
309 self.write_used_header(&header)?;
310
311 Ok(())
312 }
313
314 pub(crate) fn set_notification_with_memory(
316 &self,
317 suppress: bool,
318 memory: &mut dyn GuestMemory,
319 ) -> VirtioResult<()> {
320 if !self.is_valid() {
321 return Err(VirtioError::QueueNotReady);
322 }
323 let flags = if suppress { VIRTQ_USED_F_NO_NOTIFY } else { 0 };
324 memory.write(self.base_addr, &flags.to_le_bytes())
325 }
326
327 pub(crate) fn write_avail_event_with_memory(
329 &self,
330 event: u16,
331 memory: &mut dyn GuestMemory,
332 ) -> VirtioResult<()> {
333 if !self.is_valid() {
334 return Err(VirtioError::QueueNotReady);
335 }
336 memory.write(self.avail_event_addr(), &event.to_le_bytes())
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use alloc::{rc::Rc, vec::Vec};
343 use core::cell::RefCell;
344
345 use axvm_types::GuestPhysAddr;
346
347 use super::*;
348 use crate::{GuestMemory, NoGuestMemoryAccessor};
349
350 struct RecordingMemory {
351 events: Rc<RefCell<Vec<&'static str>>>,
352 }
353
354 impl GuestMemory for RecordingMemory {
355 fn read(&mut self, _: GuestPhysAddr, _: &mut [u8]) -> VirtioResult<()> {
356 Err(VirtioError::InvalidAddress)
357 }
358
359 fn write(&mut self, address: GuestPhysAddr, _: &[u8]) -> VirtioResult<()> {
360 self.events
361 .borrow_mut()
362 .push(if address.as_usize() == 0x1002 {
363 "used_idx"
364 } else {
365 "used_elem"
366 });
367 Ok(())
368 }
369 }
370
371 #[test]
372 fn publishes_used_element_before_used_index() {
373 let events = Rc::new(RefCell::new(Vec::new()));
374 let mut memory = RecordingMemory {
375 events: events.clone(),
376 };
377 let mut ring = UsedRing::new(
378 GuestPhysAddr::from(0x1000),
379 1,
380 alloc::sync::Arc::new(NoGuestMemoryAccessor),
381 );
382
383 ring.add_used_with_memory_and_barrier(7, 11, &mut memory, || {
384 events.borrow_mut().push("barrier");
385 })
386 .unwrap();
387
388 assert_eq!(&*events.borrow(), &["used_elem", "barrier", "used_idx"]);
389 }
390
391 #[test]
392 fn layout_size_counts_header_elements_and_footer() {
393 assert_eq!(UsedRing::<NoGuestMemoryAccessor>::layout_size(4), 38);
395 assert_eq!(
397 UsedRing::<NoGuestMemoryAccessor>::layout_size(256),
398 4 + 256 * 8 + 2
399 );
400 }
401}