1use std::sync::atomic::{Ordering, fence};
9
10pub struct GuestMemoryVirtQueue {
16 queue_idx: u16,
18 size: u16,
20 desc_gpa: u64,
22 avail_gpa: u64,
24 used_gpa: u64,
26 ram_base: *mut u8,
28 ram_size: usize,
30 last_avail_idx: u16,
32 used_idx: u16,
34 event_idx: bool,
36}
37
38unsafe impl Send for GuestMemoryVirtQueue {}
43unsafe impl Sync for GuestMemoryVirtQueue {}
44
45#[derive(Debug, Clone, Copy)]
47pub struct VirtqDesc {
48 pub addr: u64,
50 pub len: u32,
52 pub flags: u16,
54 pub next: u16,
56}
57
58impl VirtqDesc {
59 pub fn is_write(&self) -> bool {
61 self.flags & crate::queue::flags::WRITE != 0
62 }
63
64 pub fn has_next(&self) -> bool {
66 self.flags & crate::queue::flags::NEXT != 0
67 }
68}
69
70pub struct DescriptorChain {
72 pub head_idx: u16,
74 pub descriptors: Vec<VirtqDesc>,
76}
77
78impl GuestMemoryVirtQueue {
79 pub unsafe fn new(
86 queue_idx: u16,
87 size: u16,
88 desc_gpa: u64,
89 avail_gpa: u64,
90 used_gpa: u64,
91 ram_base: *mut u8,
92 ram_size: usize,
93 ) -> Self {
94 Self {
95 queue_idx,
96 size,
97 desc_gpa,
98 avail_gpa,
99 used_gpa,
100 ram_base,
101 ram_size,
102 last_avail_idx: 0,
103 used_idx: 0,
104 event_idx: false,
105 }
106 }
107
108 pub fn set_event_idx(&mut self, enabled: bool) {
110 self.event_idx = enabled;
111 }
112
113 pub fn queue_idx(&self) -> u16 {
115 self.queue_idx
116 }
117
118 fn gpa_to_ptr(&self, gpa: u64) -> Option<*mut u8> {
120 let offset = gpa as usize;
121 if offset < self.ram_size {
122 Some(unsafe { self.ram_base.add(offset) })
123 } else {
124 None
125 }
126 }
127
128 fn read_u16(&self, gpa: u64) -> Option<u16> {
130 let ptr = self.gpa_to_ptr(gpa)?;
131 if gpa as usize + 2 > self.ram_size {
132 return None;
133 }
134 Some(unsafe { (ptr as *const u16).read_unaligned() })
136 }
137
138 fn read_u32(&self, gpa: u64) -> Option<u32> {
140 let ptr = self.gpa_to_ptr(gpa)?;
141 if gpa as usize + 4 > self.ram_size {
142 return None;
143 }
144 Some(unsafe { (ptr as *const u32).read_unaligned() })
145 }
146
147 fn read_u64(&self, gpa: u64) -> Option<u64> {
149 let ptr = self.gpa_to_ptr(gpa)?;
150 if gpa as usize + 8 > self.ram_size {
151 return None;
152 }
153 Some(unsafe { (ptr as *const u64).read_unaligned() })
154 }
155
156 fn write_u16(&self, gpa: u64, val: u16) -> bool {
158 if let Some(ptr) = self.gpa_to_ptr(gpa) {
159 if gpa as usize + 2 <= self.ram_size {
160 unsafe { (ptr as *mut u16).write_unaligned(val) };
161 return true;
162 }
163 }
164 false
165 }
166
167 fn write_u32(&self, gpa: u64, val: u32) -> bool {
169 if let Some(ptr) = self.gpa_to_ptr(gpa) {
170 if gpa as usize + 4 <= self.ram_size {
171 unsafe { (ptr as *mut u32).write_unaligned(val) };
172 return true;
173 }
174 }
175 false
176 }
177
178 fn read_descriptor(&self, idx: u16) -> Option<VirtqDesc> {
180 if idx >= self.size {
181 return None;
182 }
183 let desc_offset = self.desc_gpa + u64::from(idx) * 16;
185 Some(VirtqDesc {
186 addr: self.read_u64(desc_offset)?,
187 len: self.read_u32(desc_offset + 8)?,
188 flags: self.read_u16(desc_offset + 12)?,
189 next: self.read_u16(desc_offset + 14)?,
190 })
191 }
192
193 fn avail_idx(&self) -> u16 {
195 self.read_u16(self.avail_gpa + 2).unwrap_or(0)
197 }
198
199 fn avail_ring_entry(&self, pos: u16) -> u16 {
201 let offset = self.avail_gpa + 4 + u64::from(pos % self.size) * 2;
202 self.read_u16(offset).unwrap_or(0)
203 }
204
205 pub fn has_avail(&self) -> bool {
207 fence(Ordering::Acquire);
208 self.avail_idx() != self.last_avail_idx
209 }
210
211 pub fn pop_avail(&mut self) -> Option<DescriptorChain> {
213 fence(Ordering::Acquire);
214
215 let avail_idx = self.avail_idx();
216 if avail_idx == self.last_avail_idx {
217 return None;
218 }
219
220 let head_idx = self.avail_ring_entry(self.last_avail_idx);
221 self.last_avail_idx = self.last_avail_idx.wrapping_add(1);
222
223 let mut descriptors = Vec::new();
225 let mut idx = head_idx;
226 let mut count = 0u16;
227
228 loop {
229 if count >= self.size {
230 tracing::warn!("Descriptor chain loop detected in queue {}", self.queue_idx);
231 break;
232 }
233
234 let desc = self.read_descriptor(idx)?;
235 descriptors.push(desc);
236 count += 1;
237
238 if !desc.has_next() {
239 break;
240 }
241 idx = desc.next;
242 }
243
244 Some(DescriptorChain {
245 head_idx,
246 descriptors,
247 })
248 }
249
250 pub fn push_used(&mut self, head_idx: u16, len: u32) {
252 let used_ring_offset = self.used_gpa + 4 + u64::from(self.used_idx % self.size) * 8;
254 self.write_u32(used_ring_offset, u32::from(head_idx));
255 self.write_u32(used_ring_offset + 4, len);
256
257 self.used_idx = self.used_idx.wrapping_add(1);
258
259 fence(Ordering::Release);
260 self.write_u16(self.used_gpa + 2, self.used_idx);
262 }
263
264 pub fn push_used_batch(&mut self, completions: &[(u16, u32)]) {
266 for &(head_idx, len) in completions {
267 let used_ring_offset = self.used_gpa + 4 + u64::from(self.used_idx % self.size) * 8;
268 self.write_u32(used_ring_offset, u32::from(head_idx));
269 self.write_u32(used_ring_offset + 4, len);
270 self.used_idx = self.used_idx.wrapping_add(1);
271 }
272
273 fence(Ordering::Release);
274 self.write_u16(self.used_gpa + 2, self.used_idx);
275 }
276
277 pub fn read_buffer(&self, gpa: u64, len: u32) -> Option<Vec<u8>> {
279 let len = len as usize;
280 let ptr = self.gpa_to_ptr(gpa)?;
281 if gpa as usize + len > self.ram_size {
282 return None;
283 }
284 let mut buf = vec![0u8; len];
285 unsafe {
286 std::ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), len);
287 }
288 Some(buf)
289 }
290
291 pub fn write_buffer(&self, gpa: u64, data: &[u8]) -> bool {
293 if let Some(ptr) = self.gpa_to_ptr(gpa) {
294 if gpa as usize + data.len() <= self.ram_size {
295 unsafe {
296 std::ptr::copy_nonoverlapping(data.as_ptr(), ptr, data.len());
297 }
298 return true;
299 }
300 }
301 false
302 }
303
304 pub unsafe fn guest_slice(&self, gpa: u64, len: usize) -> Option<&[u8]> {
312 let ptr = self.gpa_to_ptr(gpa)?;
313 if gpa as usize + len > self.ram_size {
314 return None;
315 }
316 Some(unsafe { std::slice::from_raw_parts(ptr, len) })
318 }
319
320 pub unsafe fn guest_slice_mut(&mut self, gpa: u64, len: usize) -> Option<&mut [u8]> {
326 let ptr = self.gpa_to_ptr(gpa)?;
327 if gpa as usize + len > self.ram_size {
328 return None;
329 }
330 Some(unsafe { std::slice::from_raw_parts_mut(ptr, len) })
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338 use std::alloc::{Layout, alloc_zeroed, dealloc};
339
340 const TEST_RAM_SIZE: usize = 64 * 1024;
342 const TEST_QUEUE_SIZE: u16 = 16;
344
345 const DESC_TABLE_GPA: u64 = 0x1000;
348 const AVAIL_RING_GPA: u64 = 0x2000;
350 const USED_RING_GPA: u64 = 0x3000;
352 const DATA_BUFFER_GPA: u64 = 0x4000;
354
355 struct TestGuestMemory {
357 ptr: *mut u8,
358 layout: Layout,
359 }
360
361 impl TestGuestMemory {
362 fn new() -> Self {
363 let layout = Layout::from_size_align(TEST_RAM_SIZE, 4096).unwrap();
364 let ptr = unsafe { alloc_zeroed(layout) };
365 assert!(!ptr.is_null(), "Failed to allocate test guest memory");
366 Self { ptr, layout }
367 }
368
369 fn as_mut_ptr(&self) -> *mut u8 {
370 self.ptr
371 }
372
373 fn write_u16(&self, gpa: u64, val: u16) {
375 let offset = gpa as usize;
376 assert!(offset + 2 <= TEST_RAM_SIZE);
377 unsafe {
378 (self.ptr.add(offset) as *mut u16).write_unaligned(val);
379 }
380 }
381
382 fn write_u32(&self, gpa: u64, val: u32) {
384 let offset = gpa as usize;
385 assert!(offset + 4 <= TEST_RAM_SIZE);
386 unsafe {
387 (self.ptr.add(offset) as *mut u32).write_unaligned(val);
388 }
389 }
390
391 fn write_u64(&self, gpa: u64, val: u64) {
393 let offset = gpa as usize;
394 assert!(offset + 8 <= TEST_RAM_SIZE);
395 unsafe {
396 (self.ptr.add(offset) as *mut u64).write_unaligned(val);
397 }
398 }
399
400 fn read_u16(&self, gpa: u64) -> u16 {
402 let offset = gpa as usize;
403 assert!(offset + 2 <= TEST_RAM_SIZE);
404 unsafe { (self.ptr.add(offset) as *const u16).read_unaligned() }
405 }
406
407 fn read_u32(&self, gpa: u64) -> u32 {
409 let offset = gpa as usize;
410 assert!(offset + 4 <= TEST_RAM_SIZE);
411 unsafe { (self.ptr.add(offset) as *const u32).read_unaligned() }
412 }
413
414 fn write_descriptor(&self, idx: u16, addr: u64, len: u32, flags: u16, next: u16) {
416 let base = DESC_TABLE_GPA + u64::from(idx) * 16;
417 self.write_u64(base, addr);
418 self.write_u32(base + 8, len);
419 self.write_u16(base + 12, flags);
420 self.write_u16(base + 14, next);
421 }
422
423 fn set_avail_idx(&self, idx: u16) {
425 self.write_u16(AVAIL_RING_GPA + 2, idx);
427 }
428
429 fn set_avail_ring_entry(&self, pos: u16, desc_idx: u16) {
431 let offset = AVAIL_RING_GPA + 4 + u64::from(pos % TEST_QUEUE_SIZE) * 2;
432 self.write_u16(offset, desc_idx);
433 }
434
435 fn write_bytes(&self, gpa: u64, data: &[u8]) {
437 let offset = gpa as usize;
438 assert!(offset + data.len() <= TEST_RAM_SIZE);
439 unsafe {
440 std::ptr::copy_nonoverlapping(data.as_ptr(), self.ptr.add(offset), data.len());
441 }
442 }
443
444 fn read_bytes(&self, gpa: u64, len: usize) -> Vec<u8> {
446 let offset = gpa as usize;
447 assert!(offset + len <= TEST_RAM_SIZE);
448 let mut buf = vec![0u8; len];
449 unsafe {
450 std::ptr::copy_nonoverlapping(self.ptr.add(offset), buf.as_mut_ptr(), len);
451 }
452 buf
453 }
454 }
455
456 impl Drop for TestGuestMemory {
457 fn drop(&mut self) {
458 unsafe {
459 dealloc(self.ptr, self.layout);
460 }
461 }
462 }
463
464 fn create_test_queue(mem: &TestGuestMemory) -> GuestMemoryVirtQueue {
466 unsafe {
467 GuestMemoryVirtQueue::new(
468 0,
469 TEST_QUEUE_SIZE,
470 DESC_TABLE_GPA,
471 AVAIL_RING_GPA,
472 USED_RING_GPA,
473 mem.as_mut_ptr(),
474 TEST_RAM_SIZE,
475 )
476 }
477 }
478
479 #[test]
484 fn test_single_descriptor_chain() {
485 let mem = TestGuestMemory::new();
486
487 mem.write_descriptor(0, DATA_BUFFER_GPA, 256, 0, 0);
489
490 mem.set_avail_ring_entry(0, 0);
492 mem.set_avail_idx(1);
493
494 let mut queue = create_test_queue(&mem);
495
496 assert!(queue.has_avail());
497 let chain = queue.pop_avail().unwrap();
498 assert_eq!(chain.head_idx, 0);
499 assert_eq!(chain.descriptors.len(), 1);
500 assert_eq!(chain.descriptors[0].addr, DATA_BUFFER_GPA);
501 assert_eq!(chain.descriptors[0].len, 256);
502 assert!(!chain.descriptors[0].has_next());
503 }
504
505 #[test]
506 fn test_chained_descriptors() {
507 let mem = TestGuestMemory::new();
508
509 let next_flag = crate::queue::flags::NEXT;
511 mem.write_descriptor(0, DATA_BUFFER_GPA, 128, next_flag, 1);
512 mem.write_descriptor(1, DATA_BUFFER_GPA + 128, 256, next_flag, 2);
513 mem.write_descriptor(2, DATA_BUFFER_GPA + 384, 512, 0, 0);
514
515 mem.set_avail_ring_entry(0, 0);
516 mem.set_avail_idx(1);
517
518 let mut queue = create_test_queue(&mem);
519 let chain = queue.pop_avail().unwrap();
520
521 assert_eq!(chain.head_idx, 0);
522 assert_eq!(chain.descriptors.len(), 3);
523 assert_eq!(chain.descriptors[0].addr, DATA_BUFFER_GPA);
524 assert_eq!(chain.descriptors[0].len, 128);
525 assert!(chain.descriptors[0].has_next());
526 assert_eq!(chain.descriptors[1].addr, DATA_BUFFER_GPA + 128);
527 assert_eq!(chain.descriptors[1].len, 256);
528 assert!(chain.descriptors[1].has_next());
529 assert_eq!(chain.descriptors[2].addr, DATA_BUFFER_GPA + 384);
530 assert_eq!(chain.descriptors[2].len, 512);
531 assert!(!chain.descriptors[2].has_next());
532 }
533
534 #[test]
535 fn test_write_descriptor_flag() {
536 let mem = TestGuestMemory::new();
537
538 let write_flag = crate::queue::flags::WRITE;
540 mem.write_descriptor(0, DATA_BUFFER_GPA, 1024, write_flag, 0);
541
542 mem.set_avail_ring_entry(0, 0);
543 mem.set_avail_idx(1);
544
545 let mut queue = create_test_queue(&mem);
546 let chain = queue.pop_avail().unwrap();
547
548 assert!(chain.descriptors[0].is_write());
549 assert!(!chain.descriptors[0].has_next());
550 }
551
552 #[test]
557 fn test_pop_avail_empty() {
558 let mem = TestGuestMemory::new();
559 let mut queue = create_test_queue(&mem);
560
561 assert!(!queue.has_avail());
563 assert!(queue.pop_avail().is_none());
564 }
565
566 #[test]
567 fn test_pop_avail_multiple() {
568 let mem = TestGuestMemory::new();
569
570 mem.write_descriptor(0, DATA_BUFFER_GPA, 100, 0, 0);
572 mem.write_descriptor(1, DATA_BUFFER_GPA + 0x100, 200, 0, 0);
573 mem.write_descriptor(2, DATA_BUFFER_GPA + 0x200, 300, 0, 0);
574
575 mem.set_avail_ring_entry(0, 0);
576 mem.set_avail_ring_entry(1, 1);
577 mem.set_avail_ring_entry(2, 2);
578 mem.set_avail_idx(3);
579
580 let mut queue = create_test_queue(&mem);
581
582 for i in 0..3 {
584 assert!(queue.has_avail());
585 let chain = queue.pop_avail().unwrap();
586 assert_eq!(chain.head_idx, i);
587 assert_eq!(chain.descriptors.len(), 1);
588 }
589
590 assert!(!queue.has_avail());
592 assert!(queue.pop_avail().is_none());
593 }
594
595 #[test]
596 fn test_push_used_single() {
597 let mem = TestGuestMemory::new();
598 let mut queue = create_test_queue(&mem);
599
600 queue.push_used(5, 1024);
601
602 let used_idx = mem.read_u16(USED_RING_GPA + 2);
604 assert_eq!(used_idx, 1);
605
606 let used_id = mem.read_u32(USED_RING_GPA + 4);
608 let used_len = mem.read_u32(USED_RING_GPA + 8);
609 assert_eq!(used_id, 5);
610 assert_eq!(used_len, 1024);
611 }
612
613 #[test]
614 fn test_push_used_wrapping() {
615 let mem = TestGuestMemory::new();
616 let mut queue = create_test_queue(&mem);
617
618 for i in 0..TEST_QUEUE_SIZE + 2 {
620 queue.push_used(i, i as u32 * 100);
621 }
622
623 let used_idx = mem.read_u16(USED_RING_GPA + 2);
624 assert_eq!(used_idx, TEST_QUEUE_SIZE + 2);
625 }
626
627 #[test]
628 fn test_pop_and_push_roundtrip() {
629 let mem = TestGuestMemory::new();
630
631 mem.write_descriptor(3, DATA_BUFFER_GPA, 512, 0, 0);
633 mem.set_avail_ring_entry(0, 3);
634 mem.set_avail_idx(1);
635
636 let mut queue = create_test_queue(&mem);
637
638 let chain = queue.pop_avail().unwrap();
640 assert_eq!(chain.head_idx, 3);
641
642 queue.push_used(chain.head_idx, 512);
644
645 let used_idx = mem.read_u16(USED_RING_GPA + 2);
647 assert_eq!(used_idx, 1);
648 let used_id = mem.read_u32(USED_RING_GPA + 4);
649 assert_eq!(used_id, 3);
650 }
651
652 #[test]
657 fn test_push_used_batch() {
658 let mem = TestGuestMemory::new();
659 let mut queue = create_test_queue(&mem);
660
661 let completions = [(0, 100), (1, 200), (2, 300)];
662 queue.push_used_batch(&completions);
663
664 let used_idx = mem.read_u16(USED_RING_GPA + 2);
666 assert_eq!(used_idx, 3);
667
668 for (i, &(id, len)) in completions.iter().enumerate() {
670 let entry_offset = USED_RING_GPA + 4 + (i as u64) * 8;
671 let entry_id = mem.read_u32(entry_offset);
672 let entry_len = mem.read_u32(entry_offset + 4);
673 assert_eq!(entry_id, u32::from(id));
674 assert_eq!(entry_len, len);
675 }
676 }
677
678 #[test]
679 fn test_push_used_batch_empty() {
680 let mem = TestGuestMemory::new();
681 let mut queue = create_test_queue(&mem);
682
683 queue.push_used_batch(&[]);
684
685 let used_idx = mem.read_u16(USED_RING_GPA + 2);
687 assert_eq!(used_idx, 0);
688 }
689
690 #[test]
695 fn test_gpa_out_of_bounds_read_buffer() {
696 let mem = TestGuestMemory::new();
697 let queue = create_test_queue(&mem);
698
699 let result = queue.read_buffer(TEST_RAM_SIZE as u64, 100);
701 assert!(result.is_none());
702 }
703
704 #[test]
705 fn test_gpa_out_of_bounds_write_buffer() {
706 let mem = TestGuestMemory::new();
707 let queue = create_test_queue(&mem);
708
709 let data = [0xAA; 100];
711 let result = queue.write_buffer(TEST_RAM_SIZE as u64, &data);
712 assert!(!result);
713 }
714
715 #[test]
716 fn test_gpa_partial_out_of_bounds() {
717 let mem = TestGuestMemory::new();
718 let queue = create_test_queue(&mem);
719
720 let gpa = (TEST_RAM_SIZE - 10) as u64;
722 let result = queue.read_buffer(gpa, 100);
723 assert!(result.is_none());
724 }
725
726 #[test]
727 fn test_guest_slice_out_of_bounds() {
728 let mem = TestGuestMemory::new();
729 let mut queue = create_test_queue(&mem);
730
731 let result = unsafe { queue.guest_slice(TEST_RAM_SIZE as u64, 1) };
732 assert!(result.is_none());
733
734 let result = unsafe { queue.guest_slice_mut(TEST_RAM_SIZE as u64, 1) };
735 assert!(result.is_none());
736 }
737
738 #[test]
743 fn test_read_buffer() {
744 let mem = TestGuestMemory::new();
745 let queue = create_test_queue(&mem);
746
747 let test_data = b"Hello, VirtIO!";
749 mem.write_bytes(DATA_BUFFER_GPA, test_data);
750
751 let result = queue.read_buffer(DATA_BUFFER_GPA, test_data.len() as u32);
753 assert!(result.is_some());
754 assert_eq!(result.unwrap(), test_data);
755 }
756
757 #[test]
758 fn test_write_buffer() {
759 let mem = TestGuestMemory::new();
760 let queue = create_test_queue(&mem);
761
762 let test_data = b"Device response";
764 let success = queue.write_buffer(DATA_BUFFER_GPA, test_data);
765 assert!(success);
766
767 let readback = mem.read_bytes(DATA_BUFFER_GPA, test_data.len());
769 assert_eq!(readback, test_data);
770 }
771
772 #[test]
773 fn test_read_write_buffer_roundtrip() {
774 let mem = TestGuestMemory::new();
775 let queue = create_test_queue(&mem);
776
777 let original = vec![0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE];
779 let success = queue.write_buffer(DATA_BUFFER_GPA, &original);
780 assert!(success);
781
782 let readback = queue.read_buffer(DATA_BUFFER_GPA, original.len() as u32);
784 assert_eq!(readback.unwrap(), original);
785 }
786
787 #[test]
788 fn test_guest_slice_read() {
789 let mem = TestGuestMemory::new();
790 let queue = create_test_queue(&mem);
791
792 let test_data = b"zero-copy read";
793 mem.write_bytes(DATA_BUFFER_GPA, test_data);
794
795 let slice = unsafe { queue.guest_slice(DATA_BUFFER_GPA, test_data.len()) };
796 assert!(slice.is_some());
797 assert_eq!(slice.unwrap(), test_data);
798 }
799
800 #[test]
801 fn test_guest_slice_mut_write() {
802 let mem = TestGuestMemory::new();
803 let mut queue = create_test_queue(&mem);
804
805 let slice = unsafe { queue.guest_slice_mut(DATA_BUFFER_GPA, 5) };
806 assert!(slice.is_some());
807 let slice = slice.unwrap();
808 slice.copy_from_slice(b"ABCDE");
809
810 let readback = mem.read_bytes(DATA_BUFFER_GPA, 5);
812 assert_eq!(readback, b"ABCDE");
813 }
814
815 #[test]
820 fn test_queue_idx_accessor() {
821 let mem = TestGuestMemory::new();
822 let queue = unsafe {
823 GuestMemoryVirtQueue::new(
824 7,
825 TEST_QUEUE_SIZE,
826 DESC_TABLE_GPA,
827 AVAIL_RING_GPA,
828 USED_RING_GPA,
829 mem.as_mut_ptr(),
830 TEST_RAM_SIZE,
831 )
832 };
833 assert_eq!(queue.queue_idx(), 7);
834 }
835
836 #[test]
837 fn test_event_idx_toggle() {
838 let mem = TestGuestMemory::new();
839 let mut queue = create_test_queue(&mem);
840
841 assert!(!queue.event_idx);
842 queue.set_event_idx(true);
843 assert!(queue.event_idx);
844 queue.set_event_idx(false);
845 assert!(!queue.event_idx);
846 }
847
848 #[test]
853 fn test_descriptor_chain_loop_terminates() {
854 let mem = TestGuestMemory::new();
855
856 let next_flag = crate::queue::flags::NEXT;
858 mem.write_descriptor(0, DATA_BUFFER_GPA, 64, next_flag, 1);
859 mem.write_descriptor(1, DATA_BUFFER_GPA + 64, 64, next_flag, 0);
860
861 mem.set_avail_ring_entry(0, 0);
862 mem.set_avail_idx(1);
863
864 let mut queue = create_test_queue(&mem);
865 let chain = queue.pop_avail().unwrap();
866
867 assert!(chain.descriptors.len() <= TEST_QUEUE_SIZE as usize);
869 }
870}