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}