devices/virtio/queue/
packed_descriptor_chain.rs1#![deny(missing_docs)]
8
9use anyhow::bail;
10use anyhow::Context;
11use anyhow::Result;
12use base::error;
13use base::trace;
14use data_model::Le16;
15use data_model::Le32;
16use data_model::Le64;
17use vm_memory::GuestAddress;
18use vm_memory::GuestMemory;
19use zerocopy::FromBytes;
20use zerocopy::Immutable;
21use zerocopy::IntoBytes;
22use zerocopy::KnownLayout;
23
24use crate::virtio::descriptor_chain::Descriptor;
25use crate::virtio::descriptor_chain::DescriptorAccess;
26use crate::virtio::descriptor_chain::DescriptorChainIter;
27use crate::virtio::descriptor_chain::VIRTQ_DESC_F_AVAIL;
28use crate::virtio::descriptor_chain::VIRTQ_DESC_F_NEXT;
29use crate::virtio::descriptor_chain::VIRTQ_DESC_F_USED;
30use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE;
31
32pub const RING_EVENT_FLAGS_ENABLE: u16 = 0x0;
34pub const RING_EVENT_FLAGS_DISABLE: u16 = 0x1;
36
37pub const RING_EVENT_FLAGS_DESC: u16 = 0x2;
40
41#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
43#[repr(C)]
44pub struct PackedDesc {
45 pub addr: Le64,
47
48 pub len: Le32,
50
51 pub id: Le16,
53
54 pub flags: Le16,
56}
57
58impl PackedDesc {
59 pub fn addr(&self) -> u64 {
60 self.addr.into()
61 }
62
63 pub fn len(&self) -> u32 {
64 self.len.into()
65 }
66
67 pub fn flags(&self) -> u16 {
68 self.flags.into()
69 }
70
71 pub fn id(&self) -> u16 {
72 self.id.into()
73 }
74
75 pub fn has_next(&self) -> bool {
76 self.flags() & VIRTQ_DESC_F_NEXT != 0
77 }
78
79 pub fn is_available(&self, wrap_value: u16) -> bool {
80 let avail = (self.flags() & VIRTQ_DESC_F_AVAIL) != 0;
81 let used = (self.flags() & VIRTQ_DESC_F_USED) != 0;
82 let wrap = wrap_value != 0;
83 avail != used && avail == wrap
84 }
85}
86
87#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
88#[repr(C)]
89pub struct PackedDescEvent {
90 pub desc: Le16,
91 pub flag: Le16,
92}
93
94impl PackedDescEvent {
95 pub fn notification_type(&self) -> PackedNotificationType {
96 let flag: u16 = self.flag.into();
97
98 if flag == RING_EVENT_FLAGS_DISABLE {
99 PackedNotificationType::Disable
100 } else if flag == RING_EVENT_FLAGS_DESC {
101 PackedNotificationType::Desc(self.desc.into())
102 } else if flag == RING_EVENT_FLAGS_ENABLE {
103 PackedNotificationType::Enable
104 } else {
105 let desc: u16 = self.desc.into();
106 error!("Unknown packed desc event flag:{:x}, desc:{:x}", flag, desc);
107 PackedNotificationType::Enable
108 }
109 }
110}
111
112pub enum PackedNotificationType {
113 Enable,
114 Disable,
115 Desc(u16),
116}
117
118pub struct PackedDescriptorChain<'m> {
119 avail_wrap_counter: bool,
120
121 index: Option<u16>,
123
124 count: u16,
127
128 id: Option<u16>,
130
131 queue_size: u16,
132
133 mem: &'m GuestMemory,
134 desc_table: GuestAddress,
135}
136
137impl<'m> PackedDescriptorChain<'m> {
138 pub fn new(
146 mem: &'m GuestMemory,
147 desc_table: GuestAddress,
148 queue_size: u16,
149 avail_wrap_counter: bool,
150 index: u16,
151 ) -> PackedDescriptorChain<'m> {
152 trace!("starting packed descriptor chain head={index}");
153 PackedDescriptorChain {
154 index: Some(index),
155 count: 0,
156 id: None,
157 queue_size,
158 mem,
159 desc_table,
160 avail_wrap_counter,
161 }
162 }
163}
164
165impl DescriptorChainIter for PackedDescriptorChain<'_> {
166 fn next(&mut self) -> Result<Option<Descriptor>> {
167 let index = match self.index {
168 Some(index) => index,
169 None => {
170 return Ok(None);
171 }
172 };
173
174 if index >= self.queue_size {
175 bail!(
176 "out of bounds descriptor index {} for queue size {}",
177 index,
178 self.queue_size
179 );
180 }
181
182 if self.count >= self.queue_size {
183 bail!("descriptor chain loop detected");
184 }
185
186 let desc_addr = self
187 .desc_table
188 .checked_add((index as u64) * 16)
189 .context("integer overflow")?;
190 let desc = self
191 .mem
192 .read_obj_from_addr::<PackedDesc>(desc_addr)
193 .with_context(|| format!("failed to read desc {:#x}", desc_addr.offset()))?;
194
195 let address: u64 = desc.addr();
196 let len: u32 = desc.len();
197 let flags: u16 = desc.flags();
198
199 trace!("{index:5}: addr={address:#016x} len={len:#08x} flags={flags:#x}");
200
201 if !desc.is_available(self.avail_wrap_counter as u16) {
202 return Ok(None);
203 }
204
205 if len == 0 {
206 bail!("invalid zero-length descriptor");
207 }
208
209 let unexpected_flags = flags
210 & !(VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_AVAIL | VIRTQ_DESC_F_USED);
211 if unexpected_flags != 0 {
212 bail!("unexpected flags in descriptor {index}: {unexpected_flags:#x}")
213 }
214
215 let access = if flags & VIRTQ_DESC_F_WRITE != 0 {
216 DescriptorAccess::DeviceWrite
217 } else {
218 DescriptorAccess::DeviceRead
219 };
220
221 if desc.has_next() {
225 if index + 1 < self.queue_size {
226 self.index = Some(index + 1);
227 } else {
228 self.index = Some(0);
229 self.avail_wrap_counter = !self.avail_wrap_counter;
230 }
231 } else {
232 self.id = Some(desc.id());
233 self.index = None;
234 }
235
236 self.count += 1;
237
238 Ok(Some(Descriptor {
239 address,
240 len,
241 access,
242 }))
243 }
244
245 fn count(&self) -> u16 {
246 self.count
247 }
248
249 fn id(&self) -> Option<u16> {
250 self.id
251 }
252}