Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/descriptor_chain.rs
5394 views
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
9
use anyhow::bail;
10
use anyhow::Context;
11
use anyhow::Result;
12
use base::trace;
13
use cros_async::MemRegion;
14
use smallvec::SmallVec;
15
use vm_memory::GuestAddress;
16
use vm_memory::GuestMemory;
17
18
use crate::virtio::descriptor_utils::Reader;
19
use crate::virtio::descriptor_utils::Writer;
20
21
/// Virtio flag indicating there is a next descriptor in descriptor chain
22
pub const VIRTQ_DESC_F_NEXT: u16 = 0x1;
23
/// Virtio flag indicating descriptor is write-only
24
pub const VIRTQ_DESC_F_WRITE: u16 = 0x2;
25
26
/// Packed virtqueue flags
27
pub const VIRTQ_DESC_F_AVAIL: u16 = 0x80;
28
pub 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)]
32
pub 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.
46
pub 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
65
impl 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`].
193
pub 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.
207
pub 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
}
218
219