Skip to main content

mshv_ioctls/ioctls/
device.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2// Copyright 2021 Microsoft
3
4use std::fs::File;
5use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
6
7use crate::ioctls::Result;
8use crate::mshv_ioctls::{MSHV_GET_DEVICE_ATTR, MSHV_HAS_DEVICE_ATTR, MSHV_SET_DEVICE_ATTR};
9use mshv_bindings::mshv_device_attr;
10use vmm_sys_util::errno;
11use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref};
12
13/// Wrapper over the file descriptor obtained when creating an emulated device in the kernel.
14#[derive(Debug)]
15pub struct DeviceFd {
16    fd: File,
17}
18
19impl DeviceFd {
20    /// Tests whether a device supports a particular attribute.
21    ///
22    /// See the documentation for `MSHV_HAS_DEVICE_ATTR`.
23    /// # Arguments
24    ///
25    /// * `device_attr` - The device attribute to be tested. `addr` field is ignored.
26    pub fn has_device_attr(&self, device_attr: &mshv_device_attr) -> Result<()> {
27        // SAFETY: IOCTL. We're sure parameters are of the correct types and meet safety
28        // requirements.
29        let ret = unsafe { ioctl_with_ref(self, MSHV_HAS_DEVICE_ATTR(), device_attr) };
30        if ret != 0 {
31            return Err(errno::Error::last().into());
32        }
33        Ok(())
34    }
35
36    /// Sets a specified piece of device configuration and/or state.
37    ///
38    /// See the documentation for `MSHV_SET_DEVICE_ATTR`.
39    /// # Arguments
40    ///
41    /// * `device_attr` - The device attribute to be set.
42    ///
43    /// # Example
44    ///
45    /// ```rust
46    /// # extern crate mshv_ioctls;
47    /// # extern crate mshv_bindings;
48    /// # use mshv_ioctls::Mshv;
49    /// # use mshv_bindings::{
50    ///    MSHV_DEV_TYPE_VFIO,
51    ///    MSHV_DEV_VFIO_FILE, MSHV_DEV_VFIO_FILE_ADD, MSHV_CREATE_DEVICE_TEST
52    /// };
53    /// let mshv = Mshv::new().unwrap();
54    /// let vm = mshv.create_vm().unwrap();
55    ///
56    /// let mut device = mshv_bindings::mshv_create_device {
57    ///     type_: MSHV_DEV_TYPE_VFIO,
58    ///     fd: 0,
59    ///     flags: MSHV_CREATE_DEVICE_TEST,
60    /// };
61    ///
62    /// let device_fd = vm
63    ///     .create_device(&mut device)
64    ///     .expect("Cannot create MSHV device");
65    ///
66    /// let dist_attr = mshv_bindings::mshv_device_attr {
67    ///     group: MSHV_DEV_VFIO_FILE,
68    ///     attr: u64::from(MSHV_DEV_VFIO_FILE_ADD),
69    ///     addr: 0,
70    ///     flags: 0,
71    /// };
72    ///
73    /// if (device_fd.has_device_attr(&dist_attr).is_ok()) {
74    ///     device_fd.set_device_attr(&dist_attr).unwrap();
75    /// }
76    /// ```
77    pub fn set_device_attr(&self, device_attr: &mshv_device_attr) -> Result<()> {
78        // SAFETY: IOCTL. We're sure parameters are of the correct types and meet safety
79        // requirements.
80        let ret = unsafe { ioctl_with_ref(self, MSHV_SET_DEVICE_ATTR(), device_attr) };
81        if ret != 0 {
82            return Err(errno::Error::last().into());
83        }
84        Ok(())
85    }
86
87    /// Gets a specified piece of device configuration and/or state.
88    ///
89    /// See the documentation for `MSHV_GET_DEVICE_ATTR`.
90    ///
91    /// # Arguments
92    ///
93    /// * `device_attr` - The device attribute to be get.
94    /// Note: This argument serves as both input and output.
95    /// When calling this function, the user should explicitly provide
96    /// valid values for the `group` and the `attr` field of the
97    /// `mshv_device_attr` structure, and a valid userspace address
98    /// (i.e. the `addr` field) to access the returned device attribute
99    /// data.
100    ///
101    /// # Returns
102    ///
103    /// * Returns the last occured `errno` wrapped in an `Err`.
104    /// * `device_attr` - The `addr` field of the `device_attr` structure will point to
105    /// the device attribute data.
106    pub fn get_device_attr(&self, device_attr: &mut mshv_device_attr) -> Result<()> {
107        // SAFETY: IOCTL. We're sure parameters are of the correct types and meet safety
108        // requirements.
109        let ret = unsafe { ioctl_with_mut_ref(self, MSHV_GET_DEVICE_ATTR(), device_attr) };
110        if ret != 0 {
111            return Err(errno::Error::last().into());
112        }
113        Ok(())
114    }
115}
116
117/// Helper function for creating a new device.
118pub fn new_device(dev_fd: File) -> DeviceFd {
119    DeviceFd { fd: dev_fd }
120}
121
122impl AsRawFd for DeviceFd {
123    fn as_raw_fd(&self) -> RawFd {
124        self.fd.as_raw_fd()
125    }
126}
127
128impl FromRawFd for DeviceFd {
129    /// This function is also unsafe as the primitives currently returned have the contract that
130    /// they are the sole owner of the file descriptor they are wrapping. Usage of this function
131    /// could accidentally allow violating this contract which can cause memory unsafety in code
132    /// that relies on it being true.
133    unsafe fn from_raw_fd(fd: RawFd) -> Self {
134        DeviceFd {
135            fd: File::from_raw_fd(fd),
136        }
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    #[cfg(target_arch = "x86_64")]
143    use super::*;
144    #[cfg(target_arch = "x86_64")]
145    use crate::ioctls::system::Mshv;
146    #[cfg(target_arch = "x86_64")]
147    use mshv_bindings::{MSHV_DEV_TYPE_VFIO, MSHV_DEV_VFIO_FILE, MSHV_DEV_VFIO_FILE_ADD};
148
149    #[test]
150    #[cfg(target_arch = "x86_64")]
151    fn test_create_device() {
152        let mshv = Mshv::new().unwrap();
153        let vm = mshv.create_vm().unwrap();
154        vm.initialize().unwrap();
155
156        let mut device = mshv_bindings::mshv_create_device {
157            type_: MSHV_DEV_TYPE_VFIO,
158            fd: 0,
159            flags: 0,
160        };
161        let device = vm
162            .create_device(&mut device)
163            .expect("Cannot create MSHV device");
164
165        // Following lines to re-construct device_fd are used to test
166        // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd().
167        // SAFETY: FFI call to dup(2).
168        let raw_fd = unsafe { libc::dup(device.as_raw_fd()) };
169        assert!(raw_fd >= 0);
170        // SAFETY: raw_fd was created by create_device and checked to be valid.
171        let device = unsafe { DeviceFd::from_raw_fd(raw_fd) };
172
173        let dist_attr = mshv_bindings::mshv_device_attr {
174            group: MSHV_DEV_VFIO_FILE,
175            attr: u64::from(MSHV_DEV_VFIO_FILE_ADD),
176            addr: 0,
177            flags: 0,
178        };
179
180        let mut dist_attr_mut = dist_attr;
181
182        // We are just creating a test device. Creating a real device would make the CI dependent
183        // on host configuration (like having /dev/vfio). We expect this to fail.
184        assert!(device.has_device_attr(&dist_attr).is_ok());
185        assert!(device.get_device_attr(&mut dist_attr_mut).is_err());
186        assert!(device.set_device_attr(&dist_attr).is_err());
187        assert_eq!(errno::Error::last().errno(), 14);
188    }
189}