From e1f8d9187d3f16e31d0c1304030ad412cf43b2bf Mon Sep 17 00:00:00 2001 From: Steven Richman Date: Fri, 1 May 2020 17:30:24 -0700 Subject: 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 Reviewed-by: Zach Reizner Tested-by: kokoro Commit-Queue: Steven Richman --- hypervisor/src/aarch64.rs | 24 +++++++++ hypervisor/src/kvm/aarch64.rs | 22 ++++++++ hypervisor/src/kvm/mod.rs | 89 +++++++++++++++++++++++++++---- hypervisor/src/kvm/x86_64.rs | 32 +++++++++++ hypervisor/src/lib.rs | 64 +++++++++++++++++----- hypervisor/src/types/mod.rs | 9 ---- hypervisor/src/types/x86.rs | 10 ---- hypervisor/src/x86_64.rs | 40 ++++++++++++++ hypervisor/tests/test_concrete.rs | 51 ++++++++++++++++++ hypervisor/tests/test_concrete_aarch64.rs | 21 ++++++++ hypervisor/tests/test_concrete_x86_64.rs | 39 ++++++++++++++ hypervisor/tests/types.rs | 18 +++++++ 12 files changed, 379 insertions(+), 40 deletions(-) create mode 100644 hypervisor/src/aarch64.rs create mode 100644 hypervisor/src/kvm/aarch64.rs create mode 100644 hypervisor/src/kvm/x86_64.rs delete mode 100644 hypervisor/src/types/mod.rs delete mode 100644 hypervisor/src/types/x86.rs create mode 100644 hypervisor/src/x86_64.rs create mode 100644 hypervisor/tests/test_concrete.rs create mode 100644 hypervisor/tests/test_concrete_aarch64.rs create mode 100644 hypervisor/tests/test_concrete_x86_64.rs create mode 100644 hypervisor/tests/types.rs (limited to 'hypervisor') 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; +} + +/// 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.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 { + Ok(KvmVm { guest_mem }) + } + + fn create_kvm_vcpu(&self, _id: usize) -> Result { + 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 { + Ok(RunnableKvmVcpu { + vcpu: self, + phantom: Default::default(), + }) + } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn get_supported_cpuid(&self) -> Result { - 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 { + Ok(VcpuExit::Unknown) + } +} + +impl Deref for RunnableKvmVcpu { + type Target = ::Vcpu; + + fn deref(&self) -> &Self::Target { + &self.vcpu + } +} - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn get_emulated_cpuid(&self) -> Result { - 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 { + unimplemented!("get_supported_cpuid for Kvm is not yet implemented"); + } + + fn get_emulated_cpuid(&self) -> Result { + 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.create_kvm_vcpu(id) + } +} + +impl VcpuX86_64 for KvmVcpu { + fn get_regs(&self) -> Result { + 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; - // Get the system emulated CPUID values. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn get_emulated_cpuid(&self) -> Result; +} + +/// 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; + + /// 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::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; +} + +/// 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, -} 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; + + /// Get the system emulated CPUID values. + fn get_emulated_cpuid(&self) -> Result; +} + +/// 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; +} + +/// 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; +} + +pub struct CpuId { + _cpu_id_entries: Vec, +} + +/// 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(_hypervm: &HyperVm, _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(_hypervm: &HyperVm) -> 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( + _hypervm: &HyperVm, + _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(hypervm: &HyperVm) -> 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 +where + HyperT: Hypervisor, + VmT: Vm, + VcpuT: Vcpu, +{ + pub hypervisor: HyperT, + pub vm: VmT, + pub vcpus: Vec, +} + +pub struct RunnableLinuxVm {} -- cgit 1.4.1