xref: /cloud-hypervisor/vm-virtio/src/queue.rs (revision 190d90196fff389b60b93b57acf958957b71b249)
1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 //
3 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE-BSD-3-Clause file.
6 //
7 // Copyright © 2019 Intel Corporation
8 //
9 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
10 
11 pub mod testing {
12     use std::marker::PhantomData;
13     use std::mem;
14 
15     use virtio_queue::desc::split::VirtqUsedElem;
16     use virtio_queue::{Queue, QueueT};
17     use vm_memory::bitmap::AtomicBitmap;
18     use vm_memory::{Address, Bytes, GuestAddress, GuestUsize};
19 
20     type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
21 
22     // Represents a location in GuestMemoryMmap which holds a given type.
23     pub struct SomeplaceInMemory<'a, T> {
24         pub location: GuestAddress,
25         mem: &'a GuestMemoryMmap,
26         phantom: PhantomData<*const T>,
27     }
28 
29     // The ByteValued trait is required to use mem.read_obj and write_obj.
30     impl<'a, T> SomeplaceInMemory<'a, T>
31     where
32         T: vm_memory::ByteValued,
33     {
new(location: GuestAddress, mem: &'a GuestMemoryMmap) -> Self34         fn new(location: GuestAddress, mem: &'a GuestMemoryMmap) -> Self {
35             SomeplaceInMemory {
36                 location,
37                 mem,
38                 phantom: PhantomData,
39             }
40         }
41 
42         // Reads from the actual memory location.
get(&self) -> T43         pub fn get(&self) -> T {
44             self.mem.read_obj(self.location).unwrap()
45         }
46 
47         // Writes to the actual memory location.
set(&self, val: T)48         pub fn set(&self, val: T) {
49             self.mem.write_obj(val, self.location).unwrap()
50         }
51 
52         // This function returns a place in memory which holds a value of type U, and starts
53         // offset bytes after the current location.
map_offset<U>(&self, offset: GuestUsize) -> SomeplaceInMemory<'a, U>54         fn map_offset<U>(&self, offset: GuestUsize) -> SomeplaceInMemory<'a, U> {
55             SomeplaceInMemory {
56                 location: self.location.checked_add(offset).unwrap(),
57                 mem: self.mem,
58                 phantom: PhantomData,
59             }
60         }
61 
62         // This function returns a place in memory which holds a value of type U, and starts
63         // immediately after the end of self (which is location + sizeof(T)).
next_place<U>(&self) -> SomeplaceInMemory<'a, U>64         fn next_place<U>(&self) -> SomeplaceInMemory<'a, U> {
65             self.map_offset::<U>(mem::size_of::<T>() as u64)
66         }
67 
end(&self) -> GuestAddress68         fn end(&self) -> GuestAddress {
69             self.location
70                 .checked_add(mem::size_of::<T>() as u64)
71                 .unwrap()
72         }
73     }
74 
75     // Represents a virtio descriptor in guest memory.
76     pub struct VirtqDesc<'a> {
77         pub addr: SomeplaceInMemory<'a, u64>,
78         pub len: SomeplaceInMemory<'a, u32>,
79         pub flags: SomeplaceInMemory<'a, u16>,
80         pub next: SomeplaceInMemory<'a, u16>,
81     }
82 
83     impl<'a> VirtqDesc<'a> {
new(start: GuestAddress, mem: &'a GuestMemoryMmap) -> Self84         pub fn new(start: GuestAddress, mem: &'a GuestMemoryMmap) -> Self {
85             assert_eq!(start.0 & 0xf, 0);
86 
87             let addr = SomeplaceInMemory::new(start, mem);
88             let len = addr.next_place();
89             let flags = len.next_place();
90             let next = flags.next_place();
91 
92             VirtqDesc {
93                 addr,
94                 len,
95                 flags,
96                 next,
97             }
98         }
99 
start(&self) -> GuestAddress100         fn start(&self) -> GuestAddress {
101             self.addr.location
102         }
103 
end(&self) -> GuestAddress104         fn end(&self) -> GuestAddress {
105             self.next.end()
106         }
107 
set(&self, addr: u64, len: u32, flags: u16, next: u16)108         pub fn set(&self, addr: u64, len: u32, flags: u16, next: u16) {
109             self.addr.set(addr);
110             self.len.set(len);
111             self.flags.set(flags);
112             self.next.set(next);
113         }
114     }
115 
116     // Represents a virtio queue ring. The only difference between the used and available rings,
117     // is the ring element type.
118     pub struct VirtqRing<'a, T> {
119         pub flags: SomeplaceInMemory<'a, u16>,
120         pub idx: SomeplaceInMemory<'a, u16>,
121         pub ring: Vec<SomeplaceInMemory<'a, T>>,
122         pub event: SomeplaceInMemory<'a, u16>,
123     }
124 
125     impl<'a, T> VirtqRing<'a, T>
126     where
127         T: vm_memory::ByteValued,
128     {
new( start: GuestAddress, mem: &'a GuestMemoryMmap, qsize: u16, alignment: GuestUsize, ) -> Self129         fn new(
130             start: GuestAddress,
131             mem: &'a GuestMemoryMmap,
132             qsize: u16,
133             alignment: GuestUsize,
134         ) -> Self {
135             assert_eq!(start.0 & (alignment - 1), 0);
136 
137             let flags = SomeplaceInMemory::new(start, mem);
138             let idx = flags.next_place();
139 
140             let mut ring = Vec::with_capacity(qsize as usize);
141 
142             ring.push(idx.next_place());
143 
144             for _ in 1..qsize as usize {
145                 let x = ring.last().unwrap().next_place();
146                 ring.push(x)
147             }
148 
149             let event = ring.last().unwrap().next_place();
150 
151             flags.set(0);
152             idx.set(0);
153             event.set(0);
154 
155             VirtqRing {
156                 flags,
157                 idx,
158                 ring,
159                 event,
160             }
161         }
162 
end(&self) -> GuestAddress163         pub fn end(&self) -> GuestAddress {
164             self.event.end()
165         }
166     }
167 
168     pub type VirtqAvail<'a> = VirtqRing<'a, u16>;
169     pub type VirtqUsed<'a> = VirtqRing<'a, VirtqUsedElem>;
170 
171     pub struct VirtQueue<'a> {
172         pub dtable: Vec<VirtqDesc<'a>>,
173         pub avail: VirtqAvail<'a>,
174         pub used: VirtqUsed<'a>,
175         pub mem: &'a GuestMemoryMmap,
176     }
177 
178     impl<'a> VirtQueue<'a> {
179         // We try to make sure things are aligned properly :-s
new(start: GuestAddress, mem: &'a GuestMemoryMmap, qsize: u16) -> Self180         pub fn new(start: GuestAddress, mem: &'a GuestMemoryMmap, qsize: u16) -> Self {
181             // power of 2?
182             assert!(qsize.is_power_of_two());
183 
184             let mut dtable = Vec::with_capacity(qsize as usize);
185 
186             let mut end = start;
187 
188             for _ in 0..qsize {
189                 let d = VirtqDesc::new(end, mem);
190                 end = d.end();
191                 dtable.push(d);
192             }
193 
194             const AVAIL_ALIGN: u64 = 2;
195 
196             let avail = VirtqAvail::new(end, mem, qsize, AVAIL_ALIGN);
197 
198             const USED_ALIGN: u64 = 4;
199 
200             let mut x = avail.end().0;
201             x = (x + USED_ALIGN - 1) & !(USED_ALIGN - 1);
202 
203             let used = VirtqUsed::new(GuestAddress(x), mem, qsize, USED_ALIGN);
204 
205             VirtQueue {
206                 dtable,
207                 avail,
208                 used,
209                 mem,
210             }
211         }
212 
size(&self) -> u16213         fn size(&self) -> u16 {
214             self.dtable.len() as u16
215         }
216 
dtable_start(&self) -> GuestAddress217         pub fn dtable_start(&self) -> GuestAddress {
218             self.dtable.first().unwrap().start()
219         }
220 
avail_start(&self) -> GuestAddress221         pub fn avail_start(&self) -> GuestAddress {
222             self.avail.flags.location
223         }
224 
used_start(&self) -> GuestAddress225         pub fn used_start(&self) -> GuestAddress {
226             self.used.flags.location
227         }
228 
229         // Creates a new Queue, using the underlying memory regions represented by the VirtQueue.
create_queue(&self) -> Queue230         pub fn create_queue(&self) -> Queue {
231             let mut q = Queue::new(self.size()).unwrap();
232 
233             q.set_size(self.size());
234             q.set_ready(true);
235             q.try_set_desc_table_address(self.dtable_start()).unwrap();
236             q.try_set_avail_ring_address(self.avail_start()).unwrap();
237             q.try_set_used_ring_address(self.used_start()).unwrap();
238 
239             q
240         }
241 
start(&self) -> GuestAddress242         pub fn start(&self) -> GuestAddress {
243             self.dtable_start()
244         }
245 
end(&self) -> GuestAddress246         pub fn end(&self) -> GuestAddress {
247             self.used.end()
248         }
249     }
250 }
251