1use alloc::vec::Vec;
2
3use axvm_types::GuestPhysAddr;
4
5use crate::{
6 VirtioDeviceID,
7 constants::*,
8 error::{VirtioError, VirtioResult},
9 memory::GuestMemory,
10};
11
12#[repr(C)]
25#[derive(Debug, Clone, Copy)]
26pub struct VirtQueueDesc {
27 pub base_addr: GuestPhysAddr,
29 pub len: u32,
31 pub flags: u16,
33 pub next: u16,
35}
36
37impl VirtQueueDesc {
38 pub fn new(base_addr: GuestPhysAddr, len: u32, flags: u16, next: u16) -> Self {
40 Self {
41 base_addr,
42 len,
43 flags,
44 next,
45 }
46 }
47
48 pub fn has_next(&self) -> bool {
50 (self.flags & VIRTQ_DESC_F_NEXT) != 0
51 }
52
53 pub fn is_write(&self) -> bool {
55 (self.flags & VIRTQ_DESC_F_WRITE) != 0
56 }
57
58 pub fn is_indirect(&self) -> bool {
60 (self.flags & VIRTQ_DESC_F_INDIRECT) != 0
61 }
62
63 pub fn guest_addr(&self) -> GuestPhysAddr {
65 self.base_addr
66 }
67
68 pub fn set_next(&mut self, has_next: bool) {
70 if has_next {
71 self.flags |= VIRTQ_DESC_F_NEXT;
72 } else {
73 self.flags &= !VIRTQ_DESC_F_NEXT;
74 }
75 }
76
77 pub fn set_write(&mut self, is_write: bool) {
79 if is_write {
80 self.flags |= VIRTQ_DESC_F_WRITE;
81 } else {
82 self.flags &= !VIRTQ_DESC_F_WRITE;
83 }
84 }
85
86 pub fn set_write_only(&mut self, is_write: bool) {
88 self.set_write(is_write);
89 }
90
91 pub fn is_write_only(&self) -> bool {
93 self.is_write()
94 }
95
96 pub fn set_indirect(&mut self, is_indirect: bool) {
98 if is_indirect {
99 self.flags |= VIRTQ_DESC_F_INDIRECT;
100 } else {
101 self.flags &= !VIRTQ_DESC_F_INDIRECT;
102 }
103 }
104}
105
106#[derive(Debug, Clone)]
116pub struct DescriptorChain {
117 head: u16,
119 descriptors: Vec<VirtQueueDesc>,
121}
122
123impl DescriptorChain {
124 pub fn new(head: u16, descriptors: Vec<VirtQueueDesc>) -> Self {
126 Self { head, descriptors }
127 }
128
129 pub fn head(&self) -> u16 {
131 self.head
132 }
133
134 pub fn descriptors(&self) -> &[VirtQueueDesc] {
136 &self.descriptors
137 }
138
139 pub fn len(&self) -> usize {
141 self.descriptors.len()
142 }
143
144 pub fn is_empty(&self) -> bool {
146 self.descriptors.is_empty()
147 }
148
149 pub fn readable(&self) -> impl Iterator<Item = &VirtQueueDesc> {
151 self.descriptors.iter().filter(|d| !d.is_write())
152 }
153
154 pub fn writable(&self) -> impl Iterator<Item = &VirtQueueDesc> {
156 self.descriptors.iter().filter(|d| d.is_write())
157 }
158
159 pub fn readable_len(&self) -> VirtioResult<usize> {
161 sum_descriptor_lens(self.readable().map(|d| d.len as usize))
162 }
163
164 pub fn writable_len(&self) -> VirtioResult<usize> {
166 sum_descriptor_lens(self.writable().map(|d| d.len as usize))
167 }
168}
169
170fn sum_descriptor_lens(lens: impl Iterator<Item = usize>) -> VirtioResult<usize> {
172 let mut total = 0usize;
173 for v in lens {
174 total = total.checked_add(v).ok_or(VirtioError::InvalidDescriptor)?;
175 }
176 Ok(total)
177}
178
179#[derive(Debug, Clone)]
208pub struct DescriptorTable {
209 pub base_addr: GuestPhysAddr,
211 pub size: u16,
213}
214
215impl DescriptorTable {
216 pub const fn new(base_addr: GuestPhysAddr, size: u16) -> Self {
218 Self { base_addr, size }
219 }
220
221 pub fn desc_addr(&self, index: u16) -> Option<GuestPhysAddr> {
223 if index >= self.size {
224 return None;
225 }
226
227 let offset = index as usize * core::mem::size_of::<VirtQueueDesc>();
228 Some(self.base_addr + offset)
229 }
230
231 pub(crate) const fn layout_size(size: u16) -> usize {
237 size as usize * core::mem::size_of::<VirtQueueDesc>()
238 }
239
240 pub fn total_size(&self) -> usize {
242 Self::layout_size(self.size)
243 }
244
245 pub fn is_valid(&self) -> bool {
247 self.base_addr.as_usize() != 0 && self.size > 0
248 }
249
250 pub fn read_desc(
252 &self,
253 index: u16,
254 memory: &mut dyn GuestMemory,
255 ) -> VirtioResult<VirtQueueDesc> {
256 if !self.is_valid() {
257 return Err(VirtioError::QueueNotReady);
258 }
259
260 let desc_addr = self.desc_addr(index).ok_or(VirtioError::InvalidQueue)?;
261
262 let mut bytes = [0u8; 16];
263 memory.read(desc_addr, &mut bytes)?;
264 Ok(VirtQueueDesc {
265 base_addr: GuestPhysAddr::from(
266 u64::from_le_bytes(bytes[0..8].try_into().unwrap()) as usize
267 ),
268 len: u32::from_le_bytes(bytes[8..12].try_into().unwrap()),
269 flags: u16::from_le_bytes(bytes[12..14].try_into().unwrap()),
270 next: u16::from_le_bytes(bytes[14..16].try_into().unwrap()),
271 })
272 }
273
274 pub fn write_desc(
276 &self,
277 index: u16,
278 desc: &VirtQueueDesc,
279 memory: &mut dyn GuestMemory,
280 ) -> VirtioResult<()> {
281 if !self.is_valid() {
282 return Err(VirtioError::QueueNotReady);
283 }
284
285 let desc_addr = self.desc_addr(index).ok_or(VirtioError::InvalidQueue)?;
286
287 let mut bytes = [0u8; 16];
288 bytes[0..8].copy_from_slice(&(desc.base_addr.as_usize() as u64).to_le_bytes());
289 bytes[8..12].copy_from_slice(&desc.len.to_le_bytes());
290 bytes[12..14].copy_from_slice(&desc.flags.to_le_bytes());
291 bytes[14..16].copy_from_slice(&desc.next.to_le_bytes());
292 memory.write(desc_addr, &bytes)?;
293
294 Ok(())
295 }
296
297 pub fn follow_chain(
299 &self,
300 head_index: u16,
301 memory: &mut dyn GuestMemory,
302 ) -> VirtioResult<Vec<VirtQueueDesc>> {
303 if !self.is_valid() {
304 return Err(VirtioError::QueueNotReady);
305 }
306
307 let mut descriptors = Vec::new();
308 let mut current_index = head_index;
309
310 loop {
311 if current_index >= self.size {
312 return Err(VirtioError::InvalidQueue);
313 }
314
315 let desc = self.read_desc(current_index, memory)?;
316 descriptors.push(desc);
317
318 if !desc.has_next() {
319 break;
320 }
321
322 current_index = desc.next;
323
324 if descriptors.len() > self.size as usize {
326 return Err(VirtioError::InvalidQueue);
327 }
328 }
329
330 Ok(descriptors)
331 }
332
333 pub fn descriptor_chain(
344 &self,
345 head: u16,
346 memory: &mut dyn GuestMemory,
347 ) -> VirtioResult<DescriptorChain> {
348 if !self.is_valid() {
349 return Err(VirtioError::QueueNotReady);
350 }
351 if head >= self.size {
352 return Err(VirtioError::InvalidDescriptor);
353 }
354
355 let mut descriptors = Vec::new();
356 let mut current = head;
357 loop {
358 if current >= self.size {
359 return Err(VirtioError::InvalidDescriptor);
360 }
361 let desc = self.read_desc(current, memory)?;
362 if desc.is_indirect() {
363 return Err(VirtioError::NotSupported);
364 }
365 if desc
366 .base_addr
367 .as_usize()
368 .checked_add(desc.len as usize)
369 .is_none()
370 {
371 return Err(VirtioError::InvalidDescriptor);
372 }
373 descriptors.push(desc);
374 if !desc.has_next() {
375 break;
376 }
377 current = desc.next;
378 if descriptors.len() > self.size as usize {
381 return Err(VirtioError::InvalidDescriptor);
382 }
383 }
384
385 Ok(DescriptorChain::new(head, descriptors))
386 }
387
388 pub fn chain_length(&self, head_index: u16, memory: &mut dyn GuestMemory) -> VirtioResult<u32> {
390 let descriptors = self.follow_chain(head_index, memory)?;
391 Ok(descriptors.iter().map(|desc| desc.len).sum())
392 }
393
394 pub fn validate_chain(
396 &self,
397 head_index: u16,
398 memory: &mut dyn GuestMemory,
399 ) -> VirtioResult<bool> {
400 let descriptors = self.follow_chain(head_index, memory)?;
401
402 if descriptors.is_empty() {
404 return Ok(false);
405 }
406
407 for (i, desc) in descriptors.iter().enumerate() {
409 if i == descriptors.len() - 1 && desc.has_next() {
411 return Ok(false);
412 }
413
414 if i < descriptors.len() - 1 && !desc.has_next() {
416 return Ok(false);
417 }
418 }
419
420 Ok(true)
421 }
422
423 pub fn get_data_buffers(
425 &self,
426 head_index: u16,
427 device_type: VirtioDeviceID,
428 memory: &mut dyn GuestMemory,
429 ) -> VirtioResult<Vec<(GuestPhysAddr, usize, bool)>> {
430 let descriptors = self.follow_chain(head_index, memory)?;
431
432 if descriptors.len() < 2 && device_type == VirtioDeviceID::Block {
433 return Ok(Vec::new());
434 }
435
436 let mut buffers = Vec::new();
437 if device_type == VirtioDeviceID::Block {
438 for desc in &descriptors[1..descriptors.len() - 1] {
439 buffers.push((desc.base_addr, desc.len as usize, desc.is_write()));
440 }
441 } else {
442 for desc in &descriptors {
443 buffers.push((desc.base_addr, desc.len as usize, desc.is_write()));
444 }
445 }
446
447 Ok(buffers)
448 }
449
450 pub fn get_status_addr(
452 &self,
453 head_index: u16,
454 memory: &mut dyn GuestMemory,
455 ) -> VirtioResult<GuestPhysAddr> {
456 let descriptors = self.follow_chain(head_index, memory)?;
457
458 if descriptors.is_empty() {
459 return Err(VirtioError::InvalidQueue);
460 }
461
462 let status_desc = &descriptors[descriptors.len() - 1];
463 if !status_desc.is_write() || status_desc.len < 1 {
465 return Err(VirtioError::InvalidQueue);
466 }
467
468 Ok(status_desc.base_addr)
469 }
470}
471
472#[cfg(test)]
473mod tests {
474 use alloc::vec;
475
476 use ax_memory_addr::PhysAddr;
477 use axaddrspace::GuestMemoryAccessor;
478
479 use super::*;
480
481 #[derive(Clone)]
482 struct TestTranslator {
483 base_host_ptr: usize,
484 }
485
486 impl GuestMemoryAccessor for TestTranslator {
487 fn translate_and_get_limit(&self, guest_addr: GuestPhysAddr) -> Option<(PhysAddr, usize)> {
488 let offset = guest_addr.as_usize();
489 Some((PhysAddr::from(self.base_host_ptr + offset), usize::MAX))
490 }
491 }
492
493 #[test]
494 fn status_descriptor_len_must_be_at_least_one() {
495 let mut mem = vec![0u8; 4096];
497 let base_ptr = mem.as_mut_ptr() as usize;
498 let translator = TestTranslator {
499 base_host_ptr: base_ptr,
500 };
501 let mut memory = crate::AddressSpaceMemory::new(&translator);
502
503 let base = GuestPhysAddr::from(0x10usize);
505 let table = DescriptorTable::new(base, 2);
506
507 let mut d0 = VirtQueueDesc::new(GuestPhysAddr::from(0x100usize), 16, 0, 1);
509 d0.set_next(true);
510 let mut d1 = VirtQueueDesc::new(GuestPhysAddr::from(0x200usize), 0, 0, 0);
511 d1.set_write(true); d1.set_next(false);
513
514 table.write_desc(0, &d0, &mut memory).unwrap();
515 table.write_desc(1, &d1, &mut memory).unwrap();
516
517 let err = table.get_status_addr(0, &mut memory).unwrap_err();
519 assert!(matches!(err, VirtioError::InvalidQueue));
520
521 let mut d1_ok = d1;
523 d1_ok.len = 1;
524 table.write_desc(1, &d1_ok, &mut memory).unwrap();
525 let ok_addr = table.get_status_addr(0, &mut memory).unwrap();
526 assert_eq!(ok_addr.as_usize(), 0x200);
527 }
528
529 #[test]
530 fn layout_size_counts_descriptors() {
531 assert_eq!(DescriptorTable::layout_size(4), 64);
533 assert_eq!(DescriptorTable::layout_size(256), 4096);
535 }
536}