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