xref: /cloud-hypervisor/vm-virtio/src/queue.rs (revision 190d90196fff389b60b93b57acf958957b71b249)
182464347SSamuel Ortiz // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
282464347SSamuel Ortiz //
382464347SSamuel Ortiz // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
482464347SSamuel Ortiz // Use of this source code is governed by a BSD-style license that can be
5040ea543SSamuel Ortiz // found in the LICENSE-BSD-3-Clause file.
6040ea543SSamuel Ortiz //
7040ea543SSamuel Ortiz // Copyright © 2019 Intel Corporation
8040ea543SSamuel Ortiz //
9040ea543SSamuel Ortiz // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
1082464347SSamuel Ortiz 
112a6eb31dSRob Bradford pub mod testing {
1282464347SSamuel Ortiz     use std::marker::PhantomData;
1382464347SSamuel Ortiz     use std::mem;
1488a9f799SRob Bradford 
15*190d9019SJinank Jain     use virtio_queue::desc::split::VirtqUsedElem;
16*190d9019SJinank Jain     use virtio_queue::{Queue, QueueT};
1761e57e1cSRuoqing He     use vm_memory::bitmap::AtomicBitmap;
1861e57e1cSRuoqing He     use vm_memory::{Address, Bytes, GuestAddress, GuestUsize};
19b5bcdbafSBo Chen 
20b5bcdbafSBo Chen     type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
2182464347SSamuel Ortiz 
2282464347SSamuel Ortiz     // Represents a location in GuestMemoryMmap which holds a given type.
2382464347SSamuel Ortiz     pub struct SomeplaceInMemory<'a, T> {
2482464347SSamuel Ortiz         pub location: GuestAddress,
2582464347SSamuel Ortiz         mem: &'a GuestMemoryMmap,
2682464347SSamuel Ortiz         phantom: PhantomData<*const T>,
2782464347SSamuel Ortiz     }
2882464347SSamuel Ortiz 
2982464347SSamuel Ortiz     // The ByteValued trait is required to use mem.read_obj and write_obj.
3082464347SSamuel Ortiz     impl<'a, T> SomeplaceInMemory<'a, T>
3182464347SSamuel Ortiz     where
3282464347SSamuel Ortiz         T: vm_memory::ByteValued,
3382464347SSamuel Ortiz     {
new(location: GuestAddress, mem: &'a GuestMemoryMmap) -> Self3482464347SSamuel Ortiz         fn new(location: GuestAddress, mem: &'a GuestMemoryMmap) -> Self {
3582464347SSamuel Ortiz             SomeplaceInMemory {
3682464347SSamuel Ortiz                 location,
3782464347SSamuel Ortiz                 mem,
3882464347SSamuel Ortiz                 phantom: PhantomData,
3982464347SSamuel Ortiz             }
4082464347SSamuel Ortiz         }
4182464347SSamuel Ortiz 
4282464347SSamuel Ortiz         // Reads from the actual memory location.
get(&self) -> T4382464347SSamuel Ortiz         pub fn get(&self) -> T {
4482464347SSamuel Ortiz             self.mem.read_obj(self.location).unwrap()
4582464347SSamuel Ortiz         }
4682464347SSamuel Ortiz 
4782464347SSamuel Ortiz         // Writes to the actual memory location.
set(&self, val: T)4882464347SSamuel Ortiz         pub fn set(&self, val: T) {
4982464347SSamuel Ortiz             self.mem.write_obj(val, self.location).unwrap()
5082464347SSamuel Ortiz         }
5182464347SSamuel Ortiz 
5282464347SSamuel Ortiz         // This function returns a place in memory which holds a value of type U, and starts
5382464347SSamuel Ortiz         // offset bytes after the current location.
map_offset<U>(&self, offset: GuestUsize) -> SomeplaceInMemory<'a, U>5482464347SSamuel Ortiz         fn map_offset<U>(&self, offset: GuestUsize) -> SomeplaceInMemory<'a, U> {
5582464347SSamuel Ortiz             SomeplaceInMemory {
5682464347SSamuel Ortiz                 location: self.location.checked_add(offset).unwrap(),
5782464347SSamuel Ortiz                 mem: self.mem,
5882464347SSamuel Ortiz                 phantom: PhantomData,
5982464347SSamuel Ortiz             }
6082464347SSamuel Ortiz         }
6182464347SSamuel Ortiz 
6282464347SSamuel Ortiz         // This function returns a place in memory which holds a value of type U, and starts
6382464347SSamuel Ortiz         // immediately after the end of self (which is location + sizeof(T)).
next_place<U>(&self) -> SomeplaceInMemory<'a, U>6482464347SSamuel Ortiz         fn next_place<U>(&self) -> SomeplaceInMemory<'a, U> {
6582464347SSamuel Ortiz             self.map_offset::<U>(mem::size_of::<T>() as u64)
6682464347SSamuel Ortiz         }
6782464347SSamuel Ortiz 
end(&self) -> GuestAddress6882464347SSamuel Ortiz         fn end(&self) -> GuestAddress {
6982464347SSamuel Ortiz             self.location
7082464347SSamuel Ortiz                 .checked_add(mem::size_of::<T>() as u64)
7182464347SSamuel Ortiz                 .unwrap()
7282464347SSamuel Ortiz         }
7382464347SSamuel Ortiz     }
7482464347SSamuel Ortiz 
7582464347SSamuel Ortiz     // Represents a virtio descriptor in guest memory.
7682464347SSamuel Ortiz     pub struct VirtqDesc<'a> {
7782464347SSamuel Ortiz         pub addr: SomeplaceInMemory<'a, u64>,
7882464347SSamuel Ortiz         pub len: SomeplaceInMemory<'a, u32>,
7982464347SSamuel Ortiz         pub flags: SomeplaceInMemory<'a, u16>,
8082464347SSamuel Ortiz         pub next: SomeplaceInMemory<'a, u16>,
8182464347SSamuel Ortiz     }
8282464347SSamuel Ortiz 
8382464347SSamuel Ortiz     impl<'a> VirtqDesc<'a> {
new(start: GuestAddress, mem: &'a GuestMemoryMmap) -> Self842a6eb31dSRob Bradford         pub fn new(start: GuestAddress, mem: &'a GuestMemoryMmap) -> Self {
8582464347SSamuel Ortiz             assert_eq!(start.0 & 0xf, 0);
8682464347SSamuel Ortiz 
8782464347SSamuel Ortiz             let addr = SomeplaceInMemory::new(start, mem);
8882464347SSamuel Ortiz             let len = addr.next_place();
8982464347SSamuel Ortiz             let flags = len.next_place();
9082464347SSamuel Ortiz             let next = flags.next_place();
9182464347SSamuel Ortiz 
9282464347SSamuel Ortiz             VirtqDesc {
9382464347SSamuel Ortiz                 addr,
9482464347SSamuel Ortiz                 len,
9582464347SSamuel Ortiz                 flags,
9682464347SSamuel Ortiz                 next,
9782464347SSamuel Ortiz             }
9882464347SSamuel Ortiz         }
9982464347SSamuel Ortiz 
start(&self) -> GuestAddress10082464347SSamuel Ortiz         fn start(&self) -> GuestAddress {
10182464347SSamuel Ortiz             self.addr.location
10282464347SSamuel Ortiz         }
10382464347SSamuel Ortiz 
end(&self) -> GuestAddress10482464347SSamuel Ortiz         fn end(&self) -> GuestAddress {
10582464347SSamuel Ortiz             self.next.end()
10682464347SSamuel Ortiz         }
10782464347SSamuel Ortiz 
set(&self, addr: u64, len: u32, flags: u16, next: u16)10882464347SSamuel Ortiz         pub fn set(&self, addr: u64, len: u32, flags: u16, next: u16) {
10982464347SSamuel Ortiz             self.addr.set(addr);
11082464347SSamuel Ortiz             self.len.set(len);
11182464347SSamuel Ortiz             self.flags.set(flags);
11282464347SSamuel Ortiz             self.next.set(next);
11382464347SSamuel Ortiz         }
11482464347SSamuel Ortiz     }
11582464347SSamuel Ortiz 
11682464347SSamuel Ortiz     // Represents a virtio queue ring. The only difference between the used and available rings,
11782464347SSamuel Ortiz     // is the ring element type.
11882464347SSamuel Ortiz     pub struct VirtqRing<'a, T> {
11982464347SSamuel Ortiz         pub flags: SomeplaceInMemory<'a, u16>,
12082464347SSamuel Ortiz         pub idx: SomeplaceInMemory<'a, u16>,
12182464347SSamuel Ortiz         pub ring: Vec<SomeplaceInMemory<'a, T>>,
12282464347SSamuel Ortiz         pub event: SomeplaceInMemory<'a, u16>,
12382464347SSamuel Ortiz     }
12482464347SSamuel Ortiz 
12582464347SSamuel Ortiz     impl<'a, T> VirtqRing<'a, T>
12682464347SSamuel Ortiz     where
12782464347SSamuel Ortiz         T: vm_memory::ByteValued,
12882464347SSamuel Ortiz     {
new( start: GuestAddress, mem: &'a GuestMemoryMmap, qsize: u16, alignment: GuestUsize, ) -> Self12982464347SSamuel Ortiz         fn new(
13082464347SSamuel Ortiz             start: GuestAddress,
13182464347SSamuel Ortiz             mem: &'a GuestMemoryMmap,
13282464347SSamuel Ortiz             qsize: u16,
13382464347SSamuel Ortiz             alignment: GuestUsize,
13482464347SSamuel Ortiz         ) -> Self {
13582464347SSamuel Ortiz             assert_eq!(start.0 & (alignment - 1), 0);
13682464347SSamuel Ortiz 
13782464347SSamuel Ortiz             let flags = SomeplaceInMemory::new(start, mem);
13882464347SSamuel Ortiz             let idx = flags.next_place();
13982464347SSamuel Ortiz 
14082464347SSamuel Ortiz             let mut ring = Vec::with_capacity(qsize as usize);
14182464347SSamuel Ortiz 
14282464347SSamuel Ortiz             ring.push(idx.next_place());
14382464347SSamuel Ortiz 
14482464347SSamuel Ortiz             for _ in 1..qsize as usize {
14582464347SSamuel Ortiz                 let x = ring.last().unwrap().next_place();
14682464347SSamuel Ortiz                 ring.push(x)
14782464347SSamuel Ortiz             }
14882464347SSamuel Ortiz 
14982464347SSamuel Ortiz             let event = ring.last().unwrap().next_place();
15082464347SSamuel Ortiz 
15182464347SSamuel Ortiz             flags.set(0);
15282464347SSamuel Ortiz             idx.set(0);
15382464347SSamuel Ortiz             event.set(0);
15482464347SSamuel Ortiz 
15582464347SSamuel Ortiz             VirtqRing {
15682464347SSamuel Ortiz                 flags,
15782464347SSamuel Ortiz                 idx,
15882464347SSamuel Ortiz                 ring,
15982464347SSamuel Ortiz                 event,
16082464347SSamuel Ortiz             }
16182464347SSamuel Ortiz         }
16282464347SSamuel Ortiz 
end(&self) -> GuestAddress16382464347SSamuel Ortiz         pub fn end(&self) -> GuestAddress {
16482464347SSamuel Ortiz             self.event.end()
16582464347SSamuel Ortiz         }
16682464347SSamuel Ortiz     }
16782464347SSamuel Ortiz 
16882464347SSamuel Ortiz     pub type VirtqAvail<'a> = VirtqRing<'a, u16>;
16982464347SSamuel Ortiz     pub type VirtqUsed<'a> = VirtqRing<'a, VirtqUsedElem>;
17082464347SSamuel Ortiz 
17182464347SSamuel Ortiz     pub struct VirtQueue<'a> {
17282464347SSamuel Ortiz         pub dtable: Vec<VirtqDesc<'a>>,
17382464347SSamuel Ortiz         pub avail: VirtqAvail<'a>,
17482464347SSamuel Ortiz         pub used: VirtqUsed<'a>,
1750249e864SSebastien Boeuf         pub mem: &'a GuestMemoryMmap,
17682464347SSamuel Ortiz     }
17782464347SSamuel Ortiz 
17882464347SSamuel Ortiz     impl<'a> VirtQueue<'a> {
17982464347SSamuel Ortiz         // We try to make sure things are aligned properly :-s
new(start: GuestAddress, mem: &'a GuestMemoryMmap, qsize: u16) -> Self18082464347SSamuel Ortiz         pub fn new(start: GuestAddress, mem: &'a GuestMemoryMmap, qsize: u16) -> Self {
18182464347SSamuel Ortiz             // power of 2?
182b41daddcSRuoqing He             assert!(qsize.is_power_of_two());
18382464347SSamuel Ortiz 
18482464347SSamuel Ortiz             let mut dtable = Vec::with_capacity(qsize as usize);
18582464347SSamuel Ortiz 
18682464347SSamuel Ortiz             let mut end = start;
18782464347SSamuel Ortiz 
18882464347SSamuel Ortiz             for _ in 0..qsize {
18982464347SSamuel Ortiz                 let d = VirtqDesc::new(end, mem);
19082464347SSamuel Ortiz                 end = d.end();
19182464347SSamuel Ortiz                 dtable.push(d);
19282464347SSamuel Ortiz             }
19382464347SSamuel Ortiz 
19482464347SSamuel Ortiz             const AVAIL_ALIGN: u64 = 2;
19582464347SSamuel Ortiz 
19682464347SSamuel Ortiz             let avail = VirtqAvail::new(end, mem, qsize, AVAIL_ALIGN);
19782464347SSamuel Ortiz 
19882464347SSamuel Ortiz             const USED_ALIGN: u64 = 4;
19982464347SSamuel Ortiz 
20082464347SSamuel Ortiz             let mut x = avail.end().0;
20182464347SSamuel Ortiz             x = (x + USED_ALIGN - 1) & !(USED_ALIGN - 1);
20282464347SSamuel Ortiz 
20382464347SSamuel Ortiz             let used = VirtqUsed::new(GuestAddress(x), mem, qsize, USED_ALIGN);
20482464347SSamuel Ortiz 
20582464347SSamuel Ortiz             VirtQueue {
20682464347SSamuel Ortiz                 dtable,
20782464347SSamuel Ortiz                 avail,
20882464347SSamuel Ortiz                 used,
2090249e864SSebastien Boeuf                 mem,
21082464347SSamuel Ortiz             }
21182464347SSamuel Ortiz         }
21282464347SSamuel Ortiz 
size(&self) -> u1621382464347SSamuel Ortiz         fn size(&self) -> u16 {
21482464347SSamuel Ortiz             self.dtable.len() as u16
21582464347SSamuel Ortiz         }
21682464347SSamuel Ortiz 
dtable_start(&self) -> GuestAddress2172a6eb31dSRob Bradford         pub fn dtable_start(&self) -> GuestAddress {
21882464347SSamuel Ortiz             self.dtable.first().unwrap().start()
21982464347SSamuel Ortiz         }
22082464347SSamuel Ortiz 
avail_start(&self) -> GuestAddress2212a6eb31dSRob Bradford         pub fn avail_start(&self) -> GuestAddress {
22282464347SSamuel Ortiz             self.avail.flags.location
22382464347SSamuel Ortiz         }
22482464347SSamuel Ortiz 
used_start(&self) -> GuestAddress2252a6eb31dSRob Bradford         pub fn used_start(&self) -> GuestAddress {
22682464347SSamuel Ortiz             self.used.flags.location
22782464347SSamuel Ortiz         }
22882464347SSamuel Ortiz 
22982464347SSamuel Ortiz         // Creates a new Queue, using the underlying memory regions represented by the VirtQueue.
create_queue(&self) -> Queue230a423bf13SSebastien Boeuf         pub fn create_queue(&self) -> Queue {
231a423bf13SSebastien Boeuf             let mut q = Queue::new(self.size()).unwrap();
23282464347SSamuel Ortiz 
233a423bf13SSebastien Boeuf             q.set_size(self.size());
234a423bf13SSebastien Boeuf             q.set_ready(true);
235a423bf13SSebastien Boeuf             q.try_set_desc_table_address(self.dtable_start()).unwrap();
236a423bf13SSebastien Boeuf             q.try_set_avail_ring_address(self.avail_start()).unwrap();
237a423bf13SSebastien Boeuf             q.try_set_used_ring_address(self.used_start()).unwrap();
23882464347SSamuel Ortiz 
23982464347SSamuel Ortiz             q
24082464347SSamuel Ortiz         }
24182464347SSamuel Ortiz 
start(&self) -> GuestAddress24282464347SSamuel Ortiz         pub fn start(&self) -> GuestAddress {
24382464347SSamuel Ortiz             self.dtable_start()
24482464347SSamuel Ortiz         }
24582464347SSamuel Ortiz 
end(&self) -> GuestAddress24682464347SSamuel Ortiz         pub fn end(&self) -> GuestAddress {
24782464347SSamuel Ortiz             self.used.end()
24882464347SSamuel Ortiz         }
24982464347SSamuel Ortiz     }
2502a6eb31dSRob Bradford }
251