• 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 //! Split 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::trace;
13 use data_model::Le16;
14 use data_model::Le32;
15 use data_model::Le64;
16 use vm_memory::GuestAddress;
17 use vm_memory::GuestMemory;
18 use zerocopy::AsBytes;
19 use zerocopy::FromBytes;
20 use zerocopy::FromZeroes;
21 
22 use crate::virtio::descriptor_chain::Descriptor;
23 use crate::virtio::descriptor_chain::DescriptorAccess;
24 use crate::virtio::descriptor_chain::DescriptorChainIter;
25 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_NEXT;
26 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE;
27 
28 /// A single virtio split queue descriptor (`struct virtq_desc` in the spec).
29 #[derive(Copy, Clone, Debug, FromZeroes, FromBytes, AsBytes)]
30 #[repr(C)]
31 pub struct Desc {
32     /// Guest address of memory described by this descriptor.
33     pub addr: Le64,
34 
35     /// Length of this descriptor's memory region in bytes.
36     pub len: Le32,
37 
38     /// `VIRTQ_DESC_F_*` flags for this descriptor.
39     pub flags: Le16,
40 
41     /// Index of the next descriptor in the chain (only valid if `flags & VIRTQ_DESC_F_NEXT`).
42     pub next: Le16,
43 }
44 
45 /// Iterator over the descriptors of a split virtqueue descriptor chain.
46 pub struct SplitDescriptorChain<'m> {
47     /// Current descriptor index within `desc_table`, or `None` if the iterator is exhausted.
48     index: Option<u16>,
49 
50     /// Number of descriptors returned by the iterator already.
51     /// If `count` reaches `queue_size`, the chain has a loop and is therefore invalid.
52     count: u16,
53 
54     queue_size: u16,
55 
56     mem: &'m GuestMemory,
57     desc_table: GuestAddress,
58 }
59 
60 impl<'m> SplitDescriptorChain<'m> {
61     /// Construct a new iterator over a split virtqueue descriptor chain.
62     ///
63     /// # Arguments
64     /// * `mem` - The [`GuestMemory`] containing the descriptor chain.
65     /// * `desc_table` - Guest physical address of the descriptor table.
66     /// * `queue_size` - Total number of entries in the descriptor table.
67     /// * `index` - The index of the first descriptor in the chain.
new( mem: &'m GuestMemory, desc_table: GuestAddress, queue_size: u16, index: u16, ) -> SplitDescriptorChain<'m>68     pub fn new(
69         mem: &'m GuestMemory,
70         desc_table: GuestAddress,
71         queue_size: u16,
72         index: u16,
73     ) -> SplitDescriptorChain<'m> {
74         trace!("starting split descriptor chain head={index}");
75         SplitDescriptorChain {
76             index: Some(index),
77             count: 0,
78             queue_size,
79             mem,
80             desc_table,
81         }
82     }
83 }
84 
85 impl DescriptorChainIter for SplitDescriptorChain<'_> {
next(&mut self) -> Result<Option<Descriptor>>86     fn next(&mut self) -> Result<Option<Descriptor>> {
87         let index = match self.index {
88             Some(index) => index,
89             None => return Ok(None),
90         };
91 
92         if index >= self.queue_size {
93             bail!(
94                 "out of bounds descriptor index {} for queue size {}",
95                 index,
96                 self.queue_size
97             );
98         }
99 
100         if self.count >= self.queue_size {
101             bail!("descriptor chain loop detected");
102         }
103         self.count += 1;
104 
105         let desc_addr = self
106             .desc_table
107             .checked_add((index as u64) * 16)
108             .context("integer overflow")?;
109         let desc = self
110             .mem
111             .read_obj_from_addr::<Desc>(desc_addr)
112             .with_context(|| format!("failed to read desc {:#x}", desc_addr.offset()))?;
113 
114         let address: u64 = desc.addr.into();
115         let len: u32 = desc.len.into();
116         let flags: u16 = desc.flags.into();
117         let next: u16 = desc.next.into();
118 
119         trace!("{index:5}: addr={address:#016x} len={len:#08x} flags={flags:#x}");
120 
121         let unexpected_flags = flags & !(VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT);
122         if unexpected_flags != 0 {
123             bail!("unexpected flags in descriptor {index}: {unexpected_flags:#x}")
124         }
125 
126         let access = if flags & VIRTQ_DESC_F_WRITE != 0 {
127             DescriptorAccess::DeviceWrite
128         } else {
129             DescriptorAccess::DeviceRead
130         };
131 
132         self.index = if flags & VIRTQ_DESC_F_NEXT != 0 {
133             Some(next)
134         } else {
135             None
136         };
137 
138         Ok(Some(Descriptor {
139             address,
140             len,
141             access,
142         }))
143     }
144 
count(&self) -> u16145     fn count(&self) -> u16 {
146         self.count
147     }
148 
id(&self) -> Option<u16>149     fn id(&self) -> Option<u16> {
150         None
151     }
152 }
153