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}