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 //! Packed 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::error; 13 use base::trace; 14 use data_model::Le16; 15 use data_model::Le32; 16 use data_model::Le64; 17 use vm_memory::GuestAddress; 18 use vm_memory::GuestMemory; 19 use zerocopy::AsBytes; 20 use zerocopy::FromBytes; 21 use zerocopy::FromZeroes; 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_AVAIL; 27 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_NEXT; 28 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_USED; 29 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE; 30 31 /// Enable events 32 pub const RING_EVENT_FLAGS_ENABLE: u16 = 0x0; 33 /// Disable events 34 pub const RING_EVENT_FLAGS_DISABLE: u16 = 0x1; 35 36 /// Enable events for a specific descriptor. 37 /// Only valid if VIRTIO_F_RING_EVENT_IDX has been negotiated. 38 pub const RING_EVENT_FLAGS_DESC: u16 = 0x2; 39 40 /// A packed virtio packed queue descriptor (`struct pvirtq_desc` in the spec). 41 #[derive(Copy, Clone, Debug, FromZeroes, FromBytes, AsBytes)] 42 #[repr(C)] 43 pub struct PackedDesc { 44 /// Guest address of memory buffer address 45 pub addr: Le64, 46 47 /// Memory buffer length in bytes 48 pub len: Le32, 49 50 /// Buffer ID 51 pub id: Le16, 52 53 /// The flags depending on descriptor type 54 pub flags: Le16, 55 } 56 57 impl PackedDesc { addr(&self) -> u6458 pub fn addr(&self) -> u64 { 59 self.addr.into() 60 } 61 len(&self) -> u3262 pub fn len(&self) -> u32 { 63 self.len.into() 64 } 65 flags(&self) -> u1666 pub fn flags(&self) -> u16 { 67 self.flags.into() 68 } 69 id(&self) -> u1670 pub fn id(&self) -> u16 { 71 self.id.into() 72 } 73 has_next(&self) -> bool74 pub fn has_next(&self) -> bool { 75 self.flags() & VIRTQ_DESC_F_NEXT != 0 76 } 77 is_available(&self, wrap_value: u16) -> bool78 pub fn is_available(&self, wrap_value: u16) -> bool { 79 let avail = (self.flags() & VIRTQ_DESC_F_AVAIL) != 0; 80 let used = (self.flags() & VIRTQ_DESC_F_USED) != 0; 81 let wrap = wrap_value != 0; 82 avail != used && avail == wrap 83 } 84 } 85 86 #[derive(Copy, Clone, Debug, FromZeroes, FromBytes, AsBytes)] 87 #[repr(C)] 88 pub struct PackedDescEvent { 89 pub desc: Le16, 90 pub flag: Le16, 91 } 92 93 impl PackedDescEvent { notification_type(&self) -> PackedNotificationType94 pub fn notification_type(&self) -> PackedNotificationType { 95 let flag: u16 = self.flag.into(); 96 97 if flag == RING_EVENT_FLAGS_DISABLE { 98 PackedNotificationType::Disable 99 } else if flag == RING_EVENT_FLAGS_DESC { 100 PackedNotificationType::Desc(self.desc.into()) 101 } else if flag == RING_EVENT_FLAGS_ENABLE { 102 PackedNotificationType::Enable 103 } else { 104 let desc: u16 = self.desc.into(); 105 error!("Unknown packed desc event flag:{:x}, desc:{:x}", flag, desc); 106 PackedNotificationType::Enable 107 } 108 } 109 } 110 111 pub enum PackedNotificationType { 112 Enable, 113 Disable, 114 Desc(u16), 115 } 116 117 pub struct PackedDescriptorChain<'m> { 118 avail_wrap_counter: bool, 119 120 /// Current descriptor index within `desc_table`, or `None` if the iterator is exhausted. 121 index: Option<u16>, 122 123 /// Number of descriptors returned by the iterator already. 124 /// If `count` reaches `queue_size`, the chain has a loop and is therefore invalid. 125 count: u16, 126 127 /// Buffer Id, which locates at the last descriptor in the chain 128 id: Option<u16>, 129 130 queue_size: u16, 131 132 mem: &'m GuestMemory, 133 desc_table: GuestAddress, 134 } 135 136 impl<'m> PackedDescriptorChain<'m> { 137 /// Construct a new iterator over a split virtqueue descriptor chain. 138 /// 139 /// # Arguments 140 /// * `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. new( mem: &'m GuestMemory, desc_table: GuestAddress, queue_size: u16, avail_wrap_counter: bool, index: u16, ) -> PackedDescriptorChain<'m>144 pub fn new( 145 mem: &'m GuestMemory, 146 desc_table: GuestAddress, 147 queue_size: u16, 148 avail_wrap_counter: bool, 149 index: u16, 150 ) -> PackedDescriptorChain<'m> { 151 trace!("starting packed descriptor chain head={index}"); 152 PackedDescriptorChain { 153 index: Some(index), 154 count: 0, 155 id: None, 156 queue_size, 157 mem, 158 desc_table, 159 avail_wrap_counter, 160 } 161 } 162 } 163 164 impl DescriptorChainIter for PackedDescriptorChain<'_> { next(&mut self) -> Result<Option<Descriptor>>165 fn next(&mut self) -> Result<Option<Descriptor>> { 166 let index = match self.index { 167 Some(index) => index, 168 None => { 169 return Ok(None); 170 } 171 }; 172 173 if index >= self.queue_size { 174 bail!( 175 "out of bounds descriptor index {} for queue size {}", 176 index, 177 self.queue_size 178 ); 179 } 180 181 if self.count >= self.queue_size { 182 bail!("descriptor chain loop detected"); 183 } 184 185 let desc_addr = self 186 .desc_table 187 .checked_add((index as u64) * 16) 188 .context("integer overflow")?; 189 let desc = self 190 .mem 191 .read_obj_from_addr::<PackedDesc>(desc_addr) 192 .with_context(|| format!("failed to read desc {:#x}", desc_addr.offset()))?; 193 194 let address: u64 = desc.addr(); 195 let len: u32 = desc.len(); 196 let flags: u16 = desc.flags(); 197 198 trace!("{index:5}: addr={address:#016x} len={len:#08x} flags={flags:#x}"); 199 200 if !desc.is_available(self.avail_wrap_counter as u16) { 201 return Ok(None); 202 } 203 204 if len == 0 { 205 bail!("invalid zero-length descriptor"); 206 } 207 208 let unexpected_flags = flags 209 & !(VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_AVAIL | VIRTQ_DESC_F_USED); 210 if unexpected_flags != 0 { 211 bail!("unexpected flags in descriptor {index}: {unexpected_flags:#x}") 212 } 213 214 let access = if flags & VIRTQ_DESC_F_WRITE != 0 { 215 DescriptorAccess::DeviceWrite 216 } else { 217 DescriptorAccess::DeviceRead 218 }; 219 220 // If VIRTQ_DESC_F_NEXT exists, the next descriptor in descriptor chain 221 // is the next element in descriptor table. When index reaches the end of 222 // descriptor table, we need to flip avail_wrap_counter. 223 if desc.has_next() { 224 if index + 1 < self.queue_size { 225 self.index = Some(index + 1); 226 } else { 227 self.index = Some(0); 228 self.avail_wrap_counter = !self.avail_wrap_counter; 229 } 230 } else { 231 self.id = Some(desc.id()); 232 self.index = None; 233 } 234 235 self.count += 1; 236 237 Ok(Some(Descriptor { 238 address, 239 len, 240 access, 241 })) 242 } 243 count(&self) -> u16244 fn count(&self) -> u16 { 245 self.count 246 } 247 id(&self) -> Option<u16>248 fn id(&self) -> Option<u16> { 249 self.id 250 } 251 } 252