Skip to content

Commit

Permalink
Add support for KVM_GET_DEVICE_ATTR ioctl
Browse files Browse the repository at this point in the history
The `KVM_GET_DEVICE_ATTR` ioctl is useful when we need to extract
the state/information of devices in the VM. In AArch64 VMs, using
this ioctl is the only method to get the vGIC states.

This commit implements the `KVM_GET_DEVICE_ATTR` ioctl with its
unit test on AArch64.

Fixes: rust-vmm#99

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
  • Loading branch information
MrXinWang authored and alxiord committed Jun 24, 2020
1 parent 6eaa36c commit 8ea124b
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 5 deletions.
2 changes: 1 addition & 1 deletion coverage_config_x86_64.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"coverage_score": 91.4,
"coverage_score": 91.3,
"exclude_path": "",
"crate_features": ""
}
106 changes: 102 additions & 4 deletions src/ioctls/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};

use ioctls::Result;
use kvm_bindings::kvm_device_attr;
use kvm_ioctls::{KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR};
use kvm_ioctls::{KVM_GET_DEVICE_ATTR, KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR};
use vmm_sys_util::errno;
use vmm_sys_util::ioctl::ioctl_with_ref;
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 {
Expand Down Expand Up @@ -80,6 +80,76 @@ impl DeviceFd {
}
Ok(())
}

/// Gets a specified piece of device configuration and/or state.
///
/// See the documentation for `KVM_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
/// `kvm_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.
///
/// # Examples
/// ```rust
/// # extern crate kvm_ioctls;
/// # extern crate kvm_bindings;
/// # use kvm_ioctls::Kvm;
///
/// let kvm = Kvm::new().unwrap();
/// let vm = kvm.create_vm().unwrap();
///
/// // As on x86_64, `get_device_attr` is not necessarily needed. Therefore here
/// // the code example is only for AArch64.
/// #[cfg(any(target_arch = "aarch64"))]
/// {
/// use kvm_bindings::{
/// KVM_DEV_ARM_VGIC_GRP_NR_IRQS, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2,
/// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
/// };
///
/// // Create a GIC device.
/// let mut gic_device = kvm_bindings::kvm_create_device {
/// type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
/// fd: 0,
/// flags: 0,
/// };
/// let device_fd = match vm.create_device(&mut gic_device) {
/// Ok(fd) => fd,
/// Err(_) => {
/// gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2;
/// vm.create_device(&mut gic_device)
/// .expect("Cannot create KVM vGIC device")
/// }
/// };
///
/// let mut data: u32 = 0;
/// let mut gic_attr = kvm_bindings::kvm_device_attr::default();
/// gic_attr.group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS;
/// gic_attr.addr = &mut data as *const u32 as u64;
///
/// device_fd.get_device_attr(&mut gic_attr).unwrap();
/// }
/// ```
///
pub fn get_device_attr(&self, device_attr: &mut kvm_device_attr) -> Result<()> {
let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEVICE_ATTR(), device_attr) };
if ret != 0 {
return Err(errno::Error::last());
}
Ok(())
}
}

/// Helper function for creating a new device.
Expand Down Expand Up @@ -155,18 +225,24 @@ mod tests {
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_fd.has_device_attr(&dist_attr).is_err());
assert!(device_fd.get_device_attr(&mut dist_attr_mut).is_err());
assert!(device_fd.set_device_attr(&dist_attr).is_err());
assert_eq!(errno::Error::last().errno(), 25);
}

#[test]
#[cfg(target_arch = "aarch64")]
fn test_create_device() {
use ioctls::vm::create_gic_device;
use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20;
use ioctls::vm::{create_gic_device, set_supported_nr_irqs};
use kvm_bindings::{
kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
};
use vmm_sys_util::errno::Error;

let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
Expand Down Expand Up @@ -197,6 +273,9 @@ mod tests {
};
assert!(device_fd.has_device_attr(&dist_attr).is_err());

// Set maximum supported number of IRQs of the vGIC device to 128.
set_supported_nr_irqs(&device_fd, 128);

// Following attribute works with VGIC, they should be accepted.
let dist_attr = kvm_bindings::kvm_device_attr {
group: KVM_DEV_ARM_VGIC_GRP_CTRL,
Expand All @@ -207,5 +286,24 @@ mod tests {

assert!(device_fd.has_device_attr(&dist_attr).is_ok());
assert!(device_fd.set_device_attr(&dist_attr).is_ok());

// Test `get_device_attr`. Here we try to extract the maximum supported number of IRQs.
// This value should be saved in the address provided to the ioctl.
let mut data: u32 = 0;

let mut gic_attr = kvm_bindings::kvm_device_attr::default();
gic_attr.group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS;
gic_attr.addr = data as u64;

// Without properly providing the address to where the
// value will be stored, the ioctl fails with EFAULT.
let res = device_fd.get_device_attr(&mut gic_attr);
assert_eq!(res, Err(Error::new(libc::EFAULT)));

gic_attr.addr = &mut data as *const u32 as u64;
assert!(device_fd.get_device_attr(&mut gic_attr).is_ok());
// The maximum supported number of IRQs should be 128, same as the value
// when we initialize the GIC.
assert_eq!(data, 128);
}
}
2 changes: 2 additions & 0 deletions src/kvm_ioctls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device);
/* Available with KVM_CAP_DEVICE_CTRL */
ioctl_iow_nr!(KVM_SET_DEVICE_ATTR, KVMIO, 0xe1, kvm_device_attr);
/* Available with KVM_CAP_DEVICE_CTRL */
ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr);
/* Available with KVM_CAP_DEVICE_CTRL */
ioctl_iow_nr!(KVM_HAS_DEVICE_ATTR, KVMIO, 0xe3, kvm_device_attr);

#[cfg(test)]
Expand Down

0 comments on commit 8ea124b

Please sign in to comment.