summary refs log tree commit diff
path: root/hypervisor
diff options
context:
space:
mode:
authorSteven Richman <srichman@google.com>2020-05-01 17:30:24 -0700
committerCommit Bot <commit-bot@chromium.org>2020-05-09 06:23:33 +0000
commite1f8d9187d3f16e31d0c1304030ad412cf43b2bf (patch)
tree45b8606c4d9fb31a4d4d335d8e0ea3bb31a6d9c2 /hypervisor
parenta7469062a2b25541cfe887f55441284553659ea0 (diff)
downloadcrosvm-e1f8d9187d3f16e31d0c1304030ad412cf43b2bf.tar
crosvm-e1f8d9187d3f16e31d0c1304030ad412cf43b2bf.tar.gz
crosvm-e1f8d9187d3f16e31d0c1304030ad412cf43b2bf.tar.bz2
crosvm-e1f8d9187d3f16e31d0c1304030ad412cf43b2bf.tar.lz
crosvm-e1f8d9187d3f16e31d0c1304030ad412cf43b2bf.tar.xz
crosvm-e1f8d9187d3f16e31d0c1304030ad412cf43b2bf.tar.zst
crosvm-e1f8d9187d3f16e31d0c1304030ad412cf43b2bf.zip
hypervisor: add Vm/Vcpu traits and Kvm impls
Add arch-agnostic traits Vm and Vcpu.  Add arch-specific traits
HypervisorXXX, VmXXX, VcpuXXX, with impls for KVM.

BUG=chromium:1077058
TEST=added test for functions and structs interacting with the traits

Change-Id: I809f42f32a558c7835831c90e24fca82ce7744ab
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2176562
Reviewed-by: Udam Saini <udam@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Steven Richman <srichman@google.com>
Diffstat (limited to 'hypervisor')
-rw-r--r--hypervisor/src/aarch64.rs24
-rw-r--r--hypervisor/src/kvm/aarch64.rs22
-rw-r--r--hypervisor/src/kvm/mod.rs89
-rw-r--r--hypervisor/src/kvm/x86_64.rs32
-rw-r--r--hypervisor/src/lib.rs64
-rw-r--r--hypervisor/src/types/mod.rs9
-rw-r--r--hypervisor/src/types/x86.rs10
-rw-r--r--hypervisor/src/x86_64.rs40
-rw-r--r--hypervisor/tests/test_concrete.rs51
-rw-r--r--hypervisor/tests/test_concrete_aarch64.rs21
-rw-r--r--hypervisor/tests/test_concrete_x86_64.rs39
-rw-r--r--hypervisor/tests/types.rs18
12 files changed, 379 insertions, 40 deletions
diff --git a/hypervisor/src/aarch64.rs b/hypervisor/src/aarch64.rs
new file mode 100644
index 0000000..dc06d8d
--- /dev/null
+++ b/hypervisor/src/aarch64.rs
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::{Vcpu, Vm};
+use sys_util::Result;
+
+/// A wrapper for using a VM on aarch64 and getting/setting its state.
+pub trait VmAArch64: Vm {
+    type Vcpu: VcpuArm;
+
+    /// Create a Vcpu with the specified Vcpu ID.
+    fn create_vcpu(&self, id: usize) -> Result<Self::Vcpu>;
+}
+
+/// A wrapper around creating and using a VCPU on aarch64.
+pub trait VcpuAArch64: Vcpu {
+    /// Sets the value of register on this VCPU.
+    ///
+    /// # Arguments
+    ///
+    /// * `reg_id` - Register ID, specified in the KVM API documentation for KVM_SET_ONE_REG
+    fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()>;
+}
diff --git a/hypervisor/src/kvm/aarch64.rs b/hypervisor/src/kvm/aarch64.rs
new file mode 100644
index 0000000..f674671
--- /dev/null
+++ b/hypervisor/src/kvm/aarch64.rs
@@ -0,0 +1,22 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// 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 super::{KvmVcpu, KvmVm};
+use crate::{Regs, VcpuAArch64, VmAarch64};
+
+impl VmAArch64 for KvmVm {
+    type Vcpu = KvmVcpu;
+
+    fn create_vcpu(&self, id: usize) -> Result<Self::Vcpu> {
+        self.create_kvm_vcpu(id)
+    }
+}
+
+impl VcpuAArch64 for KvmVcpu {
+    fn set_one_reg(&self, reg_id: u64, data: u64) -> Result<()> {
+        Ok(())
+    }
+}
diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs
index 7058921..5d78b00 100644
--- a/hypervisor/src/kvm/mod.rs
+++ b/hypervisor/src/kvm/mod.rs
@@ -2,13 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use super::{CpuId, Hypervisor, HypervisorCap};
-use libc::{open, O_CLOEXEC, O_RDWR};
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+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 libc::{open, O_CLOEXEC, O_RDWR};
+
 use sys_util::{
-    errno_result, AsRawDescriptor, FromRawDescriptor, RawDescriptor, Result, SafeDescriptor,
+    errno_result, AsRawDescriptor, FromRawDescriptor, GuestMemory, RawDescriptor, Result,
+    SafeDescriptor,
 };
 
+use crate::{Hypervisor, HypervisorCap, RunnableVcpu, Vcpu, VcpuExit, Vm};
+
 pub struct Kvm {
     kvm: SafeDescriptor,
 }
@@ -39,15 +49,76 @@ impl Hypervisor for Kvm {
     fn check_capability(&self, _cap: &HypervisorCap) -> bool {
         unimplemented!("check_capability for Kvm is not yet implemented");
     }
+}
+
+/// A wrapper around creating and using a KVM VM.
+pub struct KvmVm {
+    guest_mem: GuestMemory,
+}
+
+impl KvmVm {
+    /// Constructs a new `KvmVm` using the given `Kvm` instance.
+    pub fn new(_kvm: &Kvm, guest_mem: GuestMemory) -> Result<KvmVm> {
+        Ok(KvmVm { guest_mem })
+    }
+
+    fn create_kvm_vcpu(&self, _id: usize) -> Result<KvmVcpu> {
+        Ok(KvmVcpu {})
+    }
+}
+
+impl Vm for KvmVm {
+    fn get_guest_mem(&self) -> &GuestMemory {
+        &self.guest_mem
+    }
+}
+
+/// A wrapper around creating and using a KVM Vcpu.
+pub struct KvmVcpu {}
+
+impl Vcpu for KvmVcpu {
+    type Runnable = RunnableKvmVcpu;
+
+    fn to_runnable(self) -> Result<Self::Runnable> {
+        Ok(RunnableKvmVcpu {
+            vcpu: self,
+            phantom: Default::default(),
+        })
+    }
 
-    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    fn get_supported_cpuid(&self) -> Result<CpuId> {
-        unimplemented!("get_supported_cpuid for Kvm is not yet implemented");
+    fn request_interrupt_window(&self) -> Result<()> {
+        Ok(())
     }
+}
+
+/// A KvmVcpu that has a thread and can be run.
+pub struct RunnableKvmVcpu {
+    vcpu: KvmVcpu,
+
+    // vcpus must stay on the same thread once they start.
+    // Add the PhantomData pointer to ensure RunnableKvmVcpu is not `Send`.
+    phantom: std::marker::PhantomData<*mut u8>,
+}
+
+impl RunnableVcpu for RunnableKvmVcpu {
+    type Vcpu = KvmVcpu;
+
+    fn run(&self) -> Result<VcpuExit> {
+        Ok(VcpuExit::Unknown)
+    }
+}
+
+impl Deref for RunnableKvmVcpu {
+    type Target = <Self as RunnableVcpu>::Vcpu;
+
+    fn deref(&self) -> &Self::Target {
+        &self.vcpu
+    }
+}
 
-    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    fn get_emulated_cpuid(&self) -> Result<CpuId> {
-        unimplemented!("get_emulated_cpuid for Kvm is not yet implemented");
+impl DerefMut for RunnableKvmVcpu {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.vcpu
     }
 }
 
diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs
new file mode 100644
index 0000000..56681f9
--- /dev/null
+++ b/hypervisor/src/kvm/x86_64.rs
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// 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 super::{Kvm, KvmVcpu, KvmVm};
+use crate::{CpuId, HypervisorX86_64, Regs, VcpuX86_64, VmX86_64};
+
+impl HypervisorX86_64 for Kvm {
+    fn get_supported_cpuid(&self) -> Result<CpuId> {
+        unimplemented!("get_supported_cpuid for Kvm is not yet implemented");
+    }
+
+    fn get_emulated_cpuid(&self) -> Result<CpuId> {
+        unimplemented!("get_emulated_cpuid for Kvm is not yet implemented");
+    }
+}
+
+impl VmX86_64 for KvmVm {
+    type Vcpu = KvmVcpu;
+
+    fn create_vcpu(&self, id: usize) -> Result<Self::Vcpu> {
+        self.create_kvm_vcpu(id)
+    }
+}
+
+impl VcpuX86_64 for KvmVcpu {
+    fn get_regs(&self) -> Result<Regs> {
+        Ok(Regs {})
+    }
+}
diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs
index 056070b..0f4c278 100644
--- a/hypervisor/src/lib.rs
+++ b/hypervisor/src/lib.rs
@@ -3,23 +3,63 @@
 // found in the LICENSE file.
 
 //! A crate for abstracting the underlying kernel hypervisor used in crosvm.
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+pub mod aarch64;
 pub mod caps;
 pub mod kvm;
-pub mod types;
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub mod x86_64;
 
-use sys_util::Result;
+use std::ops::{Deref, DerefMut};
 
+use sys_util::{GuestMemory, Result};
+
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+pub use crate::aarch64::*;
 pub use crate::caps::*;
-pub use crate::types::*;
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub use crate::x86_64::*;
 
-/// A trait for managing the underlying cpu information for the hypervisor and to check its capabilities.
-trait Hypervisor {
-    // Checks if a particular `HypervisorCap` is available.
+/// A trait for checking hypervisor capabilities.
+pub trait Hypervisor {
+    /// Checks if a particular `HypervisorCap` is available.
     fn check_capability(&self, cap: &HypervisorCap) -> bool;
-    // Get the system supported CPUID values.
-    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    fn get_supported_cpuid(&self) -> Result<CpuId>;
-    // Get the system emulated CPUID values.
-    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-    fn get_emulated_cpuid(&self) -> Result<CpuId>;
+}
+
+/// A wrapper for using a VM and getting/setting its state.
+pub trait Vm {
+    // Gets the guest-mapped memory for the Vm
+    fn get_guest_mem(&self) -> &GuestMemory;
+}
+
+/// A wrapper around creating and using a VCPU.
+/// `Vcpu` provides all functionality except for running. To run, `to_runnable` must be called to
+/// lock the vcpu to a thread. Then the returned `RunnableVcpu` can be used for running.
+pub trait Vcpu {
+    type Runnable: RunnableVcpu;
+
+    /// Consumes `self` and returns a `RunnableVcpu`. A `RunnableVcpu` is required to run the guest.
+    fn to_runnable(self) -> Result<Self::Runnable>;
+
+    /// Request the Vcpu to exit the next time it can accept an interrupt.
+    fn request_interrupt_window(&self) -> Result<()>;
+}
+
+/// A Vcpu that has a thread and can be run. Created by calling `to_runnable` on a `Vcpu`.
+/// Implements `Deref` to a `Vcpu` so all `Vcpu` methods are usable, with the addition of the `run`
+/// function to execute the guest.
+pub trait RunnableVcpu: Deref<Target = <Self as RunnableVcpu>::Vcpu> + DerefMut {
+    type Vcpu: Vcpu;
+
+    /// Runs the VCPU until it exits, returning the reason for the exit.
+    ///
+    /// Note that the state of the VCPU and associated VM must be setup first for this to do
+    /// anything useful.
+    fn run(&self) -> Result<VcpuExit>;
+}
+
+/// A reason why a VCPU exited. One of these returns every time `Vcpu::run` is called.
+#[derive(Debug)]
+pub enum VcpuExit {
+    Unknown,
 }
diff --git a/hypervisor/src/types/mod.rs b/hypervisor/src/types/mod.rs
deleted file mode 100644
index 69fa9e4..0000000
--- a/hypervisor/src/types/mod.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2020 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-pub mod x86;
-
-#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-pub use self::x86::*;
diff --git a/hypervisor/src/types/x86.rs b/hypervisor/src/types/x86.rs
deleted file mode 100644
index cd5236a..0000000
--- a/hypervisor/src/types/x86.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2020 The Chromium OS Authors. All rights reserved.
-// 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;
-
-pub type CpuIdEntry = kvm_cpuid_entry2;
-pub struct CpuId {
-    _cpu_id_entries: Vec<CpuIdEntry>,
-}
diff --git a/hypervisor/src/x86_64.rs b/hypervisor/src/x86_64.rs
new file mode 100644
index 0000000..e05335c
--- /dev/null
+++ b/hypervisor/src/x86_64.rs
@@ -0,0 +1,40 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// 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.
+    fn get_supported_cpuid(&self) -> Result<CpuId>;
+
+    /// Get the system emulated CPUID values.
+    fn get_emulated_cpuid(&self) -> Result<CpuId>;
+}
+
+/// A wrapper for using a VM on x86_64 and getting/setting its state.
+pub trait VmX86_64: Vm {
+    type Vcpu: VcpuX86_64;
+
+    /// Create a Vcpu with the specified Vcpu ID.
+    fn create_vcpu(&self, id: usize) -> Result<Self::Vcpu>;
+}
+
+/// A wrapper around creating and using a VCPU on x86_64.
+pub trait VcpuX86_64: Vcpu {
+    /// Gets the VCPU registers.
+    fn get_regs(&self) -> Result<Regs>;
+}
+
+pub struct CpuId {
+    _cpu_id_entries: Vec<CpuIdEntry>,
+}
+
+/// The state of a vcpu's general-purpose registers.
+pub struct Regs {}
diff --git a/hypervisor/tests/test_concrete.rs b/hypervisor/tests/test_concrete.rs
new file mode 100644
index 0000000..fd6b89f
--- /dev/null
+++ b/hypervisor/tests/test_concrete.rs
@@ -0,0 +1,51 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO: Delete these tests soon, once we start getting real implementations in place.
+
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+mod test_concrete_aarch64;
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+mod test_concrete_x86_64;
+
+use sys_util::GuestMemory;
+
+use hypervisor::*;
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+use test_concrete_aarch64::*;
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+use test_concrete_x86_64::*;
+
+fn run_vcpu<T, U, V>(_hypervm: &HyperVm<T, U, V>, _linux: &RunnableLinuxVm, vcpu: impl Vcpu)
+where
+    T: Hypervisor,
+    U: Vm,
+    V: Vcpu,
+{
+    let vcpu = vcpu.to_runnable().unwrap();
+    vcpu.run().unwrap();
+    vcpu.request_interrupt_window().unwrap();
+}
+
+#[test]
+fn test_concrete_types() {
+    let cfg_use_kvm = true;
+    if cfg_use_kvm {
+        let hypervisor = kvm::Kvm::new().unwrap();
+        let mem = GuestMemory::new(&[]).unwrap();
+        let vm = kvm::KvmVm::new(&hypervisor, mem).unwrap();
+        let vcpu = vm.create_vcpu(0).unwrap();
+        let mut vcpus = vec![vcpu];
+        let mut hypervm = HyperVm {
+            hypervisor,
+            vm,
+            vcpus,
+        };
+        let linux = configure_vm(&hypervm);
+        vcpus = hypervm.vcpus.split_off(0);
+        for vcpu in vcpus.into_iter() {
+            run_vcpu(&hypervm, &linux, vcpu);
+        }
+    }
+}
diff --git a/hypervisor/tests/test_concrete_aarch64.rs b/hypervisor/tests/test_concrete_aarch64.rs
new file mode 100644
index 0000000..6c58bbe
--- /dev/null
+++ b/hypervisor/tests/test_concrete_aarch64.rs
@@ -0,0 +1,21 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[path = "types.rs"]
+mod types;
+
+#[allow(unused_imports)]
+use hypervisor::*;
+pub use types::{HyperVm, RunnableLinuxVm};
+
+// Inline cfg won't be needed in real code, but integration tests don't have conditional includes.
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+pub fn configure_vm<T, U, V>(_hypervm: &HyperVm<T, U, V>) -> RunnableLinuxVm
+where
+    T: Hypervisor,
+    U: VmAArch64,
+    V: VcpuAArch64,
+{
+    RunnableLinuxVm {}
+}
diff --git a/hypervisor/tests/test_concrete_x86_64.rs b/hypervisor/tests/test_concrete_x86_64.rs
new file mode 100644
index 0000000..6e24b80
--- /dev/null
+++ b/hypervisor/tests/test_concrete_x86_64.rs
@@ -0,0 +1,39 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#[path = "types.rs"]
+mod types;
+
+#[allow(unused_imports)]
+use hypervisor::*;
+pub use types::{HyperVm, RunnableLinuxVm};
+
+// Inline cfg won't be needed in real code, but integration tests don't have conditional includes.
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+fn configure_vcpu<T, U, V>(
+    _hypervm: &HyperVm<T, U, V>,
+    _linux: &RunnableLinuxVm,
+    vcpu: &impl VcpuX86_64,
+) where
+    T: HypervisorX86_64,
+    U: VmX86_64,
+    V: VcpuX86_64,
+{
+    vcpu.get_regs().unwrap();
+}
+
+// Inline cfg won't be needed in real code, but integration tests don't have conditional includes.
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub fn configure_vm<T, U, V>(hypervm: &HyperVm<T, U, V>) -> RunnableLinuxVm
+where
+    T: HypervisorX86_64,
+    U: VmX86_64,
+    V: VcpuX86_64,
+{
+    let linux = RunnableLinuxVm {};
+    for vcpu in hypervm.vcpus.iter() {
+        configure_vcpu(&hypervm, &linux, vcpu);
+    }
+    linux
+}
diff --git a/hypervisor/tests/types.rs b/hypervisor/tests/types.rs
new file mode 100644
index 0000000..d9272af
--- /dev/null
+++ b/hypervisor/tests/types.rs
@@ -0,0 +1,18 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use hypervisor::*;
+
+pub struct HyperVm<HyperT, VmT, VcpuT>
+where
+    HyperT: Hypervisor,
+    VmT: Vm,
+    VcpuT: Vcpu,
+{
+    pub hypervisor: HyperT,
+    pub vm: VmT,
+    pub vcpus: Vec<VcpuT>,
+}
+
+pub struct RunnableLinuxVm {}