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