• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #![deny(missing_docs)]
6 
7 use std::num::Wrapping;
8 use std::sync::atomic::fence;
9 use std::sync::atomic::Ordering;
10 
11 use anyhow::bail;
12 use anyhow::Result;
13 use base::error;
14 use base::warn;
15 use base::Event;
16 use serde::Deserialize;
17 use serde::Serialize;
18 use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
19 use vm_memory::GuestAddress;
20 use vm_memory::GuestMemory;
21 
22 use crate::virtio::descriptor_chain::DescriptorChain;
23 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_AVAIL;
24 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_USED;
25 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE;
26 use crate::virtio::queue::packed_descriptor_chain::PackedDesc;
27 use crate::virtio::queue::packed_descriptor_chain::PackedDescEvent;
28 use crate::virtio::queue::packed_descriptor_chain::PackedDescriptorChain;
29 use crate::virtio::queue::packed_descriptor_chain::PackedNotificationType;
30 use crate::virtio::queue::packed_descriptor_chain::RING_EVENT_FLAGS_DESC;
31 use crate::virtio::Interrupt;
32 use crate::virtio::QueueConfig;
33 
34 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
35 struct PackedQueueIndex {
36     wrap_counter: bool,
37     index: Wrapping<u16>,
38 }
39 impl PackedQueueIndex {
new(wrap_counter: bool, index: u16) -> Self40     pub fn new(wrap_counter: bool, index: u16) -> Self {
41         Self {
42             wrap_counter,
43             index: Wrapping(index),
44         }
45     }
46 
new_from_desc(desc: u16) -> Self47     pub fn new_from_desc(desc: u16) -> Self {
48         let wrap_counter: bool = (desc >> 15) == 1;
49         let mask: u16 = 0x7fff;
50         let index = desc & mask;
51         Self::new(wrap_counter, index)
52     }
53 
to_desc(self) -> PackedDescEvent54     pub fn to_desc(self) -> PackedDescEvent {
55         let flag = RING_EVENT_FLAGS_DESC;
56         let mut desc = self.index.0;
57         if self.wrap_counter {
58             desc |= 1 << 15;
59         }
60         PackedDescEvent {
61             desc: desc.into(),
62             flag: flag.into(),
63         }
64     }
65 
add_index(&mut self, index_value: u16, size: u16)66     fn add_index(&mut self, index_value: u16, size: u16) {
67         let new_index = self.index.0 + index_value;
68         if new_index < size {
69             self.index = Wrapping(new_index);
70         } else {
71             self.index = Wrapping(new_index - size);
72             self.wrap_counter = !self.wrap_counter;
73         }
74     }
75 }
76 
77 impl Default for PackedQueueIndex {
default() -> Self78     fn default() -> Self {
79         Self::new(true, 0)
80     }
81 }
82 
83 #[derive(Debug)]
84 pub struct PackedQueue {
85     mem: GuestMemory,
86 
87     event: Event,
88 
89     // The queue size in elements the driver selected
90     size: u16,
91 
92     // MSI-X vector for the queue. Don't care for INTx
93     vector: u16,
94 
95     // Internal index counter to keep track of where to poll
96     avail_index: PackedQueueIndex,
97     use_index: PackedQueueIndex,
98     signalled_used_index: PackedQueueIndex,
99 
100     // Device feature bits accepted by the driver
101     features: u64,
102 
103     // Guest physical address of the descriptor table
104     desc_table: GuestAddress,
105 
106     // Write-only by the device, Including information for reducing the number of device events
107     device_event_suppression: GuestAddress,
108 
109     // Read-only by the device, Includes information for reducing the number of driver events
110     driver_event_suppression: GuestAddress,
111 }
112 
113 #[derive(Serialize, Deserialize)]
114 pub struct PackedQueueSnapshot {
115     size: u16,
116     vector: u16,
117     avail_index: PackedQueueIndex,
118     use_index: PackedQueueIndex,
119     signalled_used_index: PackedQueueIndex,
120     features: u64,
121     desc_table: GuestAddress,
122     device_event_suppression: GuestAddress,
123     driver_event_suppression: GuestAddress,
124 }
125 
126 impl PackedQueue {
127     /// Constructs an empty virtio queue with the given `max_size`.
new(config: &QueueConfig, mem: &GuestMemory, event: Event) -> Result<Self>128     pub fn new(config: &QueueConfig, mem: &GuestMemory, event: Event) -> Result<Self> {
129         let size = config.size();
130 
131         let desc_table = config.desc_table();
132         let driver_area = config.avail_ring();
133         let device_area = config.used_ring();
134 
135         // Validate addresses and queue size to ensure that address calculation won't overflow.
136         let ring_sizes = Self::area_sizes(size, desc_table, driver_area, device_area);
137         let rings = ring_sizes.iter().zip(vec![
138             "descriptor table",
139             "driver_event_suppression",
140             "device_event_suppression",
141         ]);
142 
143         for ((addr, size), name) in rings {
144             if addr.checked_add(*size as u64).is_none() {
145                 bail!(
146                     "virtio queue {} goes out of bounds: start:0x{:08x} size:0x{:08x}",
147                     name,
148                     addr.offset(),
149                     size,
150                 );
151             }
152         }
153 
154         Ok(PackedQueue {
155             mem: mem.clone(),
156             event,
157             size,
158             vector: config.vector(),
159             desc_table: config.desc_table(),
160             driver_event_suppression: config.avail_ring(),
161             device_event_suppression: config.used_ring(),
162             features: config.acked_features(),
163             avail_index: PackedQueueIndex::default(),
164             use_index: PackedQueueIndex::default(),
165             signalled_used_index: PackedQueueIndex::default(),
166         })
167     }
168 
169     /// Return the actual size of the queue, as the driver may not set up a
170     /// queue as big as the device allows.
size(&self) -> u16171     pub fn size(&self) -> u16 {
172         self.size
173     }
174 
175     /// Getter for vector field
vector(&self) -> u16176     pub fn vector(&self) -> u16 {
177         self.vector
178     }
179 
180     /// Getter for descriptor area
desc_table(&self) -> GuestAddress181     pub fn desc_table(&self) -> GuestAddress {
182         self.desc_table
183     }
184 
185     /// Getter for driver area
avail_ring(&self) -> GuestAddress186     pub fn avail_ring(&self) -> GuestAddress {
187         self.driver_event_suppression
188     }
189 
190     /// Getter for device area
used_ring(&self) -> GuestAddress191     pub fn used_ring(&self) -> GuestAddress {
192         self.device_event_suppression
193     }
194 
195     /// Get a reference to the queue's "kick event"
event(&self) -> &Event196     pub fn event(&self) -> &Event {
197         &self.event
198     }
199 
area_sizes( queue_size: u16, desc_table: GuestAddress, driver_area: GuestAddress, device_area: GuestAddress, ) -> Vec<(GuestAddress, usize)>200     fn area_sizes(
201         queue_size: u16,
202         desc_table: GuestAddress,
203         driver_area: GuestAddress,
204         device_area: GuestAddress,
205     ) -> Vec<(GuestAddress, usize)> {
206         vec![
207             (desc_table, 16 * queue_size as usize),
208             (driver_area, 4),
209             (device_area, 4),
210         ]
211     }
212 
213     /// Set the device event suppression
214     ///
215     /// This field is used to specify the timing of when the driver notifies the
216     /// device that the descriptor table is ready to be processed.
set_avail_event(&mut self, event: PackedDescEvent)217     fn set_avail_event(&mut self, event: PackedDescEvent) {
218         fence(Ordering::SeqCst);
219         self.mem
220             .write_obj_at_addr_volatile(event, self.device_event_suppression)
221             .unwrap();
222     }
223 
224     // Get the driver event suppression.
225     // This field is used to specify the timing of when the device notifies the
226     // driver that the descriptor table is ready to be processed.
get_driver_event(&self) -> PackedDescEvent227     fn get_driver_event(&self) -> PackedDescEvent {
228         fence(Ordering::SeqCst);
229 
230         let desc: PackedDescEvent = self
231             .mem
232             .read_obj_from_addr_volatile(self.driver_event_suppression)
233             .unwrap();
234         desc
235     }
236 
237     /// Get the first available descriptor chain without removing it from the queue.
238     /// Call `pop_peeked` to remove the returned descriptor chain from the queue.
peek(&mut self) -> Option<DescriptorChain>239     pub fn peek(&mut self) -> Option<DescriptorChain> {
240         let desc_addr = self
241             .desc_table
242             .checked_add((self.avail_index.index.0 as u64) * 16)
243             .expect("peeked address will not overflow");
244 
245         let desc = self
246             .mem
247             .read_obj_from_addr::<PackedDesc>(desc_addr)
248             .map_err(|e| {
249                 error!("failed to read desc {:#x}", desc_addr.offset());
250                 e
251             })
252             .ok()?;
253 
254         if !desc.is_available(self.avail_index.wrap_counter as u16) {
255             return None;
256         }
257 
258         // This fence ensures that subsequent reads from the descriptor do not
259         // get reordered and happen only after verifying the descriptor table is
260         // available.
261         fence(Ordering::SeqCst);
262 
263         let chain = PackedDescriptorChain::new(
264             &self.mem,
265             self.desc_table,
266             self.size,
267             self.avail_index.wrap_counter,
268             self.avail_index.index.0,
269         );
270 
271         match DescriptorChain::new(chain, &self.mem, self.avail_index.index.0) {
272             Ok(descriptor_chain) => Some(descriptor_chain),
273             Err(e) => {
274                 error!("{:#}", e);
275                 None
276             }
277         }
278     }
279 
280     /// Remove the first available descriptor chain from the queue.
281     /// This function should only be called immediately following `peek` and must be passed a
282     /// reference to the same `DescriptorChain` returned by the most recent `peek`.
pop_peeked(&mut self, descriptor_chain: &DescriptorChain)283     pub(super) fn pop_peeked(&mut self, descriptor_chain: &DescriptorChain) {
284         self.avail_index
285             .add_index(descriptor_chain.count, self.size());
286         if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {
287             self.set_avail_event(self.avail_index.to_desc());
288         }
289     }
290 
291     /// Write to first descriptor in descriptor chain to mark descriptor chain as used
add_used(&mut self, desc_chain: DescriptorChain, len: u32)292     pub fn add_used(&mut self, desc_chain: DescriptorChain, len: u32) {
293         let desc_index = desc_chain.index();
294         if desc_index >= self.size {
295             error!(
296                 "attempted to add out of bounds descriptor to used ring: {}",
297                 desc_index
298             );
299             return;
300         }
301 
302         let chain_id = desc_chain
303             .id
304             .expect("Packed descriptor chain should have id");
305 
306         let desc_addr = self
307             .desc_table
308             .checked_add(self.use_index.index.0 as u64 * 16)
309             .expect("Descriptor address should not overflow.");
310 
311         // Write to len field
312         self.mem
313             .write_obj_at_addr(
314                 len,
315                 desc_addr
316                     .checked_add(8)
317                     .expect("Descriptor address should not overflow."),
318             )
319             .unwrap();
320 
321         // Write to id field
322         self.mem
323             .write_obj_at_addr(
324                 chain_id,
325                 desc_addr
326                     .checked_add(12)
327                     .expect("Descriptor address should not overflow."),
328             )
329             .unwrap();
330 
331         let wrap_counter = self.use_index.wrap_counter;
332 
333         let mut flags: u16 = 0;
334         if wrap_counter {
335             flags = flags | VIRTQ_DESC_F_USED | VIRTQ_DESC_F_AVAIL;
336         }
337         if len > 0 {
338             flags |= VIRTQ_DESC_F_WRITE;
339         }
340 
341         // Writing to flags should come at the very end to avoid showing the
342         // driver fragmented descriptor data
343         fence(Ordering::SeqCst);
344 
345         self.mem
346             .write_obj_at_addr_volatile(flags, desc_addr.unchecked_add(14))
347             .unwrap();
348 
349         self.use_index.add_index(desc_chain.count, self.size());
350     }
351 
352     /// Returns if the queue should have an interrupt sent based on its state.
queue_wants_interrupt(&mut self) -> bool353     fn queue_wants_interrupt(&mut self) -> bool {
354         let driver_event = self.get_driver_event();
355         match driver_event.notification_type() {
356             PackedNotificationType::Enable => true,
357             PackedNotificationType::Disable => false,
358             PackedNotificationType::Desc(desc) => {
359                 if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) == 0 {
360                     warn!("This is undefined behavior. We should actually send error in this case");
361                     return true;
362                 }
363 
364                 // Reserved current use_index for next notify
365                 let old = self.signalled_used_index;
366                 self.signalled_used_index = self.use_index;
367 
368                 // Get desc_event_off and desc_event_wrap from driver event suppress area
369                 let event_index: PackedQueueIndex = PackedQueueIndex::new_from_desc(desc);
370 
371                 let event_idx = event_index.index;
372                 let old_idx = old.index;
373                 let new_idx = self.use_index.index;
374 
375                 // In qemu's implementation, there's an additional calculation,
376                 // need to verify its correctness.
377                 // if event_index.wrap_counter != self.use_index.wrap_counter {
378                 //     event_idx -= self.size() as u16;
379                 // }
380 
381                 (new_idx - event_idx - Wrapping(1)) < (new_idx - old_idx)
382             }
383         };
384         true
385     }
386 
387     /// inject interrupt into guest on this queue
388     /// return true: interrupt is injected into guest for this queue
389     ///        false: interrupt isn't injected
trigger_interrupt(&mut self, interrupt: &Interrupt) -> bool390     pub fn trigger_interrupt(&mut self, interrupt: &Interrupt) -> bool {
391         if self.queue_wants_interrupt() {
392             interrupt.signal_used_queue(self.vector);
393             true
394         } else {
395             false
396         }
397     }
398 
399     /// Acknowledges that this set of features should be enabled on this queue.
ack_features(&mut self, features: u64)400     pub fn ack_features(&mut self, features: u64) {
401         self.features |= features;
402     }
403 
404     /// TODO: b/290307056 - Implement snapshot for packed virtqueue,
405     /// add tests to validate.
snapshot(&self) -> Result<serde_json::Value>406     pub fn snapshot(&self) -> Result<serde_json::Value> {
407         bail!("Snapshot for packed virtqueue not implemented.");
408     }
409 
410     /// TODO: b/290307056 - Implement restore for packed virtqueue,
411     /// add tests to validate.
restore( _queue_value: serde_json::Value, _mem: &GuestMemory, _event: Event, ) -> Result<PackedQueue>412     pub fn restore(
413         _queue_value: serde_json::Value,
414         _mem: &GuestMemory,
415         _event: Event,
416     ) -> Result<PackedQueue> {
417         bail!("Restore for packed virtqueue not implemented.");
418     }
419 }
420