Skip to content

Commit d9a5bf6

Browse files
committed
Add support for KVM_GET_XSAVE2 ioctls
Since Linux 5.17, the `kvm_xsave` struct has a flexible array member (FAM) at the end, which can be retrieved using the `KVM_GET_XSAVE2` ioctl [1]. What makes this FAM special is that the length is not stored in the header, but has to be retrieved via `KVM_CHECK_CAPABILITY(KVM_CAP_XSAVE2)`, which returns the total size of the `kvm_xsave` struct (e.g. the traditional 4096 byte header + the size of the FAM). Thus, to support `KVM_GET_XSAVE2`, we first need to check the capability on the VM fd, construct a FamStructWrapper of the correct size, and then call `KVM_GET_XSAVE2`. [1]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-get-xsave2 Signed-off-by: Patrick Roy <[email protected]>
1 parent 0ddf7ef commit d9a5bf6

File tree

5 files changed

+60
-0
lines changed

5 files changed

+60
-0
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
### Added
66

7+
- [[#261](https://github.com/rust-vmm/kvm-ioctls/pull/261)]: Add support
8+
for `KVM_CAP_XSAVE2` and the `KVM_GET_XSAVE2` ioctl.
9+
710
### Changed
811

912
## v0.17.0

src/cap.rs

+2
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ pub enum Cap {
9393
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
9494
Xsave = KVM_CAP_XSAVE,
9595
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
96+
Xsave2 = KVM_CAP_XSAVE2,
97+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
9698
Xcrs = KVM_CAP_XCRS,
9799
PpcGetPvinfo = KVM_CAP_PPC_GET_PVINFO,
98100
PpcIrqLevel = KVM_CAP_PPC_IRQ_LEVEL,

src/ioctls/vcpu.rs

+38
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,41 @@ impl VcpuFd {
853853
Ok(xsave)
854854
}
855855

856+
/// X86 specific call that returns the vcpu's current "xsave struct" via `KVM_GET_XSAVE2`.
857+
///
858+
/// See the documentation for `KVM_GET_XSAVE2` in the
859+
/// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt).
860+
///
861+
/// # Arguments
862+
///
863+
/// * `vm_fd` - the file descriptor of the VM this [`Vcpu`] belongs to.
864+
///
865+
/// # Example
866+
///
867+
/// ```rust
868+
/// # extern crate kvm_ioctls;
869+
/// # use kvm_ioctls::Kvm;
870+
/// let kvm = Kvm::new().unwrap();
871+
/// let vm = kvm.create_vm().unwrap();
872+
/// let vcpu = vm.create_vcpu(0).unwrap();
873+
/// let xsave = vcpu.get_xsave2(&vm).unwrap();
874+
/// ```
875+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
876+
pub fn get_xsave2(&self, vm_fd: &crate::VmFd) -> Result<Xsave> {
877+
let xsave_size = vm_fd.xsave_size();
878+
let fam_size = xsave_size - std::mem::size_of::<kvm_xsave>();
879+
let mut xsave = Xsave::new(fam_size).map_err(|_| errno::Error::new(libc::EINVAL))?;
880+
881+
// SAFETY: Here we trust the kernel not to read past the end of the kvm_xsave struct.
882+
let ret = unsafe {
883+
ioctl_with_mut_ref(self, KVM_GET_XSAVE2(), &mut xsave.as_mut_fam_struct().xsave)
884+
};
885+
if ret != 0 {
886+
return Err(errno::Error::last());
887+
}
888+
Ok(xsave)
889+
}
890+
856891
/// X86 specific call that sets the vcpu's current "xsave struct".
857892
///
858893
/// See the documentation for `KVM_SET_XSAVE` in the
@@ -2200,6 +2235,9 @@ mod tests {
22002235
vcpu.set_xsave(&xsave).unwrap();
22012236
let other_xsave = vcpu.get_xsave().unwrap();
22022237
assert_eq!(&xsave.region[..], &other_xsave.region[..]);
2238+
let xsave2 = vcpu.get_xsave2(&vm).unwrap();
2239+
assert_eq!(&xsave.region[..], &xsave2.as_fam_struct_ref().xsave.region[..]);
2240+
vcpu.set_xsave(&xsave2.as_fam_struct_ref().xsave).unwrap();
22032241
}
22042242

22052243
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]

src/ioctls/vm.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,20 @@ impl VmFd {
12931293
self.run_size
12941294
}
12951295

1296+
/// Get the `kvm_xsave` size
1297+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1298+
pub fn xsave_size(&self) -> usize {
1299+
match self.check_extension_int(Cap::Xsave2) {
1300+
// If KVM does not support KVM_CAP_XSAVE2, then kvm_xsave will not
1301+
// have a FAM field, meaning the size of the struct is just the 4096 byte header array.
1302+
// Otherwise, KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2) will always return at least 4096,
1303+
// and describe the size of the header plus the FAM.
1304+
// See https://docs.kernel.org/virt/kvm/api.html#kvm-get-xsave2
1305+
..=0 => std::mem::size_of::<kvm_xsave>(),
1306+
size => size as usize,
1307+
}
1308+
}
1309+
12961310
/// Wrapper over `KVM_CHECK_EXTENSION`.
12971311
///
12981312
/// Returns 0 if the capability is not available and a positive integer otherwise.

src/kvm_ioctls.rs

+3
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ ioctl_iow_nr!(KVM_SET_DEBUGREGS, KVMIO, 0xa2, kvm_debugregs);
214214
/* Available with KVM_CAP_XSAVE */
215215
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
216216
ioctl_ior_nr!(KVM_GET_XSAVE, KVMIO, 0xa4, kvm_xsave);
217+
/* Available with KVM_CAP_XSAVE2 */
218+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
219+
ioctl_ior_nr!(KVM_GET_XSAVE2, KVMIO, 0xcf, kvm_xsave);
217220
/* Available with KVM_CAP_XSAVE */
218221
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
219222
ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave);

0 commit comments

Comments
 (0)