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