1 // Copyright (C) 2019 Alibaba Cloud. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause 3 // 4 // Copyright 2017 The Chromium OS Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style license that can be 6 // found in the LICENSE-BSD-Google file. 7 8 //! Kernel-based vhost-vsock backend. 9 10 use std::fs::{File, OpenOptions}; 11 use std::os::unix::fs::OpenOptionsExt; 12 use std::os::unix::io::{AsRawFd, RawFd}; 13 14 use vm_memory::GuestAddressSpace; 15 use vmm_sys_util::ioctl::ioctl_with_ref; 16 17 use super::vhost_binding::{VHOST_VSOCK_SET_GUEST_CID, VHOST_VSOCK_SET_RUNNING}; 18 use super::{ioctl_result, Error, Result, VhostKernBackend}; 19 use crate::vsock::VhostVsock; 20 21 const VHOST_PATH: &str = "/dev/vhost-vsock"; 22 23 /// Handle for running VHOST_VSOCK ioctls. 24 pub struct Vsock<AS: GuestAddressSpace> { 25 fd: File, 26 mem: AS, 27 } 28 29 impl<AS: GuestAddressSpace> Vsock<AS> { 30 /// Open a handle to a new VHOST-VSOCK instance. new(mem: AS) -> Result<Self>31 pub fn new(mem: AS) -> Result<Self> { 32 Ok(Vsock { 33 fd: OpenOptions::new() 34 .read(true) 35 .write(true) 36 .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK) 37 .open(VHOST_PATH) 38 .map_err(Error::VhostOpen)?, 39 mem, 40 }) 41 } 42 set_running(&self, running: bool) -> Result<()>43 fn set_running(&self, running: bool) -> Result<()> { 44 let on: ::std::os::raw::c_int = if running { 1 } else { 0 }; 45 46 // SAFETY: This ioctl is called on a valid vhost-vsock fd and has its 47 // return value checked. 48 let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_RUNNING(), &on) }; 49 ioctl_result(ret, ()) 50 } 51 } 52 53 impl<AS: GuestAddressSpace> VhostVsock for Vsock<AS> { set_guest_cid(&self, cid: u64) -> Result<()>54 fn set_guest_cid(&self, cid: u64) -> Result<()> { 55 // SAFETY: This ioctl is called on a valid vhost-vsock fd and has its 56 // return value checked. 57 let ret = unsafe { ioctl_with_ref(&self.fd, VHOST_VSOCK_SET_GUEST_CID(), &cid) }; 58 ioctl_result(ret, ()) 59 } 60 start(&self) -> Result<()>61 fn start(&self) -> Result<()> { 62 self.set_running(true) 63 } 64 stop(&self) -> Result<()>65 fn stop(&self) -> Result<()> { 66 self.set_running(false) 67 } 68 } 69 70 impl<AS: GuestAddressSpace> VhostKernBackend for Vsock<AS> { 71 type AS = AS; 72 mem(&self) -> &Self::AS73 fn mem(&self) -> &Self::AS { 74 &self.mem 75 } 76 } 77 78 impl<AS: GuestAddressSpace> AsRawFd for Vsock<AS> { as_raw_fd(&self) -> RawFd79 fn as_raw_fd(&self) -> RawFd { 80 self.fd.as_raw_fd() 81 } 82 } 83 84 #[cfg(test)] 85 mod tests { 86 use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap}; 87 use vmm_sys_util::eventfd::EventFd; 88 89 use super::*; 90 use crate::{ 91 VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo, VringConfigData, 92 }; 93 94 #[test] test_vsock_new_device()95 fn test_vsock_new_device() { 96 let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); 97 let vsock = Vsock::new(&m).unwrap(); 98 99 assert!(vsock.as_raw_fd() >= 0); 100 assert!(vsock.mem().find_region(GuestAddress(0x100)).is_some()); 101 assert!(vsock.mem().find_region(GuestAddress(0x10_0000)).is_none()); 102 } 103 104 #[test] test_vsock_is_valid()105 fn test_vsock_is_valid() { 106 let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); 107 let vsock = Vsock::new(&m).unwrap(); 108 109 let mut config = VringConfigData { 110 queue_max_size: 32, 111 queue_size: 32, 112 flags: 0, 113 desc_table_addr: 0x1000, 114 used_ring_addr: 0x2000, 115 avail_ring_addr: 0x3000, 116 log_addr: None, 117 }; 118 assert!(vsock.is_valid(&config)); 119 120 config.queue_size = 0; 121 assert!(!vsock.is_valid(&config)); 122 config.queue_size = 31; 123 assert!(!vsock.is_valid(&config)); 124 config.queue_size = 33; 125 assert!(!vsock.is_valid(&config)); 126 } 127 128 #[test] test_vsock_ioctls()129 fn test_vsock_ioctls() { 130 let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap(); 131 let vsock = Vsock::new(&m).unwrap(); 132 133 let features = vsock.get_features().unwrap(); 134 vsock.set_features(features).unwrap(); 135 136 vsock.set_owner().unwrap(); 137 138 vsock.set_mem_table(&[]).unwrap_err(); 139 140 /* 141 let region = VhostUserMemoryRegionInfo { 142 guest_phys_addr: 0x0, 143 memory_size: 0x10_0000, 144 userspace_addr: 0, 145 mmap_offset: 0, 146 mmap_handle: -1, 147 }; 148 vsock.set_mem_table(&[region]).unwrap_err(); 149 */ 150 151 let region = VhostUserMemoryRegionInfo::new( 152 0x0, 153 0x10_0000, 154 m.get_host_address(GuestAddress(0x0)).unwrap() as u64, 155 0, 156 -1, 157 ); 158 vsock.set_mem_table(&[region]).unwrap(); 159 160 vsock 161 .set_log_base( 162 0x4000, 163 Some(VhostUserDirtyLogRegion { 164 mmap_size: 0x1000, 165 mmap_offset: 0x10, 166 mmap_handle: 1, 167 }), 168 ) 169 .unwrap_err(); 170 vsock.set_log_base(0x4000, None).unwrap(); 171 172 let eventfd = EventFd::new(0).unwrap(); 173 vsock.set_log_fd(eventfd.as_raw_fd()).unwrap(); 174 175 vsock.set_vring_num(0, 32).unwrap(); 176 177 let config = VringConfigData { 178 queue_max_size: 32, 179 queue_size: 32, 180 flags: 0, 181 desc_table_addr: 0x1000, 182 used_ring_addr: 0x2000, 183 avail_ring_addr: 0x3000, 184 log_addr: None, 185 }; 186 vsock.set_vring_addr(0, &config).unwrap(); 187 vsock.set_vring_base(0, 1).unwrap(); 188 vsock.set_vring_call(0, &eventfd).unwrap(); 189 vsock.set_vring_kick(0, &eventfd).unwrap(); 190 vsock.set_vring_err(0, &eventfd).unwrap(); 191 assert_eq!(vsock.get_vring_base(0).unwrap(), 1); 192 vsock.set_guest_cid(0xdead).unwrap(); 193 //vsock.start().unwrap(); 194 //vsock.stop().unwrap(); 195 } 196 } 197