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