• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 pub mod net;
6 mod vsock;
7 
8 pub use crate::net::Net;
9 pub use crate::net::NetT;
10 pub use crate::vsock::Vsock;
11 
12 use std::alloc::Layout;
13 use std::fmt::{self, Display};
14 use std::io::Error as IoError;
15 use std::mem;
16 use std::ptr::null;
17 
18 use assertions::const_assert;
19 use base::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};
20 use base::{AsRawDescriptor, Event, LayoutAllocation};
21 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
22 
23 #[derive(Debug)]
24 pub enum Error {
25     /// Error opening vhost device.
26     VhostOpen(IoError),
27     /// Error while running ioctl.
28     IoctlError(IoError),
29     /// Invalid queue.
30     InvalidQueue,
31     /// Invalid descriptor table address.
32     DescriptorTableAddress(GuestMemoryError),
33     /// Invalid used address.
34     UsedAddress(GuestMemoryError),
35     /// Invalid available address.
36     AvailAddress(GuestMemoryError),
37     /// Invalid log address.
38     LogAddress(GuestMemoryError),
39 }
40 pub type Result<T> = std::result::Result<T, Error>;
41 
42 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result43     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44         use self::Error::*;
45 
46         match self {
47             VhostOpen(e) => write!(f, "failed to open vhost device: {}", e),
48             IoctlError(e) => write!(f, "failed to run ioctl: {}", e),
49             InvalidQueue => write!(f, "invalid queue"),
50             DescriptorTableAddress(e) => write!(f, "invalid descriptor table address: {}", e),
51             UsedAddress(e) => write!(f, "invalid used address: {}", e),
52             AvailAddress(e) => write!(f, "invalid available address: {}", e),
53             LogAddress(e) => write!(f, "invalid log address: {}", e),
54         }
55     }
56 }
57 
ioctl_result<T>() -> Result<T>58 fn ioctl_result<T>() -> Result<T> {
59     Err(Error::IoctlError(IoError::last_os_error()))
60 }
61 
62 /// An interface for setting up vhost-based virtio devices.  Vhost-based devices are different
63 /// from regular virtio devices because the host kernel takes care of handling all the data
64 /// transfer.  The device itself only needs to deal with setting up the kernel driver and
65 /// managing the control channel.
66 pub trait Vhost: AsRawDescriptor + std::marker::Sized {
67     /// Get the guest memory mapping.
mem(&self) -> &GuestMemory68     fn mem(&self) -> &GuestMemory;
69 
70     /// Set the current process as the owner of this file descriptor.
71     /// This must be run before any other vhost ioctls.
set_owner(&self) -> Result<()>72     fn set_owner(&self) -> Result<()> {
73         // This ioctl is called on a valid vhost_net fd and has its
74         // return value checked.
75         let ret = unsafe { ioctl(self, virtio_sys::VHOST_SET_OWNER()) };
76         if ret < 0 {
77             return ioctl_result();
78         }
79         Ok(())
80     }
81 
82     /// Get a bitmask of supported virtio/vhost features.
get_features(&self) -> Result<u64>83     fn get_features(&self) -> Result<u64> {
84         let mut avail_features: u64 = 0;
85         // This ioctl is called on a valid vhost_net fd and has its
86         // return value checked.
87         let ret = unsafe {
88             ioctl_with_mut_ref(self, virtio_sys::VHOST_GET_FEATURES(), &mut avail_features)
89         };
90         if ret < 0 {
91             return ioctl_result();
92         }
93         Ok(avail_features)
94     }
95 
96     /// Inform the vhost subsystem which features to enable. This should be a subset of
97     /// supported features from VHOST_GET_FEATURES.
98     ///
99     /// # Arguments
100     /// * `features` - Bitmask of features to set.
set_features(&self, features: u64) -> Result<()>101     fn set_features(&self, features: u64) -> Result<()> {
102         // This ioctl is called on a valid vhost_net fd and has its
103         // return value checked.
104         let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_FEATURES(), &features) };
105         if ret < 0 {
106             return ioctl_result();
107         }
108         Ok(())
109     }
110 
111     /// Set the guest memory mappings for vhost to use.
set_mem_table(&self) -> Result<()>112     fn set_mem_table(&self) -> Result<()> {
113         const SIZE_OF_MEMORY: usize = mem::size_of::<virtio_sys::vhost_memory>();
114         const SIZE_OF_REGION: usize = mem::size_of::<virtio_sys::vhost_memory_region>();
115         const ALIGN_OF_MEMORY: usize = mem::align_of::<virtio_sys::vhost_memory>();
116         const ALIGN_OF_REGION: usize = mem::align_of::<virtio_sys::vhost_memory_region>();
117         const_assert!(ALIGN_OF_MEMORY >= ALIGN_OF_REGION);
118 
119         let num_regions = self.mem().num_regions() as usize;
120         let size = SIZE_OF_MEMORY + num_regions * SIZE_OF_REGION;
121         let layout = Layout::from_size_align(size, ALIGN_OF_MEMORY).expect("impossible layout");
122         let mut allocation = LayoutAllocation::zeroed(layout);
123 
124         // Safe to obtain an exclusive reference because there are no other
125         // references to the allocation yet and all-zero is a valid bit pattern.
126         let vhost_memory = unsafe { allocation.as_mut::<virtio_sys::vhost_memory>() };
127 
128         vhost_memory.nregions = num_regions as u32;
129         // regions is a zero-length array, so taking a mut slice requires that
130         // we correctly specify the size to match the amount of backing memory.
131         let vhost_regions = unsafe { vhost_memory.regions.as_mut_slice(num_regions as usize) };
132 
133         let _ = self
134             .mem()
135             .with_regions::<_, ()>(|index, guest_addr, size, host_addr, _, _| {
136                 vhost_regions[index] = virtio_sys::vhost_memory_region {
137                     guest_phys_addr: guest_addr.offset() as u64,
138                     memory_size: size as u64,
139                     userspace_addr: host_addr as u64,
140                     flags_padding: 0u64,
141                 };
142                 Ok(())
143             });
144 
145         // This ioctl is called with a pointer that is valid for the lifetime
146         // of this function. The kernel will make its own copy of the memory
147         // tables. As always, check the return value.
148         let ret = unsafe { ioctl_with_ptr(self, virtio_sys::VHOST_SET_MEM_TABLE(), vhost_memory) };
149         if ret < 0 {
150             return ioctl_result();
151         }
152 
153         Ok(())
154 
155         // vhost_memory allocation is deallocated.
156     }
157 
158     /// Set the number of descriptors in the vring.
159     ///
160     /// # Arguments
161     /// * `queue_index` - Index of the queue to set descriptor count for.
162     /// * `num` - Number of descriptors in the queue.
set_vring_num(&self, queue_index: usize, num: u16) -> Result<()>163     fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> {
164         let vring_state = virtio_sys::vhost_vring_state {
165             index: queue_index as u32,
166             num: num as u32,
167         };
168 
169         // This ioctl is called on a valid vhost_net fd and has its
170         // return value checked.
171         let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_NUM(), &vring_state) };
172         if ret < 0 {
173             return ioctl_result();
174         }
175         Ok(())
176     }
177 
178     // TODO(smbarber): This is copypasta. Eliminate the copypasta.
179     #[allow(clippy::if_same_then_else)]
is_valid( &self, queue_max_size: u16, queue_size: u16, desc_addr: GuestAddress, avail_addr: GuestAddress, used_addr: GuestAddress, ) -> bool180     fn is_valid(
181         &self,
182         queue_max_size: u16,
183         queue_size: u16,
184         desc_addr: GuestAddress,
185         avail_addr: GuestAddress,
186         used_addr: GuestAddress,
187     ) -> bool {
188         let desc_table_size = 16 * queue_size as usize;
189         let avail_ring_size = 6 + 2 * queue_size as usize;
190         let used_ring_size = 6 + 8 * queue_size as usize;
191         if queue_size > queue_max_size || queue_size == 0 || (queue_size & (queue_size - 1)) != 0 {
192             false
193         } else if desc_addr
194             .checked_add(desc_table_size as u64)
195             .map_or(true, |v| !self.mem().address_in_range(v))
196         {
197             false
198         } else if avail_addr
199             .checked_add(avail_ring_size as u64)
200             .map_or(true, |v| !self.mem().address_in_range(v))
201         {
202             false
203         } else if used_addr
204             .checked_add(used_ring_size as u64)
205             .map_or(true, |v| !self.mem().address_in_range(v))
206         {
207             false
208         } else {
209             true
210         }
211     }
212 
213     /// Set the addresses for a given vring.
214     ///
215     /// # Arguments
216     /// * `queue_max_size` - Maximum queue size supported by the device.
217     /// * `queue_size` - Actual queue size negotiated by the driver.
218     /// * `queue_index` - Index of the queue to set addresses for.
219     /// * `flags` - Bitmask of vring flags.
220     /// * `desc_addr` - Descriptor table address.
221     /// * `used_addr` - Used ring buffer address.
222     /// * `avail_addr` - Available ring buffer address.
223     /// * `log_addr` - Optional address for logging.
set_vring_addr( &self, queue_max_size: u16, queue_size: u16, queue_index: usize, flags: u32, desc_addr: GuestAddress, used_addr: GuestAddress, avail_addr: GuestAddress, log_addr: Option<GuestAddress>, ) -> Result<()>224     fn set_vring_addr(
225         &self,
226         queue_max_size: u16,
227         queue_size: u16,
228         queue_index: usize,
229         flags: u32,
230         desc_addr: GuestAddress,
231         used_addr: GuestAddress,
232         avail_addr: GuestAddress,
233         log_addr: Option<GuestAddress>,
234     ) -> Result<()> {
235         // TODO(smbarber): Refactor out virtio from crosvm so we can
236         // validate a Queue struct directly.
237         if !self.is_valid(queue_max_size, queue_size, desc_addr, used_addr, avail_addr) {
238             return Err(Error::InvalidQueue);
239         }
240 
241         let desc_addr = self
242             .mem()
243             .get_host_address(desc_addr)
244             .map_err(Error::DescriptorTableAddress)?;
245         let used_addr = self
246             .mem()
247             .get_host_address(used_addr)
248             .map_err(Error::UsedAddress)?;
249         let avail_addr = self
250             .mem()
251             .get_host_address(avail_addr)
252             .map_err(Error::AvailAddress)?;
253         let log_addr = match log_addr {
254             None => null(),
255             Some(a) => self.mem().get_host_address(a).map_err(Error::LogAddress)?,
256         };
257 
258         let vring_addr = virtio_sys::vhost_vring_addr {
259             index: queue_index as u32,
260             flags,
261             desc_user_addr: desc_addr as u64,
262             used_user_addr: used_addr as u64,
263             avail_user_addr: avail_addr as u64,
264             log_guest_addr: log_addr as u64,
265         };
266 
267         // This ioctl is called on a valid vhost_net fd and has its
268         // return value checked.
269         let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_ADDR(), &vring_addr) };
270         if ret < 0 {
271             return ioctl_result();
272         }
273         Ok(())
274     }
275 
276     /// Set the first index to look for available descriptors.
277     ///
278     /// # Arguments
279     /// * `queue_index` - Index of the queue to modify.
280     /// * `num` - Index where available descriptors start.
set_vring_base(&self, queue_index: usize, num: u16) -> Result<()>281     fn set_vring_base(&self, queue_index: usize, num: u16) -> Result<()> {
282         let vring_state = virtio_sys::vhost_vring_state {
283             index: queue_index as u32,
284             num: num as u32,
285         };
286 
287         // This ioctl is called on a valid vhost_net fd and has its
288         // return value checked.
289         let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_BASE(), &vring_state) };
290         if ret < 0 {
291             return ioctl_result();
292         }
293         Ok(())
294     }
295 
296     /// Set the event to trigger when buffers have been used by the host.
297     ///
298     /// # Arguments
299     /// * `queue_index` - Index of the queue to modify.
300     /// * `event` - Event to trigger.
set_vring_call(&self, queue_index: usize, event: &Event) -> Result<()>301     fn set_vring_call(&self, queue_index: usize, event: &Event) -> Result<()> {
302         let vring_file = virtio_sys::vhost_vring_file {
303             index: queue_index as u32,
304             event: event.as_raw_descriptor(),
305         };
306 
307         // This ioctl is called on a valid vhost_net fd and has its
308         // return value checked.
309         let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_CALL(), &vring_file) };
310         if ret < 0 {
311             return ioctl_result();
312         }
313         Ok(())
314     }
315 
316     /// Set the event that will be signaled by the guest when buffers are
317     /// available for the host to process.
318     ///
319     /// # Arguments
320     /// * `queue_index` - Index of the queue to modify.
321     /// * `fd` - Event that will be signaled from guest.
set_vring_kick(&self, queue_index: usize, event: &Event) -> Result<()>322     fn set_vring_kick(&self, queue_index: usize, event: &Event) -> Result<()> {
323         let vring_file = virtio_sys::vhost_vring_file {
324             index: queue_index as u32,
325             event: event.as_raw_descriptor(),
326         };
327 
328         // This ioctl is called on a valid vhost_net fd and has its
329         // return value checked.
330         let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_KICK(), &vring_file) };
331         if ret < 0 {
332             return ioctl_result();
333         }
334         Ok(())
335     }
336 }
337 
338 #[cfg(test)]
339 mod tests {
340     use super::*;
341 
342     use crate::net::fakes::FakeNet;
343     use net_util::fakes::FakeTap;
344     use std::{path::PathBuf, result};
345     use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
346 
create_guest_memory() -> result::Result<GuestMemory, GuestMemoryError>347     fn create_guest_memory() -> result::Result<GuestMemory, GuestMemoryError> {
348         let start_addr1 = GuestAddress(0x0);
349         let start_addr2 = GuestAddress(0x1000);
350         GuestMemory::new(&vec![(start_addr1, 0x1000), (start_addr2, 0x4000)])
351     }
352 
assert_ok_or_known_failure<T>(res: Result<T>)353     fn assert_ok_or_known_failure<T>(res: Result<T>) {
354         match &res {
355             // FakeNet won't respond to ioctl's
356             Ok(_t) => {}
357             Err(Error::IoctlError(ioe)) if ioe.raw_os_error().unwrap() == 25 => {}
358             Err(e) => panic!("Unexpected Error:\n{}", e),
359         }
360     }
361 
create_fake_vhost_net() -> FakeNet<FakeTap>362     fn create_fake_vhost_net() -> FakeNet<FakeTap> {
363         let gm = create_guest_memory().unwrap();
364         FakeNet::<FakeTap>::new(&PathBuf::from(""), &gm).unwrap()
365     }
366 
367     #[test]
test_create_fake_vhost_net()368     fn test_create_fake_vhost_net() {
369         create_fake_vhost_net();
370     }
371 
372     #[test]
set_owner()373     fn set_owner() {
374         let vhost_net = create_fake_vhost_net();
375         let res = vhost_net.set_owner();
376         assert_ok_or_known_failure(res);
377     }
378 
379     #[test]
get_features()380     fn get_features() {
381         let vhost_net = create_fake_vhost_net();
382         let res = vhost_net.get_features();
383         assert_ok_or_known_failure(res);
384     }
385 
386     #[test]
set_features()387     fn set_features() {
388         let vhost_net = create_fake_vhost_net();
389         let res = vhost_net.set_features(0);
390         assert_ok_or_known_failure(res);
391     }
392 
393     #[test]
set_mem_table()394     fn set_mem_table() {
395         let vhost_net = create_fake_vhost_net();
396         let res = vhost_net.set_mem_table();
397         assert_ok_or_known_failure(res);
398     }
399 
400     #[test]
set_vring_num()401     fn set_vring_num() {
402         let vhost_net = create_fake_vhost_net();
403         let res = vhost_net.set_vring_num(0, 1);
404         assert_ok_or_known_failure(res);
405     }
406 
407     #[test]
set_vring_addr()408     fn set_vring_addr() {
409         let vhost_net = create_fake_vhost_net();
410         let res = vhost_net.set_vring_addr(
411             1,
412             1,
413             0,
414             0x0,
415             GuestAddress(0x0),
416             GuestAddress(0x0),
417             GuestAddress(0x0),
418             None,
419         );
420         assert_ok_or_known_failure(res);
421     }
422 
423     #[test]
set_vring_base()424     fn set_vring_base() {
425         let vhost_net = create_fake_vhost_net();
426         let res = vhost_net.set_vring_base(0, 1);
427         assert_ok_or_known_failure(res);
428     }
429 
430     #[test]
set_vring_call()431     fn set_vring_call() {
432         let vhost_net = create_fake_vhost_net();
433         let res = vhost_net.set_vring_call(0, &Event::new().unwrap());
434         assert_ok_or_known_failure(res);
435     }
436 
437     #[test]
set_vring_kick()438     fn set_vring_kick() {
439         let vhost_net = create_fake_vhost_net();
440         let res = vhost_net.set_vring_kick(0, &Event::new().unwrap());
441         assert_ok_or_known_failure(res);
442     }
443 }
444