Path: blob/main/devices/src/virtio/queue/split_descriptor_chain.rs
5394 views
// Copyright 2023 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34//! Split virtqueue descriptor chain iterator56#![deny(missing_docs)]78use anyhow::bail;9use anyhow::Context;10use anyhow::Result;11use base::trace;12use data_model::Le16;13use data_model::Le32;14use data_model::Le64;15use vm_memory::GuestAddress;16use vm_memory::GuestMemory;17use zerocopy::FromBytes;18use zerocopy::Immutable;19use zerocopy::IntoBytes;20use zerocopy::KnownLayout;2122use crate::virtio::descriptor_chain::Descriptor;23use crate::virtio::descriptor_chain::DescriptorAccess;24use crate::virtio::descriptor_chain::DescriptorChainIter;25use crate::virtio::descriptor_chain::VIRTQ_DESC_F_NEXT;26use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE;2728/// A single virtio split queue descriptor (`struct virtq_desc` in the spec).29#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]30#[repr(C)]31pub struct Desc {32/// Guest address of memory described by this descriptor.33pub addr: Le64,3435/// Length of this descriptor's memory region in bytes.36pub len: Le32,3738/// `VIRTQ_DESC_F_*` flags for this descriptor.39pub flags: Le16,4041/// Index of the next descriptor in the chain (only valid if `flags & VIRTQ_DESC_F_NEXT`).42pub next: Le16,43}4445/// Iterator over the descriptors of a split virtqueue descriptor chain.46pub struct SplitDescriptorChain<'m> {47/// Current descriptor index within `desc_table`, or `None` if the iterator is exhausted.48index: Option<u16>,4950/// Number of descriptors returned by the iterator already.51/// If `count` reaches `queue_size`, the chain has a loop and is therefore invalid.52count: u16,5354queue_size: u16,5556mem: &'m GuestMemory,57desc_table: GuestAddress,58}5960impl<'m> SplitDescriptorChain<'m> {61/// Construct a new iterator over a split virtqueue descriptor chain.62///63/// # Arguments64/// * `mem` - The [`GuestMemory`] containing the descriptor chain.65/// * `desc_table` - Guest physical address of the descriptor table.66/// * `queue_size` - Total number of entries in the descriptor table.67/// * `index` - The index of the first descriptor in the chain.68pub fn new(69mem: &'m GuestMemory,70desc_table: GuestAddress,71queue_size: u16,72index: u16,73) -> SplitDescriptorChain<'m> {74trace!("starting split descriptor chain head={index}");75SplitDescriptorChain {76index: Some(index),77count: 0,78queue_size,79mem,80desc_table,81}82}83}8485impl DescriptorChainIter for SplitDescriptorChain<'_> {86fn next(&mut self) -> Result<Option<Descriptor>> {87let index = match self.index {88Some(index) => index,89None => return Ok(None),90};9192if index >= self.queue_size {93bail!(94"out of bounds descriptor index {} for queue size {}",95index,96self.queue_size97);98}99100if self.count >= self.queue_size {101bail!("descriptor chain loop detected");102}103self.count += 1;104105let desc_addr = self106.desc_table107.checked_add((index as u64) * 16)108.context("integer overflow")?;109let desc = self110.mem111.read_obj_from_addr::<Desc>(desc_addr)112.with_context(|| format!("failed to read desc {:#x}", desc_addr.offset()))?;113114let address: u64 = desc.addr.into();115let len: u32 = desc.len.into();116let flags: u16 = desc.flags.into();117let next: u16 = desc.next.into();118119trace!("{index:5}: addr={address:#016x} len={len:#08x} flags={flags:#x}");120121let unexpected_flags = flags & !(VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT);122if unexpected_flags != 0 {123bail!("unexpected flags in descriptor {index}: {unexpected_flags:#x}")124}125126let access = if flags & VIRTQ_DESC_F_WRITE != 0 {127DescriptorAccess::DeviceWrite128} else {129DescriptorAccess::DeviceRead130};131132self.index = if flags & VIRTQ_DESC_F_NEXT != 0 {133Some(next)134} else {135None136};137138Ok(Some(Descriptor {139address,140len,141access,142}))143}144145fn count(&self) -> u16 {146self.count147}148149fn id(&self) -> Option<u16> {150None151}152}153154155