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