Skip to main content

kvm_ioctls/ioctls/
device.rs

1// Copyright © 2024 Institute of Software, CAS. All rights reserved.
2//
3// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR MIT
5
6use std::fs::File;
7use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
8
9use crate::ioctls::Result;
10use crate::kvm_ioctls::{KVM_GET_DEVICE_ATTR, KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR};
11use kvm_bindings::kvm_device_attr;
12use vmm_sys_util::errno;
13use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref};
14
15/// Wrapper over the file descriptor obtained when creating an emulated device in the kernel.
16#[derive(Debug)]
17pub struct DeviceFd {
18    fd: File,
19}
20
21impl DeviceFd {
22    /// Tests whether a device supports a particular attribute.
23    ///
24    /// See the documentation for `KVM_HAS_DEVICE_ATTR`.
25    /// # Arguments
26    ///
27    /// * `device_attr` - The device attribute to be tested. `addr` field is ignored.
28    pub fn has_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> {
29        // SAFETY: We are calling this function with a Device fd, and we trust the kernel.
30        let ret = unsafe { ioctl_with_ref(self, KVM_HAS_DEVICE_ATTR(), device_attr) };
31        if ret != 0 {
32            return Err(errno::Error::last());
33        }
34        Ok(())
35    }
36
37    /// Sets a specified piece of device configuration and/or state.
38    ///
39    /// See the documentation for `KVM_SET_DEVICE_ATTR`.
40    /// # Arguments
41    ///
42    /// * `device_attr` - The device attribute to be set.
43    ///
44    /// # Example
45    ///
46    /// Configuring a VFIO device using `set_device_attr`. Note that VFIO
47    /// devices are not yet available on RISC-V The patch for QEMU:
48    /// https://lore.kernel.org/all/20240903201633.93182-1-dbarboza@ventanamicro.com/
49    /// and patch for linux kernel
50    /// https://github.com/ventanamicro/linux/tree/dev-upstream are both not
51    /// upstreamed. Disabling VFIO device test for RISC-V at the time being.
52    ///
53    /// ```rust
54    /// # extern crate kvm_ioctls;
55    /// # extern crate kvm_bindings;
56    /// # use kvm_ioctls::Kvm;
57    /// let kvm = Kvm::new().unwrap();
58    /// let vm = kvm.create_vm().unwrap();
59    ///
60    /// # #[cfg(not(target_arch = "riscv64"))]
61    /// # {
62    /// # use kvm_bindings::{
63    /// #     kvm_device_type_KVM_DEV_TYPE_VFIO,
64    /// #     KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, KVM_CREATE_DEVICE_TEST
65    /// # };
66    /// let mut device = kvm_bindings::kvm_create_device {
67    ///     type_: kvm_device_type_KVM_DEV_TYPE_VFIO,
68    ///     fd: 0,
69    ///     flags: KVM_CREATE_DEVICE_TEST,
70    /// };
71    ///
72    /// let device_fd = vm
73    ///     .create_device(&mut device)
74    ///     .expect("Cannot create KVM device");
75    ///
76    /// let dist_attr = kvm_bindings::kvm_device_attr {
77    ///     group: KVM_DEV_VFIO_GROUP,
78    ///     attr: u64::from(KVM_DEV_VFIO_GROUP_ADD),
79    ///     addr: 0x0,
80    ///     flags: 0,
81    /// };
82    ///
83    /// if (device_fd.has_device_attr(&dist_attr).is_ok()) {
84    ///     device_fd.set_device_attr(&dist_attr).unwrap();
85    /// }
86    /// # }
87    /// ```
88    pub fn set_device_attr(&self, device_attr: &kvm_device_attr) -> Result<()> {
89        // SAFETY: We are calling this function with a Device fd, and we trust the kernel.
90        let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), device_attr) };
91        if ret != 0 {
92            return Err(errno::Error::last());
93        }
94        Ok(())
95    }
96
97    /// Gets a specified piece of device configuration and/or state.
98    ///
99    /// See the documentation for `KVM_GET_DEVICE_ATTR`.
100    ///
101    /// # Arguments
102    ///
103    /// * `device_attr` - The device attribute to be get.
104    ///   Note: This argument serves as both input and output.
105    ///   When calling this function, the user should explicitly provide
106    ///   valid values for the `group` and the `attr` field of the
107    ///   `kvm_device_attr` structure, and a valid userspace address
108    ///   (i.e. the `addr` field) to access the returned device attribute
109    ///   data.
110    ///
111    /// # Returns
112    ///
113    /// * Returns the last occured `errno` wrapped in an `Err`.
114    /// * `device_attr` - The `addr` field of the `device_attr` structure will point to
115    ///   the device attribute data.
116    ///
117    /// # Safety
118    ///
119    /// The caller is responsible for the validity of the `device_attr` argument,
120    /// including that it is safe to write to the `addr` member.
121    ///
122    /// # Examples
123    ///
124    /// Getting the number of IRQs for a GICv2 device on an aarch64 platform
125    ///
126    /// ```rust
127    /// # extern crate kvm_ioctls;
128    /// # extern crate kvm_bindings;
129    /// # use kvm_ioctls::Kvm;
130    /// let kvm = Kvm::new().unwrap();
131    /// let vm = kvm.create_vm().unwrap();
132    ///
133    /// # #[cfg(target_arch = "aarch64")]
134    /// # {
135    /// use kvm_bindings::{
136    ///     KVM_DEV_ARM_VGIC_GRP_NR_IRQS, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2,
137    ///     kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
138    /// };
139    ///
140    /// // Create a GIC device.
141    /// let mut gic_device = kvm_bindings::kvm_create_device {
142    ///     type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
143    ///     fd: 0,
144    ///     flags: 0,
145    /// };
146    /// let device_fd = match vm.create_device(&mut gic_device) {
147    ///     Ok(fd) => fd,
148    ///     Err(_) => {
149    ///         gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2;
150    ///         vm.create_device(&mut gic_device)
151    ///             .expect("Cannot create KVM vGIC device")
152    ///     }
153    /// };
154    ///
155    /// let mut data: u32 = 0;
156    /// let mut gic_attr = kvm_bindings::kvm_device_attr::default();
157    /// gic_attr.group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS;
158    /// gic_attr.addr = &mut data as *mut u32 as u64;
159    ///
160    /// // SAFETY: gic_attr.addr is safe to write to.
161    /// unsafe { device_fd.get_device_attr(&mut gic_attr) }.unwrap();
162    /// # }
163    /// ```
164    pub unsafe fn get_device_attr(&self, device_attr: &mut kvm_device_attr) -> Result<()> {
165        // SAFETY: Caller has ensured device_attr.addr is safe to write to.
166        // We are calling this function with a Device fd,  we trust the kernel.
167        let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEVICE_ATTR(), device_attr) };
168        if ret != 0 {
169            return Err(errno::Error::last());
170        }
171        Ok(())
172    }
173}
174
175/// Helper function for creating a new device.
176pub fn new_device(dev_fd: File) -> DeviceFd {
177    DeviceFd { fd: dev_fd }
178}
179
180impl AsRawFd for DeviceFd {
181    fn as_raw_fd(&self) -> RawFd {
182        self.fd.as_raw_fd()
183    }
184}
185
186impl FromRawFd for DeviceFd {
187    /// # Safety
188    ///
189    /// This function is unsafe as the primitives currently returned have the contract that
190    /// they are the sole owner of the file descriptor they are wrapping. Usage of this function
191    /// could accidentally allow violating this contract which can cause memory unsafety in code
192    /// that relies on it being true.
193    unsafe fn from_raw_fd(fd: RawFd) -> Self {
194        DeviceFd {
195            // SAFETY: we trust the kernel and verified parameters
196            fd: unsafe { File::from_raw_fd(fd) },
197        }
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    #![allow(clippy::undocumented_unsafe_blocks)]
204    use super::*;
205    use crate::ioctls::system::Kvm;
206    #[cfg(target_arch = "aarch64")]
207    use kvm_bindings::{KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD};
208    #[cfg(target_arch = "x86_64")]
209    use kvm_bindings::{
210        KVM_DEV_VFIO_GROUP, KVM_DEV_VFIO_GROUP_ADD, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
211        kvm_device_type_KVM_DEV_TYPE_VFIO,
212    };
213
214    use kvm_bindings::KVM_CREATE_DEVICE_TEST;
215
216    #[test]
217    #[cfg(target_arch = "x86_64")]
218    fn test_create_device() {
219        let kvm = Kvm::new().unwrap();
220        let vm = kvm.create_vm().unwrap();
221
222        let mut gic_device = kvm_bindings::kvm_create_device {
223            type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
224            fd: 0,
225            flags: KVM_CREATE_DEVICE_TEST,
226        };
227        // This fails on x86_64 because there is no VGIC there.
228        vm.create_device(&mut gic_device).unwrap_err();
229
230        gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_VFIO;
231
232        let device_fd = vm
233            .create_device(&mut gic_device)
234            .expect("Cannot create KVM device");
235
236        // Following lines to re-construct device_fd are used to test
237        // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd().
238        let raw_fd = unsafe { libc::dup(device_fd.as_raw_fd()) };
239        assert!(raw_fd >= 0);
240        let device_fd = unsafe { DeviceFd::from_raw_fd(raw_fd) };
241
242        let dist_attr = kvm_bindings::kvm_device_attr {
243            group: KVM_DEV_VFIO_GROUP,
244            attr: u64::from(KVM_DEV_VFIO_GROUP_ADD),
245            addr: 0x0,
246            flags: 0,
247        };
248
249        let mut dist_attr_mut = dist_attr;
250
251        // We are just creating a test device. Creating a real device would make the CI dependent
252        // on host configuration (like having /dev/vfio). We expect this to fail.
253        device_fd.has_device_attr(&dist_attr).unwrap_err();
254        unsafe { device_fd.get_device_attr(&mut dist_attr_mut) }.unwrap_err();
255        device_fd.set_device_attr(&dist_attr).unwrap_err();
256        assert_eq!(errno::Error::last().errno(), 25);
257    }
258
259    #[test]
260    #[cfg(target_arch = "aarch64")]
261    fn test_create_device() {
262        use crate::ioctls::vm::{create_gic_device, request_gic_init, set_supported_nr_irqs};
263        use kvm_bindings::{
264            KVM_DEV_ARM_VGIC_GRP_NR_IRQS, kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20,
265        };
266        use vmm_sys_util::errno::Error;
267
268        let kvm = Kvm::new().unwrap();
269        let vm = kvm.create_vm().unwrap();
270
271        let mut gic_device = kvm_bindings::kvm_create_device {
272            type_: kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20,
273            fd: 0,
274            flags: KVM_CREATE_DEVICE_TEST,
275        };
276        // This fails on aarch64 as it does not use MPIC (MultiProcessor Interrupt Controller),
277        // it uses the VGIC.
278        vm.create_device(&mut gic_device).unwrap_err();
279
280        let device_fd = create_gic_device(&vm, 0);
281
282        // GICv3 on arm/aarch64 requires an online vCPU prior to setting device attributes,
283        // see: https://www.kernel.org/doc/html/latest/virt/kvm/devices/arm-vgic-v3.html
284        vm.create_vcpu(0).unwrap();
285
286        // Following lines to re-construct device_fd are used to test
287        // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd().
288        let raw_fd = unsafe { libc::dup(device_fd.as_raw_fd()) };
289        assert!(raw_fd >= 0);
290        let device_fd = unsafe { DeviceFd::from_raw_fd(raw_fd) };
291
292        // Set some attribute that does not apply to VGIC, expect the test to fail.
293        let dist_attr = kvm_bindings::kvm_device_attr {
294            group: KVM_DEV_VFIO_GROUP,
295            attr: u64::from(KVM_DEV_VFIO_GROUP_ADD),
296            addr: 0x0,
297            flags: 0,
298        };
299        device_fd.has_device_attr(&dist_attr).unwrap_err();
300
301        // Set maximum supported number of IRQs of the vGIC device to 128.
302        set_supported_nr_irqs(&device_fd, 128);
303
304        // Initialize valid vGIC device.
305        request_gic_init(&device_fd);
306
307        // Test `get_device_attr`. Here we try to extract the maximum supported number of IRQs.
308        // This value should be saved in the address provided to the ioctl.
309        let mut data: u32 = 0;
310
311        let mut gic_attr = kvm_bindings::kvm_device_attr {
312            group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
313            addr: data as u64,
314            ..Default::default()
315        };
316
317        // Without properly providing the address to where the
318        // value will be stored, the ioctl fails with EFAULT.
319        let res = unsafe { device_fd.get_device_attr(&mut gic_attr) };
320        assert_eq!(res, Err(Error::new(libc::EFAULT)));
321
322        gic_attr.addr = &mut data as *mut u32 as u64;
323        unsafe { device_fd.get_device_attr(&mut gic_attr) }.unwrap();
324        // The maximum supported number of IRQs should be 128, same as the value
325        // when we initialize the GIC.
326        assert_eq!(data, 128);
327    }
328
329    #[test]
330    #[cfg(target_arch = "riscv64")]
331    fn test_create_device() {
332        use crate::ioctls::vm::{create_aia_device, request_aia_init, set_supported_nr_irqs};
333        use kvm_bindings::{
334            KVM_DEV_RISCV_AIA_ADDR_APLIC, KVM_DEV_RISCV_AIA_CONFIG_SRCS,
335            KVM_DEV_RISCV_AIA_GRP_ADDR, KVM_DEV_RISCV_AIA_GRP_CONFIG, kvm_device_attr,
336            kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20,
337        };
338        use vmm_sys_util::errno::Error;
339
340        let kvm = Kvm::new().unwrap();
341        let vm = kvm.create_vm().unwrap();
342
343        let mut aia_device = kvm_bindings::kvm_create_device {
344            type_: kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20,
345            fd: 0,
346            flags: KVM_CREATE_DEVICE_TEST,
347        };
348        // This fails on riscv64 as it does not use MPIC (MultiProcessor Interrupt Controller),
349        // it uses the vAIA.
350        vm.create_device(&mut aia_device).unwrap_err();
351
352        let device_fd = create_aia_device(&vm, 0);
353
354        // AIA on riscv64 requires at least one online vCPU prior to setting
355        // device attributes. Otherwise it would fail when trying to set address
356        // of IMSIC.
357        vm.create_vcpu(0).unwrap();
358
359        // Following lines to re-construct device_fd are used to test
360        // DeviceFd::from_raw_fd() and DeviceFd::as_raw_fd().
361        let raw_fd = unsafe { libc::dup(device_fd.as_raw_fd()) };
362        assert!(raw_fd >= 0);
363        let device_fd = unsafe { DeviceFd::from_raw_fd(raw_fd) };
364
365        // Set maximum supported number of IRQs of the vAIA device to 128.
366        set_supported_nr_irqs(&device_fd, 128);
367
368        // Before request vAIA device to initialize, APLIC and IMSIC must be set
369        let aplic_addr: u64 = 0x4000;
370        device_fd
371            .set_device_attr(&kvm_device_attr {
372                group: KVM_DEV_RISCV_AIA_GRP_ADDR,
373                attr: u64::from(KVM_DEV_RISCV_AIA_ADDR_APLIC),
374                addr: &aplic_addr as *const u64 as u64,
375                flags: 0,
376            })
377            .unwrap();
378        let imsic_addr: u64 = 0x8000;
379        device_fd
380            .set_device_attr(&kvm_device_attr {
381                group: KVM_DEV_RISCV_AIA_GRP_ADDR,
382                attr: 1u64,
383                addr: &imsic_addr as *const u64 as u64,
384                flags: 0,
385            })
386            .unwrap();
387
388        // Initialize valid vAIA device.
389        request_aia_init(&device_fd);
390
391        // Test `get_device_attr`. Here we try to extract the maximum supported number of IRQs.
392        // This value should be saved in the address provided to the ioctl.
393        let mut data: u32 = 0;
394
395        let mut aia_attr = kvm_bindings::kvm_device_attr {
396            group: KVM_DEV_RISCV_AIA_GRP_CONFIG,
397            attr: u64::from(KVM_DEV_RISCV_AIA_CONFIG_SRCS),
398            addr: data as u64,
399            ..Default::default()
400        };
401
402        // Without properly providing the address to where the
403        // value will be stored, the ioctl fails with EFAULT.
404        let res = unsafe { device_fd.get_device_attr(&mut aia_attr) };
405        assert_eq!(res, Err(Error::new(libc::EFAULT)));
406
407        aia_attr.addr = &mut data as *mut u32 as u64;
408        unsafe { device_fd.get_device_attr(&mut aia_attr) }.unwrap();
409        // The maximum supported number of IRQs should be 128, same as the value
410        // when we initialize the AIA.
411        assert_eq!(data, 128);
412    }
413}