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