Skip to content

Commit 8ea124b

Browse files
MrXinWangalxiord
authored andcommitted
Add support for KVM_GET_DEVICE_ATTR ioctl
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: #99 Signed-off-by: Henry Wang <[email protected]>
1 parent 6eaa36c commit 8ea124b

File tree

3 files changed

+105
-5
lines changed

3 files changed

+105
-5
lines changed

coverage_config_x86_64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 91.4,
2+
"coverage_score": 91.3,
33
"exclude_path": "",
44
"crate_features": ""
55
}

src/ioctls/device.rs

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
66

77
use ioctls::Result;
88
use kvm_bindings::kvm_device_attr;
9-
use kvm_ioctls::{KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR};
9+
use kvm_ioctls::{KVM_GET_DEVICE_ATTR, KVM_HAS_DEVICE_ATTR, KVM_SET_DEVICE_ATTR};
1010
use vmm_sys_util::errno;
11-
use vmm_sys_util::ioctl::ioctl_with_ref;
11+
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref};
1212

1313
/// Wrapper over the file descriptor obtained when creating an emulated device in the kernel.
1414
pub struct DeviceFd {
@@ -80,6 +80,76 @@ impl DeviceFd {
8080
}
8181
Ok(())
8282
}
83+
84+
/// Gets a specified piece of device configuration and/or state.
85+
///
86+
/// See the documentation for `KVM_GET_DEVICE_ATTR`.
87+
///
88+
/// # Arguments
89+
///
90+
/// * `device_attr` - The device attribute to be get.
91+
/// Note: This argument serves as both input and output.
92+
/// When calling this function, the user should explicitly provide
93+
/// valid values for the `group` and the `attr` field of the
94+
/// `kvm_device_attr` structure, and a valid userspace address
95+
/// (i.e. the `addr` field) to access the returned device attribute
96+
/// data.
97+
///
98+
/// # Returns
99+
///
100+
/// * Returns the last occured `errno` wrapped in an `Err`.
101+
/// * `device_attr` - The `addr` field of the `device_attr` structure will point to
102+
/// the device attribute data.
103+
///
104+
/// # Examples
105+
/// ```rust
106+
/// # extern crate kvm_ioctls;
107+
/// # extern crate kvm_bindings;
108+
/// # use kvm_ioctls::Kvm;
109+
///
110+
/// let kvm = Kvm::new().unwrap();
111+
/// let vm = kvm.create_vm().unwrap();
112+
///
113+
/// // As on x86_64, `get_device_attr` is not necessarily needed. Therefore here
114+
/// // the code example is only for AArch64.
115+
/// #[cfg(any(target_arch = "aarch64"))]
116+
/// {
117+
/// use kvm_bindings::{
118+
/// KVM_DEV_ARM_VGIC_GRP_NR_IRQS, kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2,
119+
/// kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
120+
/// };
121+
///
122+
/// // Create a GIC device.
123+
/// let mut gic_device = kvm_bindings::kvm_create_device {
124+
/// type_: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
125+
/// fd: 0,
126+
/// flags: 0,
127+
/// };
128+
/// let device_fd = match vm.create_device(&mut gic_device) {
129+
/// Ok(fd) => fd,
130+
/// Err(_) => {
131+
/// gic_device.type_ = kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V2;
132+
/// vm.create_device(&mut gic_device)
133+
/// .expect("Cannot create KVM vGIC device")
134+
/// }
135+
/// };
136+
///
137+
/// let mut data: u32 = 0;
138+
/// let mut gic_attr = kvm_bindings::kvm_device_attr::default();
139+
/// gic_attr.group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS;
140+
/// gic_attr.addr = &mut data as *const u32 as u64;
141+
///
142+
/// device_fd.get_device_attr(&mut gic_attr).unwrap();
143+
/// }
144+
/// ```
145+
///
146+
pub fn get_device_attr(&self, device_attr: &mut kvm_device_attr) -> Result<()> {
147+
let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_DEVICE_ATTR(), device_attr) };
148+
if ret != 0 {
149+
return Err(errno::Error::last());
150+
}
151+
Ok(())
152+
}
83153
}
84154

85155
/// Helper function for creating a new device.
@@ -155,18 +225,24 @@ mod tests {
155225
flags: 0,
156226
};
157227

228+
let mut dist_attr_mut = dist_attr;
229+
158230
// We are just creating a test device. Creating a real device would make the CI dependent
159231
// on host configuration (like having /dev/vfio). We expect this to fail.
160232
assert!(device_fd.has_device_attr(&dist_attr).is_err());
233+
assert!(device_fd.get_device_attr(&mut dist_attr_mut).is_err());
161234
assert!(device_fd.set_device_attr(&dist_attr).is_err());
162235
assert_eq!(errno::Error::last().errno(), 25);
163236
}
164237

165238
#[test]
166239
#[cfg(target_arch = "aarch64")]
167240
fn test_create_device() {
168-
use ioctls::vm::create_gic_device;
169-
use kvm_bindings::kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20;
241+
use ioctls::vm::{create_gic_device, set_supported_nr_irqs};
242+
use kvm_bindings::{
243+
kvm_device_type_KVM_DEV_TYPE_FSL_MPIC_20, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
244+
};
245+
use vmm_sys_util::errno::Error;
170246

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

276+
// Set maximum supported number of IRQs of the vGIC device to 128.
277+
set_supported_nr_irqs(&device_fd, 128);
278+
200279
// Following attribute works with VGIC, they should be accepted.
201280
let dist_attr = kvm_bindings::kvm_device_attr {
202281
group: KVM_DEV_ARM_VGIC_GRP_CTRL,
@@ -207,5 +286,24 @@ mod tests {
207286

208287
assert!(device_fd.has_device_attr(&dist_attr).is_ok());
209288
assert!(device_fd.set_device_attr(&dist_attr).is_ok());
289+
290+
// Test `get_device_attr`. Here we try to extract the maximum supported number of IRQs.
291+
// This value should be saved in the address provided to the ioctl.
292+
let mut data: u32 = 0;
293+
294+
let mut gic_attr = kvm_bindings::kvm_device_attr::default();
295+
gic_attr.group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS;
296+
gic_attr.addr = data as u64;
297+
298+
// Without properly providing the address to where the
299+
// value will be stored, the ioctl fails with EFAULT.
300+
let res = device_fd.get_device_attr(&mut gic_attr);
301+
assert_eq!(res, Err(Error::new(libc::EFAULT)));
302+
303+
gic_attr.addr = &mut data as *const u32 as u64;
304+
assert!(device_fd.get_device_attr(&mut gic_attr).is_ok());
305+
// The maximum supported number of IRQs should be 128, same as the value
306+
// when we initialize the GIC.
307+
assert_eq!(data, 128);
210308
}
211309
}

src/kvm_ioctls.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ ioctl_iowr_nr!(KVM_CREATE_DEVICE, KVMIO, 0xe0, kvm_create_device);
219219
/* Available with KVM_CAP_DEVICE_CTRL */
220220
ioctl_iow_nr!(KVM_SET_DEVICE_ATTR, KVMIO, 0xe1, kvm_device_attr);
221221
/* Available with KVM_CAP_DEVICE_CTRL */
222+
ioctl_iow_nr!(KVM_GET_DEVICE_ATTR, KVMIO, 0xe2, kvm_device_attr);
223+
/* Available with KVM_CAP_DEVICE_CTRL */
222224
ioctl_iow_nr!(KVM_HAS_DEVICE_ATTR, KVMIO, 0xe3, kvm_device_attr);
223225

224226
#[cfg(test)]

0 commit comments

Comments
 (0)