devices/virtio/queue/
split_descriptor_chain.rs1#![deny(missing_docs)]
8
9use anyhow::bail;
10use anyhow::Context;
11use anyhow::Result;
12use base::trace;
13use data_model::Le16;
14use data_model::Le32;
15use data_model::Le64;
16use vm_memory::GuestAddress;
17use vm_memory::GuestMemory;
18use zerocopy::FromBytes;
19use zerocopy::Immutable;
20use zerocopy::IntoBytes;
21use zerocopy::KnownLayout;
22
23use crate::virtio::descriptor_chain::Descriptor;
24use crate::virtio::descriptor_chain::DescriptorAccess;
25use crate::virtio::descriptor_chain::DescriptorChainIter;
26use crate::virtio::descriptor_chain::VIRTQ_DESC_F_NEXT;
27use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE;
28
29#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
31#[repr(C)]
32pub struct Desc {
33 pub addr: Le64,
35
36 pub len: Le32,
38
39 pub flags: Le16,
41
42 pub next: Le16,
44}
45
46pub struct SplitDescriptorChain<'m> {
48 index: Option<u16>,
50
51 count: u16,
54
55 queue_size: u16,
56
57 mem: &'m GuestMemory,
58 desc_table: GuestAddress,
59}
60
61impl<'m> SplitDescriptorChain<'m> {
62 pub fn new(
70 mem: &'m GuestMemory,
71 desc_table: GuestAddress,
72 queue_size: u16,
73 index: u16,
74 ) -> SplitDescriptorChain<'m> {
75 trace!("starting split descriptor chain head={index}");
76 SplitDescriptorChain {
77 index: Some(index),
78 count: 0,
79 queue_size,
80 mem,
81 desc_table,
82 }
83 }
84}
85
86impl DescriptorChainIter for SplitDescriptorChain<'_> {
87 fn next(&mut self) -> Result<Option<Descriptor>> {
88 let index = match self.index {
89 Some(index) => index,
90 None => return Ok(None),
91 };
92
93 if index >= self.queue_size {
94 bail!(
95 "out of bounds descriptor index {} for queue size {}",
96 index,
97 self.queue_size
98 );
99 }
100
101 if self.count >= self.queue_size {
102 bail!("descriptor chain loop detected");
103 }
104 self.count += 1;
105
106 let desc_addr = self
107 .desc_table
108 .checked_add((index as u64) * 16)
109 .context("integer overflow")?;
110 let desc = self
111 .mem
112 .read_obj_from_addr::<Desc>(desc_addr)
113 .with_context(|| format!("failed to read desc {:#x}", desc_addr.offset()))?;
114
115 let address: u64 = desc.addr.into();
116 let len: u32 = desc.len.into();
117 let flags: u16 = desc.flags.into();
118 let next: u16 = desc.next.into();
119
120 trace!("{index:5}: addr={address:#016x} len={len:#08x} flags={flags:#x}");
121
122 let unexpected_flags = flags & !(VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT);
123 if unexpected_flags != 0 {
124 bail!("unexpected flags in descriptor {index}: {unexpected_flags:#x}")
125 }
126
127 let access = if flags & VIRTQ_DESC_F_WRITE != 0 {
128 DescriptorAccess::DeviceWrite
129 } else {
130 DescriptorAccess::DeviceRead
131 };
132
133 self.index = if flags & VIRTQ_DESC_F_NEXT != 0 {
134 Some(next)
135 } else {
136 None
137 };
138
139 Ok(Some(Descriptor {
140 address,
141 len,
142 access,
143 }))
144 }
145
146 fn count(&self) -> u16 {
147 self.count
148 }
149
150 fn id(&self) -> Option<u16> {
151 None
152 }
153}