Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/queue/packed_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
//! 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::FromBytes;
20
use zerocopy::Immutable;
21
use zerocopy::IntoBytes;
22
use zerocopy::KnownLayout;
23
24
use crate::virtio::descriptor_chain::Descriptor;
25
use crate::virtio::descriptor_chain::DescriptorAccess;
26
use crate::virtio::descriptor_chain::DescriptorChainIter;
27
use crate::virtio::descriptor_chain::VIRTQ_DESC_F_AVAIL;
28
use crate::virtio::descriptor_chain::VIRTQ_DESC_F_NEXT;
29
use crate::virtio::descriptor_chain::VIRTQ_DESC_F_USED;
30
use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE;
31
32
/// Enable events
33
pub const RING_EVENT_FLAGS_ENABLE: u16 = 0x0;
34
/// Disable events
35
pub const RING_EVENT_FLAGS_DISABLE: u16 = 0x1;
36
37
/// Enable events for a specific descriptor.
38
/// Only valid if VIRTIO_F_RING_EVENT_IDX has been negotiated.
39
pub const RING_EVENT_FLAGS_DESC: u16 = 0x2;
40
41
/// A packed virtio packed queue descriptor (`struct pvirtq_desc` in the spec).
42
#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
43
#[repr(C)]
44
pub struct PackedDesc {
45
/// Guest address of memory buffer address
46
pub addr: Le64,
47
48
/// Memory buffer length in bytes
49
pub len: Le32,
50
51
/// Buffer ID
52
pub id: Le16,
53
54
/// The flags depending on descriptor type
55
pub flags: Le16,
56
}
57
58
impl 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)]
89
pub struct PackedDescEvent {
90
pub desc: Le16,
91
pub flag: Le16,
92
}
93
94
impl 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
112
pub enum PackedNotificationType {
113
Enable,
114
Disable,
115
Desc(u16),
116
}
117
118
pub struct PackedDescriptorChain<'m> {
119
avail_wrap_counter: bool,
120
121
/// Current descriptor index within `desc_table`, or `None` if the iterator is exhausted.
122
index: Option<u16>,
123
124
/// Number of descriptors returned by the iterator already.
125
/// If `count` reaches `queue_size`, the chain has a loop and is therefore invalid.
126
count: u16,
127
128
/// Buffer Id, which locates at the last descriptor in the chain
129
id: Option<u16>,
130
131
queue_size: u16,
132
133
mem: &'m GuestMemory,
134
desc_table: GuestAddress,
135
}
136
137
impl<'m> PackedDescriptorChain<'m> {
138
/// Construct a new iterator over a split virtqueue descriptor chain.
139
///
140
/// # Arguments
141
/// * `mem` - The [`GuestMemory`] containing the descriptor chain.
142
/// * `desc_table` - Guest physical address of the descriptor table.
143
/// * `queue_size` - Total number of entries in the descriptor table.
144
/// * `index` - The index of the first descriptor in the chain.
145
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
165
impl 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 VIRTQ_DESC_F_NEXT exists, the next descriptor in descriptor chain
222
// is the next element in descriptor table. When index reaches the end of
223
// descriptor table, we need to flip avail_wrap_counter.
224
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
}
253
254