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