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