Skip to content

Commit

Permalink
mshv-ioctls: add devicefd abstraction
Browse files Browse the repository at this point in the history
Signed-off-by: Wei Liu <liuwe@microsoft.com>
  • Loading branch information
liuw committed Sep 2, 2021
1 parent 37024a1 commit ef0212d
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 0 deletions.
183 changes: 183 additions & 0 deletions mshv-ioctls/src/ioctls/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright 2021 Microsoft

use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};

use crate::ioctls::Result;
use crate::mshv_ioctls::{MSHV_GET_DEVICE_ATTR, MSHV_HAS_DEVICE_ATTR, MSHV_SET_DEVICE_ATTR};
use mshv_bindings::mshv_device_attr;
use vmm_sys_util::errno;
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref};

/// Wrapper over the file descriptor obtained when creating an emulated device in the kernel.
pub struct DeviceFd {
fd: File,
}

impl DeviceFd {
/// Tests whether a device supports a particular attribute.
///
/// See the documentation for `MSHV_HAS_DEVICE_ATTR`.
/// # Arguments
///
/// * `device_attr` - The device attribute to be tested. `addr` field is ignored.
///
pub fn has_device_attr(&self, device_attr: &mshv_device_attr) -> Result<()> {
let ret = unsafe { ioctl_with_ref(self, MSHV_HAS_DEVICE_ATTR(), device_attr) };
if ret != 0 {
return Err(errno::Error::last());
}
Ok(())
}

/// Sets a specified piece of device configuration and/or state.
///
/// See the documentation for `MSHV_SET_DEVICE_ATTR`.
/// # Arguments
///
/// * `device_attr` - The device attribute to be set.
///
/// # Example
///
/// ```rust
/// # extern crate mshv_ioctls;
/// # extern crate mshv_bindings;
/// # use mshv_ioctls::Mshv;
/// # use mshv_bindings::{
/// mshv_device_type_MSHV_DEV_TYPE_VFIO,
/// MSHV_DEV_VFIO_GROUP, MSHV_DEV_VFIO_GROUP_ADD, MSHV_CREATE_DEVICE_TEST
/// };
/// let mshv = Mshv::new().unwrap();
/// let vm = mshv.create_vm().unwrap();
///
/// let mut device = mshv_bindings::mshv_create_device {
/// type_: mshv_device_type_MSHV_DEV_TYPE_VFIO,
/// fd: 0,
/// flags: MSHV_CREATE_DEVICE_TEST,
/// };
///
/// let device_fd = vm
/// .create_device(&mut device)
/// .expect("Cannot create MSHV device");
///
/// let dist_attr = mshv_bindings::mshv_device_attr {
/// group: MSHV_DEV_VFIO_GROUP,
/// attr: u64::from(MSHV_DEV_VFIO_GROUP_ADD),
/// addr: 0,
/// flags: 0,
/// };
///
/// if (device_fd.has_device_attr(&dist_attr).is_ok()) {
/// device_fd.set_device_attr(&dist_attr).unwrap();
/// }
/// ```
///
pub fn set_device_attr(&self, device_attr: &mshv_device_attr) -> Result<()> {
let ret = unsafe { ioctl_with_ref(self, MSHV_SET_DEVICE_ATTR(), device_attr) };
if ret != 0 {
return Err(errno::Error::last());
}
Ok(())
}

/// Gets a specified piece of device configuration and/or state.
///
/// See the documentation for `MSHV_GET_DEVICE_ATTR`.
///
/// # Arguments
///
/// * `device_attr` - The device attribute to be get.
/// Note: This argument serves as both input and output.
/// When calling this function, the user should explicitly provide
/// valid values for the `group` and the `attr` field of the
/// `mshv_device_attr` structure, and a valid userspace address
/// (i.e. the `addr` field) to access the returned device attribute
/// data.
///
/// # Returns
///
/// * Returns the last occured `errno` wrapped in an `Err`.
/// * `device_attr` - The `addr` field of the `device_attr` structure will point to
/// the device attribute data.
///
pub fn get_device_attr(&self, device_attr: &mut mshv_device_attr) -> Result<()> {
let ret = unsafe { ioctl_with_mut_ref(self, MSHV_GET_DEVICE_ATTR(), device_attr) };
if ret != 0 {
return Err(errno::Error::last());
}
Ok(())
}
}

/// Helper function for creating a new device.
pub fn new_device(dev_fd: File) -> DeviceFd {
DeviceFd { fd: dev_fd }
}

impl AsRawFd for DeviceFd {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}

impl FromRawFd for DeviceFd {
/// This function is also unsafe as the primitives currently returned have the contract that
/// they are the sole owner of the file descriptor they are wrapping. Usage of this function
/// could accidentally allow violating this contract which can cause memory unsafety in code
/// that relies on it being true.
unsafe fn from_raw_fd(fd: RawFd) -> Self {
DeviceFd {
fd: File::from_raw_fd(fd),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::ioctls::system::Mshv;
#[cfg(target_arch = "x86_64")]
use mshv_bindings::{
mshv_device_type_MSHV_DEV_TYPE_VFIO, MSHV_DEV_VFIO_GROUP, MSHV_DEV_VFIO_GROUP_ADD,
};

#[test]
#[ignore]
#[cfg(target_arch = "x86_64")]
fn test_create_device() {
let mshv = Mshv::new().unwrap();
let vm = mshv.create_vm().unwrap();

let mut device = mshv_bindings::mshv_create_device {
type_: mshv_device_type_MSHV_DEV_TYPE_VFIO,
fd: 0,
flags: 0,
};
let device = vm
.create_device(&mut device)
.expect("Cannot create MSHV device");

// Following lines to re-construct device_fd are used to test
// DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd().
let raw_fd = unsafe { libc::dup(device.as_raw_fd()) };
assert!(raw_fd >= 0);
let device = unsafe { DeviceFd::from_raw_fd(raw_fd) };

let dist_attr = mshv_bindings::mshv_device_attr {
group: MSHV_DEV_VFIO_GROUP,
attr: u64::from(MSHV_DEV_VFIO_GROUP_ADD),
addr: 0,
flags: 0,
};

let mut dist_attr_mut = dist_attr;

// We are just creating a test device. Creating a real device would make the CI dependent
// on host configuration (like having /dev/vfio). We expect this to fail.
assert!(device.has_device_attr(&dist_attr).is_ok());
assert!(device.get_device_attr(&mut dist_attr_mut).is_err());
assert!(device.set_device_attr(&dist_attr).is_err());
assert_eq!(errno::Error::last().errno(), 14);
}
}
1 change: 1 addition & 0 deletions mshv-ioctls/src/ioctls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
use vmm_sys_util::errno;
pub mod device;
pub mod system;
pub mod vcpu;
pub mod vm;
Expand Down
13 changes: 13 additions & 0 deletions mshv-ioctls/src/ioctls/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
//
use crate::ioctls::device::{new_device, DeviceFd};
use crate::ioctls::vcpu::{new_vcpu, VcpuFd};
use crate::ioctls::Result;
use crate::mshv_ioctls::*;
Expand Down Expand Up @@ -541,6 +542,18 @@ impl VmFd {
}
Ok(bitmap)
}
/// Create an in-kernel device
///
/// See the documentation for `MSHV_CREATE_DEVICE`.
///
pub fn create_device(&self, device: &mut mshv_create_device) -> Result<DeviceFd> {
let ret = unsafe { ioctl_with_ref(self, MSHV_CREATE_DEVICE(), device) };
if ret == 0 {
Ok(new_device(unsafe { File::from_raw_fd(device.fd as i32) }))
} else {
Err(errno::Error::last())
}
}
}
/// Helper function to create a new `VmFd`.
///
Expand Down
1 change: 1 addition & 0 deletions mshv-ioctls/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

extern crate mshv_bindings;
pub mod ioctls;
pub use ioctls::device::DeviceFd;
pub use ioctls::system::Mshv;
pub use ioctls::vcpu::VcpuFd;
pub use ioctls::vm::InterruptRequest;
Expand Down
5 changes: 5 additions & 0 deletions mshv-ioctls/src/mshv_ioctls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,8 @@ ioctl_iowr_nr!(
0x12,
mshv_get_gpa_pages_access_state
);

ioctl_iowr_nr!(MSHV_CREATE_DEVICE, MSHV_IOCTL, 0x13, mshv_create_device);
ioctl_iow_nr!(MSHV_SET_DEVICE_ATTR, MSHV_IOCTL, 0x14, mshv_device_attr);
ioctl_iow_nr!(MSHV_GET_DEVICE_ATTR, MSHV_IOCTL, 0x15, mshv_device_attr);
ioctl_iow_nr!(MSHV_HAS_DEVICE_ATTR, MSHV_IOCTL, 0x16, mshv_device_attr);

0 comments on commit ef0212d

Please sign in to comment.