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