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