Skip to content

Commit

Permalink
Add support for KVM_GET_XSAVE2 ioctls
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
roypat committed Apr 24, 2024
1 parent 0ddf7ef commit 1422be7
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

### Added

- [[#261](https://github.com/rust-vmm/kvm-ioctls/pull/261)]: Add support
for `KVM_CAP_XSAVE2` and the `KVM_GET_XSAVE2` ioctl.

### Changed

## v0.17.0
Expand Down
2 changes: 2 additions & 0 deletions src/cap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ pub enum Cap {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Xsave = KVM_CAP_XSAVE,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Xsave2 = KVM_CAP_XSAVE2,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Xcrs = KVM_CAP_XCRS,
PpcGetPvinfo = KVM_CAP_PPC_GET_PVINFO,
PpcIrqLevel = KVM_CAP_PPC_IRQ_LEVEL,
Expand Down
35 changes: 35 additions & 0 deletions src/ioctls/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,41 @@ impl VcpuFd {
Ok(xsave)
}

/// X86 specific call that returns the vcpu's current "xsave struct" via `KVM_GET_XSAVE2`.
///
/// See the documentation for `KVM_GET_XSAVE2` in the
/// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt).
///
/// # Arguments
///
/// * `vm_fd` - the file descriptor of the VM this [`Vcpu`] belongs to.
///
/// # Example
///
/// ```rust
/// # extern crate kvm_ioctls;
/// # use kvm_ioctls::Kvm;
/// let kvm = Kvm::new().unwrap();
/// let vm = kvm.create_vm().unwrap();
/// let vcpu = vm.create_vcpu(0).unwrap();
/// let xsave = vcpu.get_xsave2(&vm).unwrap();
/// ```
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn get_xsave2(&self, vm_fd: &crate::VmFd) -> Result<Xsave> {
let xsave_size = vm_fd.xsave_size();
let fam_size = xsave_size - std::mem::size_of::<kvm_xsave>();
let mut xsave = Xsave::new(fam_size).map_err(|_| errno::Error::new(libc::EINVAL))?;

// SAFETY: Here we trust the kernel not to read past the end of the kvm_xsave struct.
let ret = unsafe {
ioctl_with_mut_ref(self, KVM_GET_XSAVE2(), &mut xsave.as_mut_fam_struct().xsave)
};
if ret != 0 {
return Err(errno::Error::last());
}
Ok(xsave)
}

/// X86 specific call that sets the vcpu's current "xsave struct".
///
/// See the documentation for `KVM_SET_XSAVE` in the
Expand Down
14 changes: 14 additions & 0 deletions src/ioctls/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,20 @@ impl VmFd {
self.run_size
}

/// Get the `kvm_xsave` size
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn xsave_size(&self) -> usize {
match self.check_extension_int(Cap::Xsave2) {
// If KVM does not support KVM_CAP_XSAVE2, then kvm_xsave will not
// have a FAM field, meaning the size of the struct is just the 4096 byte header array.
// Otherwise, KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2) will always return at least 4096,
// and describe the size of the header plus the FAM.
// See https://docs.kernel.org/virt/kvm/api.html#kvm-get-xsave2
..=0 => std::mem::size_of::<kvm_xsave>(),
size => size as usize,
}
}

/// Wrapper over `KVM_CHECK_EXTENSION`.
///
/// Returns 0 if the capability is not available and a positive integer otherwise.
Expand Down
3 changes: 3 additions & 0 deletions src/kvm_ioctls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ ioctl_iow_nr!(KVM_SET_DEBUGREGS, KVMIO, 0xa2, kvm_debugregs);
/* Available with KVM_CAP_XSAVE */
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ioctl_ior_nr!(KVM_GET_XSAVE, KVMIO, 0xa4, kvm_xsave);
/* Available with KVM_CAP_XSAVE2 */
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ioctl_ior_nr!(KVM_GET_XSAVE2, KVMIO, 0xcf, kvm_xsave);
/* Available with KVM_CAP_XSAVE */
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ioctl_iow_nr!(KVM_SET_XSAVE, KVMIO, 0xa5, kvm_xsave);
Expand Down

0 comments on commit 1422be7

Please sign in to comment.