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