devices/virtio/queue/
packed_descriptor_chain.rs

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