diff options
author | Udam Saini <udam@google.com> | 2020-05-02 10:01:38 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-05-15 16:57:22 +0000 |
commit | 3730355406ed5fcc6505a35d55c683567159dd82 (patch) | |
tree | 6d7d16e9f588c682ce65e0b57f09db0e4c14edda /hypervisor/src | |
parent | c569a579fe674773807b888f2eb65b23405c53b9 (diff) | |
download | crosvm-3730355406ed5fcc6505a35d55c683567159dd82.tar crosvm-3730355406ed5fcc6505a35d55c683567159dd82.tar.gz crosvm-3730355406ed5fcc6505a35d55c683567159dd82.tar.bz2 crosvm-3730355406ed5fcc6505a35d55c683567159dd82.tar.lz crosvm-3730355406ed5fcc6505a35d55c683567159dd82.tar.xz crosvm-3730355406ed5fcc6505a35d55c683567159dd82.tar.zst crosvm-3730355406ed5fcc6505a35d55c683567159dd82.zip |
Implements the Hypervisor trait for Kvm.
This adds the ability for getting both supported/emulated cpuids from the kvm hypervisor. In addition, checking the available capabilities for kvm is now implemented. BUG=chromium:1077058 TEST=Added unit tests for each implemented function. Change-Id: Ide4c2840b7bfa022deae835eb734ea97c1859169 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2177641 Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Commit-Queue: Udam Saini <udam@google.com>
Diffstat (limited to 'hypervisor/src')
-rw-r--r-- | hypervisor/src/caps.rs | 8 | ||||
-rw-r--r-- | hypervisor/src/kvm/mod.rs | 55 | ||||
-rw-r--r-- | hypervisor/src/kvm/x86_64.rs | 100 | ||||
-rw-r--r-- | hypervisor/src/x86_64.rs | 20 |
4 files changed, 165 insertions, 18 deletions
diff --git a/hypervisor/src/caps.rs b/hypervisor/src/caps.rs index d088ca7..c3fe745 100644 --- a/hypervisor/src/caps.rs +++ b/hypervisor/src/caps.rs @@ -3,4 +3,10 @@ // found in the LICENSE file. /// An enumeration of different hypervisor capabilities. -pub enum HypervisorCap {} +pub enum HypervisorCap { + ArmPmuV3, + ImmediateExit, + S390UserSigp, + TscDeadlineTimer, + UserMemory, +} diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 5d78b00..ede1391 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -7,14 +7,15 @@ mod aarch64; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86_64; -use std::ops::{Deref, DerefMut}; -use std::os::raw::c_char; - +use kvm_sys::*; use libc::{open, O_CLOEXEC, O_RDWR}; - +use std::convert::TryFrom; +use std::ops::{Deref, DerefMut}; +use std::os::raw::{c_char, c_ulong}; +use std::os::unix::io::{AsRawFd, RawFd}; use sys_util::{ - errno_result, AsRawDescriptor, FromRawDescriptor, GuestMemory, RawDescriptor, Result, - SafeDescriptor, + errno_result, ioctl_with_val, AsRawDescriptor, Error, FromRawDescriptor, GuestMemory, + RawDescriptor, Result, SafeDescriptor, }; use crate::{Hypervisor, HypervisorCap, RunnableVcpu, Vcpu, VcpuExit, Vm}; @@ -23,6 +24,8 @@ pub struct Kvm { kvm: SafeDescriptor, } +type KvmCap = kvm::Cap; + impl Kvm { /// Opens `/dev/kvm/` and returns a Kvm object on success. pub fn new() -> Result<Kvm> { @@ -45,9 +48,22 @@ impl AsRawDescriptor for Kvm { } } +impl AsRawFd for Kvm { + fn as_raw_fd(&self) -> RawFd { + self.kvm.as_raw_descriptor() + } +} + impl Hypervisor for Kvm { - fn check_capability(&self, _cap: &HypervisorCap) -> bool { - unimplemented!("check_capability for Kvm is not yet implemented"); + fn check_capability(&self, cap: &HypervisorCap) -> bool { + if let Ok(kvm_cap) = KvmCap::try_from(cap) { + // this ioctl is safe because we know this kvm descriptor is valid, + // and we are copying over the kvm capability (u32) as a c_ulong value. + unsafe { ioctl_with_val(self, KVM_CHECK_EXTENSION(), kvm_cap as c_ulong) == 1 } + } else { + // this capability cannot be converted on this platform, so return false + false + } } } @@ -122,12 +138,33 @@ impl DerefMut for RunnableKvmVcpu { } } +impl<'a> TryFrom<&'a HypervisorCap> for KvmCap { + type Error = Error; + + fn try_from(cap: &'a HypervisorCap) -> Result<KvmCap> { + match cap { + HypervisorCap::ArmPmuV3 => Ok(KvmCap::ArmPmuV3), + HypervisorCap::ImmediateExit => Ok(KvmCap::ImmediateExit), + HypervisorCap::S390UserSigp => Ok(KvmCap::S390UserSigp), + HypervisorCap::TscDeadlineTimer => Ok(KvmCap::TscDeadlineTimer), + HypervisorCap::UserMemory => Ok(KvmCap::UserMemory), + } + } +} + #[cfg(test)] mod tests { - use super::Kvm; + use super::{Hypervisor, HypervisorCap, Kvm}; #[test] fn new() { Kvm::new().unwrap(); } + + #[test] + fn check_capability() { + let kvm = Kvm::new().unwrap(); + assert!(kvm.check_capability(&HypervisorCap::UserMemory)); + assert!(!kvm.check_capability(&HypervisorCap::S390UserSigp)); + } } diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs index 56681f9..43d6c97 100644 --- a/hypervisor/src/kvm/x86_64.rs +++ b/hypervisor/src/kvm/x86_64.rs @@ -2,18 +2,80 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use sys_util::Result; +use kvm_sys::*; +use libc::E2BIG; +use sys_util::{ioctl_with_mut_ptr, Error, Result}; use super::{Kvm, KvmVcpu, KvmVm}; -use crate::{CpuId, HypervisorX86_64, Regs, VcpuX86_64, VmX86_64}; +use crate::{CpuId, CpuIdEntry, HypervisorX86_64, Regs, VcpuX86_64, VmX86_64}; + +type KvmCpuId = kvm::CpuId; + +impl Kvm { + pub fn get_cpuid(&self, kind: u64) -> Result<CpuId> { + const KVM_MAX_ENTRIES: usize = 256; + self.get_cpuid_with_initial_capacity(kind, KVM_MAX_ENTRIES) + } + + fn get_cpuid_with_initial_capacity(&self, kind: u64, initial_capacity: usize) -> Result<CpuId> { + let mut entries: usize = initial_capacity; + + loop { + let mut kvm_cpuid = KvmCpuId::new(entries); + + let ret = unsafe { + // ioctl is unsafe. The kernel is trusted not to write beyond the bounds of the + // memory allocated for the struct. The limit is read from nent within KvmCpuId, + // which is set to the allocated size above. + ioctl_with_mut_ptr(self, kind, kvm_cpuid.as_mut_ptr()) + }; + if ret < 0 { + let err = Error::last(); + match err.errno() { + E2BIG => { + // double the available memory for cpuid entries for kvm. + if let Some(val) = entries.checked_mul(2) { + entries = val; + } else { + return Err(err); + } + } + _ => return Err(err), + } + } else { + return Ok(CpuId::from(&kvm_cpuid)); + } + } + } +} + +impl<'a> From<&'a KvmCpuId> for CpuId { + fn from(kvm_cpuid: &'a KvmCpuId) -> CpuId { + let kvm_entries = kvm_cpuid.entries_slice(); + let mut cpu_id_entries = Vec::with_capacity(kvm_entries.len()); + + for entry in kvm_entries { + let cpu_id_entry = CpuIdEntry { + function: entry.function, + index: entry.index, + eax: entry.eax, + ebx: entry.ebx, + ecx: entry.ecx, + edx: entry.edx, + }; + cpu_id_entries.push(cpu_id_entry) + } + CpuId { cpu_id_entries } + } +} impl HypervisorX86_64 for Kvm { fn get_supported_cpuid(&self) -> Result<CpuId> { - unimplemented!("get_supported_cpuid for Kvm is not yet implemented"); + self.get_cpuid(KVM_GET_SUPPORTED_CPUID()) } fn get_emulated_cpuid(&self) -> Result<CpuId> { - unimplemented!("get_emulated_cpuid for Kvm is not yet implemented"); + self.get_cpuid(KVM_GET_EMULATED_CPUID()) } } @@ -30,3 +92,33 @@ impl VcpuX86_64 for KvmVcpu { Ok(Regs {}) } } + +#[cfg(test)] +mod tests { + use super::Kvm; + use crate::HypervisorX86_64; + use kvm_sys::*; + + #[test] + fn get_supported_cpuid() { + let hypervisor = Kvm::new().unwrap(); + let cpuid = hypervisor.get_supported_cpuid().unwrap(); + assert!(cpuid.cpu_id_entries.len() > 0); + } + + #[test] + fn get_emulated_cpuid() { + let hypervisor = Kvm::new().unwrap(); + let cpuid = hypervisor.get_emulated_cpuid().unwrap(); + assert!(cpuid.cpu_id_entries.len() > 0); + } + + #[test] + fn entries_double_on_error() { + let hypervisor = Kvm::new().unwrap(); + let cpuid = hypervisor + .get_cpuid_with_initial_capacity(KVM_GET_SUPPORTED_CPUID(), 4) + .unwrap(); + assert!(cpuid.cpu_id_entries.len() > 4); + } +} diff --git a/hypervisor/src/x86_64.rs b/hypervisor/src/x86_64.rs index e05335c..87f0777 100644 --- a/hypervisor/src/x86_64.rs +++ b/hypervisor/src/x86_64.rs @@ -2,13 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use kvm_sys::kvm_cpuid_entry2; use sys_util::Result; use crate::{Hypervisor, Vcpu, Vm}; -pub type CpuIdEntry = kvm_cpuid_entry2; - /// A trait for managing cpuids for an x86_64 hypervisor and for checking its capabilities. pub trait HypervisorX86_64: Hypervisor { /// Get the system supported CPUID values. @@ -32,8 +29,23 @@ pub trait VcpuX86_64: Vcpu { fn get_regs(&self) -> Result<Regs>; } +/// A CpuId Entry contains supported feature information for the given processor. +/// This can be modified by the hypervisor to pass additional information to the guest kernel +/// about the hypervisor or vm. Information is returned in the eax, ebx, ecx and edx registers +/// by the cpu for a given function and index/subfunction (passed into the cpu via the eax and ecx +/// register respectively). +pub struct CpuIdEntry { + pub function: u32, + pub index: u32, + pub eax: u32, + pub ebx: u32, + pub ecx: u32, + pub edx: u32, +} + +/// A container for the list of cpu id entries for the hypervisor and underlying cpu. pub struct CpuId { - _cpu_id_entries: Vec<CpuIdEntry>, + pub cpu_id_entries: Vec<CpuIdEntry>, } /// The state of a vcpu's general-purpose registers. |