From 1422be739be2d6b512f272025c2bcc0264ab7812 Mon Sep 17 00:00:00 2001 From: Patrick Roy Date: Wed, 24 Apr 2024 13:10:04 +0100 Subject: [PATCH] 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 --- CHANGELOG.md | 3 +++ src/cap.rs | 2 ++ src/ioctls/vcpu.rs | 35 +++++++++++++++++++++++++++++++++++ src/ioctls/vm.rs | 14 ++++++++++++++ src/kvm_ioctls.rs | 3 +++ 5 files changed, 57 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1244d90..8e0e8c8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/cap.rs b/src/cap.rs index 71b71817..72b8f30f 100644 --- a/src/cap.rs +++ b/src/cap.rs @@ -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, diff --git a/src/ioctls/vcpu.rs b/src/ioctls/vcpu.rs index b4519f78..e532b898 100644 --- a/src/ioctls/vcpu.rs +++ b/src/ioctls/vcpu.rs @@ -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 { + let xsave_size = vm_fd.xsave_size(); + let fam_size = xsave_size - std::mem::size_of::(); + 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 diff --git a/src/ioctls/vm.rs b/src/ioctls/vm.rs index c8607100..018c44fd 100644 --- a/src/ioctls/vm.rs +++ b/src/ioctls/vm.rs @@ -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::(), + size => size as usize, + } + } + /// Wrapper over `KVM_CHECK_EXTENSION`. /// /// Returns 0 if the capability is not available and a positive integer otherwise. diff --git a/src/kvm_ioctls.rs b/src/kvm_ioctls.rs index 397bb14d..85b9e24b 100644 --- a/src/kvm_ioctls.rs +++ b/src/kvm_ioctls.rs @@ -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);