mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 20:48:55 +00:00
hypervisor: Add get_xsave and set_xsave support to x86.
This change adds the function get_xsave to the x86/86_64 platform. get_xsave allows us to get state of the x87 FPU, MMX, XMM and YMM registers. This change is needed for serializing vCPUs. Adds also function to check if XSAVE2 Capability is enabled. get_xsave is not implemented yet for Windows. BUG=b:266515147 Test=Build and run VM Change-Id: I5a9d3b2b86bc11a66db331b4b25d0c348459ab69 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4292298 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Commit-Queue: Elie Kheirallah <khei@google.com>
This commit is contained in:
parent
af1a48da8a
commit
041764bcd3
6 changed files with 109 additions and 0 deletions
|
@ -54,6 +54,7 @@ use hypervisor::Vcpu;
|
|||
use hypervisor::VcpuExit;
|
||||
use hypervisor::VcpuRunHandle;
|
||||
use hypervisor::VcpuX86_64;
|
||||
use hypervisor::Xsave;
|
||||
use resources::AddressRange;
|
||||
use resources::SystemAllocator;
|
||||
use resources::SystemAllocatorConfig;
|
||||
|
@ -739,6 +740,12 @@ impl VcpuX86_64 for FakeVcpu {
|
|||
fn set_fpu(&self, _fpu: &Fpu) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn get_xsave(&self) -> Result<Xsave> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn set_xsave(&self, _xsave: &Xsave) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn get_debugregs(&self) -> Result<DebugRegs> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ use data_model::vec_with_array_field;
|
|||
use libc::EINVAL;
|
||||
use libc::ENOENT;
|
||||
use libc::ENXIO;
|
||||
use libc::EOPNOTSUPP;
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
use super::*;
|
||||
|
@ -47,6 +48,7 @@ use crate::Vcpu;
|
|||
use crate::VcpuExit;
|
||||
use crate::VcpuRunHandle;
|
||||
use crate::VcpuX86_64;
|
||||
use crate::Xsave;
|
||||
|
||||
// HAXM exit reasons
|
||||
// IO port request
|
||||
|
@ -469,6 +471,14 @@ impl VcpuX86_64 for HaxmVcpu {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_xsave(&self) -> Result<Xsave> {
|
||||
Err(Error::new(EOPNOTSUPP))
|
||||
}
|
||||
|
||||
fn set_xsave(&self, _xsave: &Xsave) -> Result<()> {
|
||||
Err(Error::new(EOPNOTSUPP))
|
||||
}
|
||||
|
||||
/// Gets the VCPU debug registers.
|
||||
fn get_debugregs(&self) -> Result<DebugRegs> {
|
||||
Ok(self.get_vcpu_state()?.get_debugregs())
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
use std::arch::x86_64::CpuidResult;
|
||||
use std::mem::size_of;
|
||||
|
||||
use base::errno_result;
|
||||
use base::error;
|
||||
|
@ -20,6 +21,7 @@ use base::Result;
|
|||
use data_model::vec_with_array_field;
|
||||
use kvm_sys::*;
|
||||
use libc::E2BIG;
|
||||
use libc::EIO;
|
||||
use libc::ENXIO;
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
|
@ -55,10 +57,12 @@ use crate::VcpuExit;
|
|||
use crate::VcpuX86_64;
|
||||
use crate::VmCap;
|
||||
use crate::VmX86_64;
|
||||
use crate::Xsave;
|
||||
use crate::MAX_IOAPIC_PINS;
|
||||
use crate::NUM_IOAPIC_PINS;
|
||||
|
||||
type KvmCpuId = kvm::CpuId;
|
||||
const KVM_XSAVE_MAX_SIZE: i32 = 4096;
|
||||
|
||||
pub fn get_cpuid_with_initial_capacity<T: AsRawDescriptor>(
|
||||
descriptor: &T,
|
||||
|
@ -673,6 +677,63 @@ impl VcpuX86_64 for KvmVcpu {
|
|||
}
|
||||
}
|
||||
|
||||
/// If the VM reports using XSave2, the function will call XSave2.
|
||||
fn get_xsave(&self) -> Result<Xsave> {
|
||||
// Safe because we know that our file is a VM fd, we know that the
|
||||
// kernel will only read correct amount of memory from our pointer, and
|
||||
// we verify the return result.
|
||||
// Get the size of Xsave in bytes. Values are of type u32.
|
||||
let size =
|
||||
unsafe { ioctl_with_val(&self.vm, KVM_CHECK_EXTENSION(), KVM_CAP_XSAVE2 as u64) };
|
||||
if size < 0 {
|
||||
return errno_result();
|
||||
}
|
||||
// Size / sizeof(u32) = len of vec.
|
||||
let mut xsave: Vec<u32> = vec![0u32; size as usize / size_of::<u32>()];
|
||||
let ioctl_nr = if size > KVM_XSAVE_MAX_SIZE {
|
||||
KVM_GET_XSAVE2()
|
||||
} else {
|
||||
KVM_GET_XSAVE()
|
||||
};
|
||||
// Safe because we know that our file is a VCPU fd, we know the kernel will only write the
|
||||
// correct amount of memory to our pointer, and we verify the return result.
|
||||
let ret = unsafe { ioctl_with_mut_ptr(self, ioctl_nr, xsave.as_mut_ptr()) };
|
||||
if ret == 0 {
|
||||
Ok(Xsave::from(xsave))
|
||||
} else {
|
||||
errno_result()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_xsave(&self, xsave: &Xsave) -> Result<()> {
|
||||
// Safe because we know that our file is a VM fd, we know that the
|
||||
// kernel will only read correct amount of memory from our pointer, and
|
||||
// get size from KVM_CAP_XSAVE2. Will return at least 4096 as a value if XSAVE2 is not
|
||||
// supported or if no extensions are enabled. Otherwise it will return a value higher than
|
||||
// 4096.
|
||||
let size =
|
||||
unsafe { ioctl_with_val(&self.vm, KVM_CHECK_EXTENSION(), KVM_CAP_XSAVE2 as u64) };
|
||||
if size < 0 {
|
||||
return errno_result();
|
||||
}
|
||||
// Ensure xsave is the same size as used in get_xsave.
|
||||
// Return err if sizes don't match => not the same extensions are enabled for CPU.
|
||||
if xsave.0.len() != size as usize / size_of::<u32>() {
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
|
||||
// Safe because we know that our file is a VCPU fd, we know the kernel will only write the
|
||||
// correct amount of memory to our pointer, and we verify the return result.
|
||||
// Because of the len check above, and because the layout of `struct kvm_xsave` is
|
||||
// compatible with a slice of `u32`, we can pass the pointer to `xsave` directly.
|
||||
let ret = unsafe { ioctl_with_ptr(self, KVM_SET_XSAVE(), xsave.0.as_ptr()) };
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
errno_result()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_debugregs(&self) -> Result<DebugRegs> {
|
||||
// Safe because we know that our file is a VCPU fd, we know the kernel will only write the
|
||||
// correct amount of memory to our pointer, and we verify the return result.
|
||||
|
@ -1316,6 +1377,12 @@ impl From<&Fpu> for kvm_fpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl Xsave {
|
||||
fn from(r: Vec<u32>) -> Self {
|
||||
Xsave(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&kvm_debugregs> for DebugRegs {
|
||||
fn from(r: &kvm_debugregs) -> Self {
|
||||
DebugRegs {
|
||||
|
|
|
@ -17,6 +17,7 @@ use libc::EBUSY;
|
|||
use libc::EINVAL;
|
||||
use libc::ENOENT;
|
||||
use libc::ENXIO;
|
||||
use libc::EOPNOTSUPP;
|
||||
use vm_memory::GuestAddress;
|
||||
use winapi::shared::winerror::E_UNEXPECTED;
|
||||
|
||||
|
@ -38,6 +39,7 @@ use crate::Vcpu;
|
|||
use crate::VcpuExit;
|
||||
use crate::VcpuRunHandle;
|
||||
use crate::VcpuX86_64;
|
||||
use crate::Xsave;
|
||||
|
||||
const WHPX_EXIT_DIRECTION_MMIO_READ: u8 = 0;
|
||||
const WHPX_EXIT_DIRECTION_MMIO_WRITE: u8 = 1;
|
||||
|
@ -1047,6 +1049,18 @@ impl VcpuX86_64 for WhpxVcpu {
|
|||
})
|
||||
}
|
||||
|
||||
/// Gets the VCPU XSAVE.
|
||||
// TODO: b/270734340 implement
|
||||
fn get_xsave(&self) -> Result<Xsave> {
|
||||
Err(Error::new(EOPNOTSUPP))
|
||||
}
|
||||
|
||||
/// Sets the VCPU XSAVE.
|
||||
// TODO: b/270734340 implement
|
||||
fn set_xsave(&self, _xsave: &Xsave) -> Result<()> {
|
||||
Err(Error::new(EOPNOTSUPP))
|
||||
}
|
||||
|
||||
/// Gets the VCPU debug registers.
|
||||
fn get_debugregs(&self) -> Result<DebugRegs> {
|
||||
let mut whpx_debugregs: WhpxDebugRegs = Default::default();
|
||||
|
|
|
@ -95,6 +95,12 @@ pub trait VcpuX86_64: Vcpu {
|
|||
/// Sets the VCPU extended control registers.
|
||||
fn set_xcrs(&self, xcrs: &[Register]) -> Result<()>;
|
||||
|
||||
/// Gets the VCPU x87 FPU, MMX, XMM, YMM and MXCSR registers.
|
||||
fn get_xsave(&self) -> Result<Xsave>;
|
||||
|
||||
/// Sets the VCPU x87 FPU, MMX, XMM, YMM and MXCSR registers.
|
||||
fn set_xsave(&self, xsave: &Xsave) -> Result<()>;
|
||||
|
||||
/// Gets the model-specific registers. `msrs` specifies the MSR indexes to be queried, and
|
||||
/// on success contains their indexes and values.
|
||||
fn get_msrs(&self, msrs: &mut Vec<Register>) -> Result<()>;
|
||||
|
@ -858,3 +864,7 @@ pub enum CpuHybridType {
|
|||
/// Intel Core.
|
||||
Core,
|
||||
}
|
||||
|
||||
/// State of the VCPU's x87 FPU, MMX, XMM, YMM registers.
|
||||
/// May contain more state depending on enabled extensions.
|
||||
pub struct Xsave(pub Vec<u32>);
|
||||
|
|
|
@ -59,6 +59,7 @@ pub mod x86 {
|
|||
ioctl_ior_nr!(KVM_GET_XCRS, KVMIO, 0xa6, kvm_xcrs);
|
||||
ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs);
|
||||
ioctl_iowr_nr!(KVM_GET_SUPPORTED_HV_CPUID, KVMIO, 0xc1, kvm_cpuid2);
|
||||
ioctl_ior_nr!(KVM_GET_XSAVE2, KVMIO, 0xcf, kvm_xsave);
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
|
|
Loading…
Reference in a new issue