Path: blob/main/devices/src/virtio/queue/packed_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//! Packed virtqueue descriptor chain iterator56#![deny(missing_docs)]78use anyhow::bail;9use anyhow::Context;10use anyhow::Result;11use base::error;12use base::trace;13use data_model::Le16;14use data_model::Le32;15use data_model::Le64;16use vm_memory::GuestAddress;17use vm_memory::GuestMemory;18use zerocopy::FromBytes;19use zerocopy::Immutable;20use zerocopy::IntoBytes;21use zerocopy::KnownLayout;2223use crate::virtio::descriptor_chain::Descriptor;24use crate::virtio::descriptor_chain::DescriptorAccess;25use crate::virtio::descriptor_chain::DescriptorChainIter;26use crate::virtio::descriptor_chain::VIRTQ_DESC_F_AVAIL;27use crate::virtio::descriptor_chain::VIRTQ_DESC_F_NEXT;28use crate::virtio::descriptor_chain::VIRTQ_DESC_F_USED;29use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE;3031/// Enable events32pub const RING_EVENT_FLAGS_ENABLE: u16 = 0x0;33/// Disable events34pub const RING_EVENT_FLAGS_DISABLE: u16 = 0x1;3536/// Enable events for a specific descriptor.37/// Only valid if VIRTIO_F_RING_EVENT_IDX has been negotiated.38pub const RING_EVENT_FLAGS_DESC: u16 = 0x2;3940/// A packed virtio packed queue descriptor (`struct pvirtq_desc` in the spec).41#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]42#[repr(C)]43pub struct PackedDesc {44/// Guest address of memory buffer address45pub addr: Le64,4647/// Memory buffer length in bytes48pub len: Le32,4950/// Buffer ID51pub id: Le16,5253/// The flags depending on descriptor type54pub flags: Le16,55}5657impl PackedDesc {58pub fn addr(&self) -> u64 {59self.addr.into()60}6162pub fn len(&self) -> u32 {63self.len.into()64}6566pub fn flags(&self) -> u16 {67self.flags.into()68}6970pub fn id(&self) -> u16 {71self.id.into()72}7374pub fn has_next(&self) -> bool {75self.flags() & VIRTQ_DESC_F_NEXT != 076}7778pub fn is_available(&self, wrap_value: u16) -> bool {79let avail = (self.flags() & VIRTQ_DESC_F_AVAIL) != 0;80let used = (self.flags() & VIRTQ_DESC_F_USED) != 0;81let wrap = wrap_value != 0;82avail != used && avail == wrap83}84}8586#[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]87#[repr(C)]88pub struct PackedDescEvent {89pub desc: Le16,90pub flag: Le16,91}9293impl PackedDescEvent {94pub fn notification_type(&self) -> PackedNotificationType {95let flag: u16 = self.flag.into();9697if flag == RING_EVENT_FLAGS_DISABLE {98PackedNotificationType::Disable99} else if flag == RING_EVENT_FLAGS_DESC {100PackedNotificationType::Desc(self.desc.into())101} else if flag == RING_EVENT_FLAGS_ENABLE {102PackedNotificationType::Enable103} else {104let desc: u16 = self.desc.into();105error!("Unknown packed desc event flag:{:x}, desc:{:x}", flag, desc);106PackedNotificationType::Enable107}108}109}110111pub enum PackedNotificationType {112Enable,113Disable,114Desc(u16),115}116117pub struct PackedDescriptorChain<'m> {118avail_wrap_counter: bool,119120/// Current descriptor index within `desc_table`, or `None` if the iterator is exhausted.121index: Option<u16>,122123/// Number of descriptors returned by the iterator already.124/// If `count` reaches `queue_size`, the chain has a loop and is therefore invalid.125count: u16,126127/// Buffer Id, which locates at the last descriptor in the chain128id: Option<u16>,129130queue_size: u16,131132mem: &'m GuestMemory,133desc_table: GuestAddress,134}135136impl<'m> PackedDescriptorChain<'m> {137/// Construct a new iterator over a split virtqueue descriptor chain.138///139/// # Arguments140/// * `mem` - The [`GuestMemory`] containing the descriptor chain.141/// * `desc_table` - Guest physical address of the descriptor table.142/// * `queue_size` - Total number of entries in the descriptor table.143/// * `index` - The index of the first descriptor in the chain.144pub fn new(145mem: &'m GuestMemory,146desc_table: GuestAddress,147queue_size: u16,148avail_wrap_counter: bool,149index: u16,150) -> PackedDescriptorChain<'m> {151trace!("starting packed descriptor chain head={index}");152PackedDescriptorChain {153index: Some(index),154count: 0,155id: None,156queue_size,157mem,158desc_table,159avail_wrap_counter,160}161}162}163164impl DescriptorChainIter for PackedDescriptorChain<'_> {165fn next(&mut self) -> Result<Option<Descriptor>> {166let index = match self.index {167Some(index) => index,168None => {169return Ok(None);170}171};172173if index >= self.queue_size {174bail!(175"out of bounds descriptor index {} for queue size {}",176index,177self.queue_size178);179}180181if self.count >= self.queue_size {182bail!("descriptor chain loop detected");183}184185let desc_addr = self186.desc_table187.checked_add((index as u64) * 16)188.context("integer overflow")?;189let desc = self190.mem191.read_obj_from_addr::<PackedDesc>(desc_addr)192.with_context(|| format!("failed to read desc {:#x}", desc_addr.offset()))?;193194let address: u64 = desc.addr();195let len: u32 = desc.len();196let flags: u16 = desc.flags();197198trace!("{index:5}: addr={address:#016x} len={len:#08x} flags={flags:#x}");199200if !desc.is_available(self.avail_wrap_counter as u16) {201return Ok(None);202}203204if len == 0 {205bail!("invalid zero-length descriptor");206}207208let unexpected_flags = flags209& !(VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_AVAIL | VIRTQ_DESC_F_USED);210if unexpected_flags != 0 {211bail!("unexpected flags in descriptor {index}: {unexpected_flags:#x}")212}213214let access = if flags & VIRTQ_DESC_F_WRITE != 0 {215DescriptorAccess::DeviceWrite216} else {217DescriptorAccess::DeviceRead218};219220// If VIRTQ_DESC_F_NEXT exists, the next descriptor in descriptor chain221// is the next element in descriptor table. When index reaches the end of222// descriptor table, we need to flip avail_wrap_counter.223if desc.has_next() {224if index + 1 < self.queue_size {225self.index = Some(index + 1);226} else {227self.index = Some(0);228self.avail_wrap_counter = !self.avail_wrap_counter;229}230} else {231self.id = Some(desc.id());232self.index = None;233}234235self.count += 1;236237Ok(Some(Descriptor {238address,239len,240access,241}))242}243244fn count(&self) -> u16 {245self.count246}247248fn id(&self) -> Option<u16> {249self.id250}251}252253254