summary refs log tree commit diff
path: root/hypervisor/src
diff options
context:
space:
mode:
authorUdam Saini <udam@google.com>2020-05-02 10:01:38 -0700
committerCommit Bot <commit-bot@chromium.org>2020-05-15 16:57:22 +0000
commit3730355406ed5fcc6505a35d55c683567159dd82 (patch)
tree6d7d16e9f588c682ce65e0b57f09db0e4c14edda /hypervisor/src
parentc569a579fe674773807b888f2eb65b23405c53b9 (diff)
downloadcrosvm-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.rs8
-rw-r--r--hypervisor/src/kvm/mod.rs55
-rw-r--r--hypervisor/src/kvm/x86_64.rs100
-rw-r--r--hypervisor/src/x86_64.rs20
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.