• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Packed virtqueue descriptor chain iterator
6 
7 #![deny(missing_docs)]
8 
9 use anyhow::bail;
10 use anyhow::Context;
11 use anyhow::Result;
12 use base::error;
13 use base::trace;
14 use data_model::Le16;
15 use data_model::Le32;
16 use data_model::Le64;
17 use vm_memory::GuestAddress;
18 use vm_memory::GuestMemory;
19 use zerocopy::AsBytes;
20 use zerocopy::FromBytes;
21 use zerocopy::FromZeroes;
22 
23 use crate::virtio::descriptor_chain::Descriptor;
24 use crate::virtio::descriptor_chain::DescriptorAccess;
25 use crate::virtio::descriptor_chain::DescriptorChainIter;
26 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_AVAIL;
27 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_NEXT;
28 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_USED;
29 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE;
30 
31 /// Enable events
32 pub const RING_EVENT_FLAGS_ENABLE: u16 = 0x0;
33 /// Disable events
34 pub const RING_EVENT_FLAGS_DISABLE: u16 = 0x1;
35 
36 /// Enable events for a specific descriptor.
37 /// Only valid if VIRTIO_F_RING_EVENT_IDX has been negotiated.
38 pub const RING_EVENT_FLAGS_DESC: u16 = 0x2;
39 
40 /// A packed virtio packed queue descriptor (`struct pvirtq_desc` in the spec).
41 #[derive(Copy, Clone, Debug, FromZeroes, FromBytes, AsBytes)]
42 #[repr(C)]
43 pub struct PackedDesc {
44     /// Guest address of memory buffer address
45     pub addr: Le64,
46 
47     /// Memory buffer length in bytes
48     pub len: Le32,
49 
50     /// Buffer ID
51     pub id: Le16,
52 
53     /// The flags depending on descriptor type
54     pub flags: Le16,
55 }
56 
57 impl PackedDesc {
addr(&self) -> u6458     pub fn addr(&self) -> u64 {
59         self.addr.into()
60     }
61 
len(&self) -> u3262     pub fn len(&self) -> u32 {
63         self.len.into()
64     }
65 
flags(&self) -> u1666     pub fn flags(&self) -> u16 {
67         self.flags.into()
68     }
69 
id(&self) -> u1670     pub fn id(&self) -> u16 {
71         self.id.into()
72     }
73 
has_next(&self) -> bool74     pub fn has_next(&self) -> bool {
75         self.flags() & VIRTQ_DESC_F_NEXT != 0
76     }
77 
is_available(&self, wrap_value: u16) -> bool78     pub fn is_available(&self, wrap_value: u16) -> bool {
79         let avail = (self.flags() & VIRTQ_DESC_F_AVAIL) != 0;
80         let used = (self.flags() & VIRTQ_DESC_F_USED) != 0;
81         let wrap = wrap_value != 0;
82         avail != used && avail == wrap
83     }
84 }
85 
86 #[derive(Copy, Clone, Debug, FromZeroes, FromBytes, AsBytes)]
87 #[repr(C)]
88 pub struct PackedDescEvent {
89     pub desc: Le16,
90     pub flag: Le16,
91 }
92 
93 impl PackedDescEvent {
notification_type(&self) -> PackedNotificationType94     pub fn notification_type(&self) -> PackedNotificationType {
95         let flag: u16 = self.flag.into();
96 
97         if flag == RING_EVENT_FLAGS_DISABLE {
98             PackedNotificationType::Disable
99         } else if flag == RING_EVENT_FLAGS_DESC {
100             PackedNotificationType::Desc(self.desc.into())
101         } else if flag == RING_EVENT_FLAGS_ENABLE {
102             PackedNotificationType::Enable
103         } else {
104             let desc: u16 = self.desc.into();
105             error!("Unknown packed desc event flag:{:x}, desc:{:x}", flag, desc);
106             PackedNotificationType::Enable
107         }
108     }
109 }
110 
111 pub enum PackedNotificationType {
112     Enable,
113     Disable,
114     Desc(u16),
115 }
116 
117 pub struct PackedDescriptorChain<'m> {
118     avail_wrap_counter: bool,
119 
120     /// Current descriptor index within `desc_table`, or `None` if the iterator is exhausted.
121     index: Option<u16>,
122 
123     /// Number of descriptors returned by the iterator already.
124     /// If `count` reaches `queue_size`, the chain has a loop and is therefore invalid.
125     count: u16,
126 
127     /// Buffer Id, which locates at the last descriptor in the chain
128     id: Option<u16>,
129 
130     queue_size: u16,
131 
132     mem: &'m GuestMemory,
133     desc_table: GuestAddress,
134 }
135 
136 impl<'m> PackedDescriptorChain<'m> {
137     /// Construct a new iterator over a split virtqueue descriptor chain.
138     ///
139     /// # Arguments
140     /// * `mem` - The [`GuestMemory`] containing the descriptor chain.
141     /// * `desc_table` - Guest physical address of the descriptor table.
142     /// * `queue_size` - Total number of entries in the descriptor table.
143     /// * `index` - The index of the first descriptor in the chain.
new( mem: &'m GuestMemory, desc_table: GuestAddress, queue_size: u16, avail_wrap_counter: bool, index: u16, ) -> PackedDescriptorChain<'m>144     pub fn new(
145         mem: &'m GuestMemory,
146         desc_table: GuestAddress,
147         queue_size: u16,
148         avail_wrap_counter: bool,
149         index: u16,
150     ) -> PackedDescriptorChain<'m> {
151         trace!("starting packed descriptor chain head={index}");
152         PackedDescriptorChain {
153             index: Some(index),
154             count: 0,
155             id: None,
156             queue_size,
157             mem,
158             desc_table,
159             avail_wrap_counter,
160         }
161     }
162 }
163 
164 impl DescriptorChainIter for PackedDescriptorChain<'_> {
next(&mut self) -> Result<Option<Descriptor>>165     fn next(&mut self) -> Result<Option<Descriptor>> {
166         let index = match self.index {
167             Some(index) => index,
168             None => {
169                 return Ok(None);
170             }
171         };
172 
173         if index >= self.queue_size {
174             bail!(
175                 "out of bounds descriptor index {} for queue size {}",
176                 index,
177                 self.queue_size
178             );
179         }
180 
181         if self.count >= self.queue_size {
182             bail!("descriptor chain loop detected");
183         }
184 
185         let desc_addr = self
186             .desc_table
187             .checked_add((index as u64) * 16)
188             .context("integer overflow")?;
189         let desc = self
190             .mem
191             .read_obj_from_addr::<PackedDesc>(desc_addr)
192             .with_context(|| format!("failed to read desc {:#x}", desc_addr.offset()))?;
193 
194         let address: u64 = desc.addr();
195         let len: u32 = desc.len();
196         let flags: u16 = desc.flags();
197 
198         trace!("{index:5}: addr={address:#016x} len={len:#08x} flags={flags:#x}");
199 
200         if !desc.is_available(self.avail_wrap_counter as u16) {
201             return Ok(None);
202         }
203 
204         if len == 0 {
205             bail!("invalid zero-length descriptor");
206         }
207 
208         let unexpected_flags = flags
209             & !(VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_AVAIL | VIRTQ_DESC_F_USED);
210         if unexpected_flags != 0 {
211             bail!("unexpected flags in descriptor {index}: {unexpected_flags:#x}")
212         }
213 
214         let access = if flags & VIRTQ_DESC_F_WRITE != 0 {
215             DescriptorAccess::DeviceWrite
216         } else {
217             DescriptorAccess::DeviceRead
218         };
219 
220         // If VIRTQ_DESC_F_NEXT exists, the next descriptor in descriptor chain
221         // is the next element in descriptor table. When index reaches the end of
222         // descriptor table, we need to flip avail_wrap_counter.
223         if desc.has_next() {
224             if index + 1 < self.queue_size {
225                 self.index = Some(index + 1);
226             } else {
227                 self.index = Some(0);
228                 self.avail_wrap_counter = !self.avail_wrap_counter;
229             }
230         } else {
231             self.id = Some(desc.id());
232             self.index = None;
233         }
234 
235         self.count += 1;
236 
237         Ok(Some(Descriptor {
238             address,
239             len,
240             access,
241         }))
242     }
243 
count(&self) -> u16244     fn count(&self) -> u16 {
245         self.count
246     }
247 
id(&self) -> Option<u16>248     fn id(&self) -> Option<u16> {
249         self.id
250     }
251 }
252