devices/virtio/descriptor_chain.rs
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//! Virtqueue descriptor chain abstraction
6
7#![deny(missing_docs)]
8
9use anyhow::bail;
10use anyhow::Context;
11use anyhow::Result;
12use base::trace;
13use cros_async::MemRegion;
14use smallvec::SmallVec;
15use vm_memory::GuestAddress;
16use vm_memory::GuestMemory;
17
18use crate::virtio::descriptor_utils::Reader;
19use crate::virtio::descriptor_utils::Writer;
20
21/// Virtio flag indicating there is a next descriptor in descriptor chain
22pub const VIRTQ_DESC_F_NEXT: u16 = 0x1;
23/// Virtio flag indicating descriptor is write-only
24pub const VIRTQ_DESC_F_WRITE: u16 = 0x2;
25
26/// Packed virtqueue flags
27pub const VIRTQ_DESC_F_AVAIL: u16 = 0x80;
28pub const VIRTQ_DESC_F_USED: u16 = 0x8000;
29
30/// Type of access allowed for a single virtio descriptor within a descriptor chain.
31#[derive(Copy, Clone, Debug, PartialEq, Eq)]
32pub enum DescriptorAccess {
33 /// Descriptor is readable by the device (written by the driver before putting the descriptor
34 /// chain on the available queue).
35 DeviceRead,
36 /// Descriptor is writable by the device (read by the driver after the device puts the
37 /// descriptor chain on the used queue).
38 DeviceWrite,
39}
40
41/// A virtio descriptor chain.
42///
43/// This is a low-level representation of the memory regions in a descriptor chain. Most code should
44/// use [`virtio::Reader`](crate::virtio::Reader) and [`virtio::Writer`](crate::virtio::Writer)
45/// rather than working with `DescriptorChain` memory regions directly.
46pub struct DescriptorChain {
47 mem: GuestMemory,
48
49 /// Index into the descriptor table.
50 index: u16,
51
52 /// The readable memory regions that make up the descriptor chain.
53 pub reader: Reader,
54
55 /// The writable memory regions that make up the descriptor chain.
56 pub writer: Writer,
57
58 /// The descriptor chain id(only if it is a packed virtqueue descriptor chain)
59 pub id: Option<u16>,
60
61 /// Number of descriptor in descriptor chain
62 pub count: u16,
63}
64
65impl DescriptorChain {
66 /// Read all descriptors from `chain` into a new `DescriptorChain` instance.
67 ///
68 /// This function validates the following properties of the descriptor chain:
69 /// * The chain contains at least one descriptor.
70 /// * Each descriptor has a non-zero length.
71 /// * Each descriptor's memory falls entirely within a contiguous region of `mem`.
72 /// * The total length of the descriptor chain data is representable in `u32`.
73 ///
74 /// If these properties do not hold, `Err` will be returned.
75 ///
76 /// # Arguments
77 ///
78 /// * `chain` - Iterator that will be walked to retrieve all of the descriptors in the chain.
79 /// * `mem` - The [`GuestMemory`] backing the descriptor table and descriptor memory regions.
80 /// * `index` - The index of the first descriptor in the chain.
81 pub fn new(
82 mut chain: impl DescriptorChainIter,
83 mem: &GuestMemory,
84 index: u16,
85 ) -> Result<DescriptorChain> {
86 let mut readable_regions = SmallVec::new();
87 let mut writable_regions = SmallVec::new();
88
89 // If `writable` is true, a writable descriptor has already been encountered.
90 // Valid descriptor chains must consist of readable descriptors followed by writable
91 // descriptors.
92 let mut writable = false;
93
94 while let Some(desc) = chain.next()? {
95 if desc.len == 0 {
96 trace!("zero-length descriptor at index {index}");
97 continue;
98 }
99
100 let region = MemRegion {
101 offset: desc.address,
102 len: desc.len as usize,
103 };
104
105 match desc.access {
106 DescriptorAccess::DeviceRead => {
107 if writable {
108 bail!("invalid device-readable descriptor following writable descriptors");
109 }
110 readable_regions.push(region);
111 }
112 DescriptorAccess::DeviceWrite => {
113 writable = true;
114 writable_regions.push(region);
115 }
116 }
117 }
118
119 let count = chain.count();
120 let id = chain.id();
121
122 Self::validate_mem_regions(mem, &readable_regions, &writable_regions)
123 .context("invalid descriptor chain memory regions")?;
124
125 trace!(
126 "Descriptor chain created, index:{index}, count:{count}, buffer id:{:?}, readable:{}, writable:{}",
127 id,
128 readable_regions.len(),
129 writable_regions.len()
130 );
131
132 let reader = Reader::new_from_regions(mem, readable_regions);
133 let writer = Writer::new_from_regions(mem, writable_regions);
134
135 let desc_chain = DescriptorChain {
136 mem: mem.clone(),
137 index,
138 reader,
139 writer,
140 id,
141 count,
142 };
143
144 Ok(desc_chain)
145 }
146
147 fn validate_mem_regions(
148 mem: &GuestMemory,
149 readable_regions: &[MemRegion],
150 writable_regions: &[MemRegion],
151 ) -> Result<()> {
152 let mut total_len: u32 = 0;
153 for r in readable_regions.iter().chain(writable_regions.iter()) {
154 // This cast is safe because the virtio descriptor length field is u32.
155 let len = r.len as u32;
156
157 // Check that all the regions are totally contained in GuestMemory.
158 if !mem.is_valid_range(GuestAddress(r.offset), len.into()) {
159 bail!(
160 "descriptor address range out of bounds: addr={:#x} len={:#x}",
161 r.offset,
162 r.len
163 );
164 }
165
166 // Verify that summing the descriptor sizes does not overflow.
167 // This can happen if a driver tricks a device into reading/writing more data than
168 // fits in a `u32`.
169 total_len = total_len
170 .checked_add(len)
171 .context("descriptor chain length overflow")?;
172 }
173
174 if total_len == 0 {
175 bail!("invalid zero-length descriptor chain");
176 }
177
178 Ok(())
179 }
180
181 /// Returns a reference to the [`GuestMemory`] instance.
182 pub fn mem(&self) -> &GuestMemory {
183 &self.mem
184 }
185
186 /// Returns the index of the first descriptor in the chain.
187 pub fn index(&self) -> u16 {
188 self.index
189 }
190}
191
192/// A single descriptor within a [`DescriptorChain`].
193pub struct Descriptor {
194 /// Guest memory address of this descriptor.
195 /// If IOMMU is enabled, this is an IOVA, which must be translated via the IOMMU to get a
196 /// guest physical address.
197 pub address: u64,
198
199 /// Length of the descriptor in bytes.
200 pub len: u32,
201
202 /// Whether this descriptor should be treated as writable or readable by the device.
203 pub access: DescriptorAccess,
204}
205
206/// Iterator over the descriptors of a descriptor chain.
207pub trait DescriptorChainIter {
208 /// Return the next descriptor in the chain, or `None` if there are no more descriptors.
209 fn next(&mut self) -> Result<Option<Descriptor>>;
210
211 /// Return the number of descriptor has been iterated in the chain
212 fn count(&self) -> u16;
213
214 /// Return Packed descriptor chain buffer id if iterator reaches end, otherwise return None
215 /// SplitDescriptorChainIter should return None.
216 fn id(&self) -> Option<u16>;
217}