| // Copyright (C) 2019 Alibaba Cloud. All rights reserved. |
| // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause |
| // |
| // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE-BSD-Google file. |
| |
| //! Kernel-based vhost-vsock backend. |
| |
| use std::fs::{File, OpenOptions}; |
| use std::os::unix::fs::OpenOptionsExt; |
| use std::os::unix::io::{AsRawFd, RawFd}; |
| |
| use vm_memory::GuestAddressSpace; |
| use vmm_sys_util::ioctl::ioctl_with_ref; |
| |
| use super::vhost_binding::{VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; |
| use super::{ioctl_result, Error, Result, VhostKernBackend}; |
| use crate::vsock::VhostVsock; |
| |
| const VHOST_PATH: &str = "/dev/vhost-vsock"; |
| |
| /// Handle for running VHOST_VSOCK ioctls. |
| pub struct Vsock<AS: GuestAddressSpace> { |
| fd: File, |
| mem: AS, |
| } |
| |
| impl<AS: GuestAddressSpace> Vsock<AS> { |
| /// Open a handle to a new VHOST-VSOCK instance. |
| pub fn new(mem: AS) -> Result<Self> { |
| Ok(Vsock { |
| fd: OpenOptions::new() |
| .read(true) |
| .write(true) |
| .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK) |
| .open(VHOST_PATH) |
| .map_err(Error::VhostOpen)?, |
| mem, |
| }) |
| } |
| |
| fn set_running(&self, running: bool) -> Result<()> { |
| let on: ::std::os::raw::c_int = if running { 1 } else { 0 }; |
| |
| // SAFETY: This ioctl is called on a valid vhost-vsock fd and has its |
| // return value checked. |
| let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_RUNNING(), &on) }; |
| ioctl_result(ret, ()) |
| } |
| } |
| |
| impl<AS: GuestAddressSpace> VhostVsock for Vsock<AS> { |
| fn set_guest_cid(&self, cid: u64) -> Result<()> { |
| // SAFETY: This ioctl is called on a valid vhost-vsock fd and has its |
| // return value checked. |
| let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_GUEST_CID(), &cid) }; |
| ioctl_result(ret, ()) |
| } |
| |
| fn start(&self) -> Result<()> { |
| self.set_running(true) |
| } |
| |
| fn stop(&self) -> Result<()> { |
| self.set_running(false) |
| } |
| } |
| |
| impl<AS: GuestAddressSpace> VhostKernBackend for Vsock<AS> { |
| type AS = AS; |
| |
| fn mem(&self) -> &Self::AS { |
| &self.mem |
| } |
| } |
| |
| impl<AS: GuestAddressSpace> AsRawFd for Vsock<AS> { |
| fn as_raw_fd(&self) -> RawFd { |
| self.fd.as_raw_fd() |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap}; |
| use vmm_sys_util::eventfd::EventFd; |
| |
| use super::*; |
| use crate::{ |
| VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo, VringConfigData, |
| }; |
| |
| #[test] |
| fn test_vsock_new_device() { |
| let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); |
| let vsock = Vsock::new(&m).unwrap(); |
| |
| assert!(vsock.as_raw_fd() >= 0); |
| assert!(vsock.mem().find_region(GuestAddress(0x100)).is_some()); |
| assert!(vsock.mem().find_region(GuestAddress(0x10_0000)).is_none()); |
| } |
| |
| #[test] |
| fn test_vsock_is_valid() { |
| let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); |
| let vsock = Vsock::new(&m).unwrap(); |
| |
| let mut config = VringConfigData { |
| queue_max_size: 32, |
| queue_size: 32, |
| flags: 0, |
| desc_table_addr: 0x1000, |
| used_ring_addr: 0x2000, |
| avail_ring_addr: 0x3000, |
| log_addr: None, |
| }; |
| assert!(vsock.is_valid(&config)); |
| |
| config.queue_size = 0; |
| assert!(!vsock.is_valid(&config)); |
| config.queue_size = 31; |
| assert!(!vsock.is_valid(&config)); |
| config.queue_size = 33; |
| assert!(!vsock.is_valid(&config)); |
| } |
| |
| #[test] |
| fn test_vsock_ioctls() { |
| let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); |
| let vsock = Vsock::new(&m).unwrap(); |
| |
| let features = vsock.get_features().unwrap(); |
| vsock.set_features(features).unwrap(); |
| |
| vsock.set_owner().unwrap(); |
| |
| vsock.set_mem_table(&[]).unwrap_err(); |
| |
| /* |
| let region = VhostUserMemoryRegionInfo { |
| guest_phys_addr: 0x0, |
| memory_size: 0x10_0000, |
| userspace_addr: 0, |
| mmap_offset: 0, |
| mmap_handle: -1, |
| }; |
| vsock.set_mem_table(&[region]).unwrap_err(); |
| */ |
| |
| let region = VhostUserMemoryRegionInfo::new( |
| 0x0, |
| 0x10_0000, |
| m.get_host_address(GuestAddress(0x0)).unwrap() as u64, |
| 0, |
| -1, |
| ); |
| vsock.set_mem_table(&[region]).unwrap(); |
| |
| vsock |
| .set_log_base( |
| 0x4000, |
| Some(VhostUserDirtyLogRegion { |
| mmap_size: 0x1000, |
| mmap_offset: 0x10, |
| mmap_handle: 1, |
| }), |
| ) |
| .unwrap_err(); |
| vsock.set_log_base(0x4000, None).unwrap(); |
| |
| let eventfd = EventFd::new(0).unwrap(); |
| vsock.set_log_fd(eventfd.as_raw_fd()).unwrap(); |
| |
| vsock.set_vring_num(0, 32).unwrap(); |
| |
| let config = VringConfigData { |
| queue_max_size: 32, |
| queue_size: 32, |
| flags: 0, |
| desc_table_addr: 0x1000, |
| used_ring_addr: 0x2000, |
| avail_ring_addr: 0x3000, |
| log_addr: None, |
| }; |
| vsock.set_vring_addr(0, &config).unwrap(); |
| vsock.set_vring_base(0, 1).unwrap(); |
| vsock.set_vring_call(0, &eventfd).unwrap(); |
| vsock.set_vring_kick(0, &eventfd).unwrap(); |
| vsock.set_vring_err(0, &eventfd).unwrap(); |
| assert_eq!(vsock.get_vring_base(0).unwrap(), 1); |
| vsock.set_guest_cid(0xdead).unwrap(); |
| //vsock.start().unwrap(); |
| //vsock.stop().unwrap(); |
| } |
| } |