From 23c46b162e638be7068aaa09f07791601c7cd179 Mon Sep 17 00:00:00 2001 From: Muminul Islam Date: Thu, 3 Dec 2020 15:24:57 -0800 Subject: [PATCH] hypervisor, vmm: Implement the mshv module and add mshv support in vmm Implement hypervisor, Vm, Vcpu crate at a minimal functionalities. Also adds the mshv feature gate, separates out the functionalities between kvm and mshv inside the vmm crate. Co-Developed-by: Nuno Das Neves Signed-off-by: Nuno Das Neves Co-Developed-by: Praveen Paladugu Signed-off-by: Praveen Paladugu Co-Developed-by: Samuel Ortiz Signed-off-by: Samuel Ortiz Co-Developed-by: Wei Liu Signed-off-by: Wei Liu Signed-off-by: Muminul Islam --- hypervisor/src/cpu.rs | 26 +- hypervisor/src/hypervisor.rs | 10 +- hypervisor/src/lib.rs | 5 + hypervisor/src/mshv/mod.rs | 457 +++++++++++++++++++++++++++++++++++ hypervisor/src/vm.rs | 8 +- 5 files changed, 499 insertions(+), 7 deletions(-) diff --git a/hypervisor/src/cpu.rs b/hypervisor/src/cpu.rs index fccdb8909..b30cad3c6 100644 --- a/hypervisor/src/cpu.rs +++ b/hypervisor/src/cpu.rs @@ -12,13 +12,17 @@ use crate::aarch64::VcpuInit; #[cfg(target_arch = "aarch64")] use crate::aarch64::{RegList, Register, StandardRegisters}; -use crate::{CpuState, MpState}; - +#[cfg(target_arch = "x86_64")] +use crate::x86_64::{CpuId, LapicState}; #[cfg(target_arch = "x86_64")] use crate::x86_64::{ - CpuId, ExtendedControlRegisters, FpuState, LapicState, MsrEntries, SpecialRegisters, - StandardRegisters, VcpuEvents, Xsave, + ExtendedControlRegisters, FpuState, MsrEntries, SpecialRegisters, StandardRegisters, VcpuEvents, }; +use crate::CpuState; +#[cfg(feature = "kvm")] +use crate::MpState; +#[cfg(target_arch = "x86_64")] +use crate::Xsave; use thiserror::Error; #[derive(Error, Debug)] @@ -151,6 +155,20 @@ pub enum HypervisorCpuError { #[error("Failed to notify guest its clock was paused: {0}")] NotifyGuestClockPaused(#[source] anyhow::Error), /// + /// Setting debug register error + /// + #[error("Failed to set debug registers: {0}")] + SetDebugRegs(#[source] anyhow::Error), + /// + /// Getting debug register error + /// + #[error("Failed to get debug registers: {0}")] + GetDebugRegs(#[source] anyhow::Error), + /// + /// Write to Guest Mem + /// + #[error("Failed to write to Guest Mem at: {0}")] + GuestMemWrite(#[source] anyhow::Error), /// Enabling HyperV SynIC error /// #[error("Failed to enable HyperV SynIC")] diff --git a/hypervisor/src/hypervisor.rs b/hypervisor/src/hypervisor.rs index 485e3abb2..aac0a7f3d 100644 --- a/hypervisor/src/hypervisor.rs +++ b/hypervisor/src/hypervisor.rs @@ -9,7 +9,9 @@ // use crate::vm::Vm; #[cfg(target_arch = "x86_64")] -use crate::x86_64::{CpuId, MsrList}; +use crate::x86_64::CpuId; +#[cfg(target_arch = "x86_64")] +use crate::x86_64::MsrList; #[cfg(all(feature = "kvm", target_arch = "x86_64"))] use kvm_ioctls::Cap; use std::sync::Arc; @@ -20,6 +22,11 @@ use thiserror::Error; /// /// pub enum HypervisorError { + /// + /// hypervisor creation error + /// + #[error("Failed to create the hypervisor: {0}")] + HypervisorCreate(#[source] anyhow::Error), /// /// Vm creation failure /// @@ -103,6 +110,7 @@ pub trait Hypervisor: Send + Sync { /// Get the supported CpuID /// fn get_cpuid(&self) -> Result; + #[cfg(not(feature = "mshv"))] /// /// Check particular extensions if any /// diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 6d0336ba0..5e3c376c2 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -56,6 +56,8 @@ pub use cpu::{HypervisorCpuError, Vcpu, VmExit}; pub use device::{Device, HypervisorDeviceError}; #[cfg(feature = "kvm")] pub use kvm::*; +#[cfg(all(feature = "mshv", target_arch = "x86_64"))] +pub use mshv::*; pub use vm::{DataMatch, HypervisorVmError, Vm}; use std::sync::Arc; @@ -64,5 +66,8 @@ pub fn new() -> std::result::Result, HypervisorError> { #[cfg(feature = "kvm")] let hv = kvm::KvmHypervisor::new()?; + #[cfg(feature = "mshv")] + let hv = mshv::MshvHypervisor::new()?; + Ok(Arc::new(hv)) } diff --git a/hypervisor/src/mshv/mod.rs b/hypervisor/src/mshv/mod.rs index 5049a0aca..642b0433b 100644 --- a/hypervisor/src/mshv/mod.rs +++ b/hypervisor/src/mshv/mod.rs @@ -9,9 +9,464 @@ #![allow(unused_macros)] #![allow(non_upper_case_globals)] +use crate::arch::emulator::{EmulationError, PlatformEmulator, PlatformError}; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86::emulator::{Emulator, EmulatorCpuState}; +use crate::cpu; +use crate::cpu::Vcpu; +use crate::hypervisor; +use crate::vm::{self, VmmOps}; +pub use mshv_bindings::*; +use mshv_ioctls::{set_registers_64, Mshv, VcpuFd, VmFd}; +use serde_derive::{Deserialize, Serialize}; +use std::sync::Arc; +use vm::DataMatch; // x86_64 dependencies #[cfg(target_arch = "x86_64")] pub mod x86_64; +use crate::device; +use std::convert::TryInto; +use vmm_sys_util::eventfd::EventFd; +#[cfg(target_arch = "x86_64")] +pub use x86_64::VcpuMshvState as CpuState; +#[cfg(target_arch = "x86_64")] +pub use x86_64::*; + +// Wei: for emulating irqfd and ioeventfd +use std::collections::HashMap; +use std::fs::File; +use std::io; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::sync::{Mutex, RwLock}; +use std::thread; + +pub const PAGE_SHIFT: usize = 12; + +#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)] +pub struct HvState { + hypercall_page: u64, +} + +pub use HvState as VmState; + +/// Wrapper over mshv system ioctls. +pub struct MshvHypervisor { + mshv: Mshv, +} + +impl MshvHypervisor { + /// Create a hypervisor based on Mshv + pub fn new() -> hypervisor::Result { + let mshv_obj = + Mshv::new().map_err(|e| hypervisor::HypervisorError::HypervisorCreate(e.into()))?; + Ok(MshvHypervisor { mshv: mshv_obj }) + } +} +/// Implementation of Hypervisor trait for Mshv +/// Example: +/// #[cfg(feature = "mshv")] +/// extern crate hypervisor +/// let mshv = hypervisor::mshv::MshvHypervisor::new().unwrap(); +/// let hypervisor: Arc = Arc::new(mshv); +/// let vm = hypervisor.create_vm().expect("new VM fd creation failed"); +/// +impl hypervisor::Hypervisor for MshvHypervisor { + /// Create a mshv vm object and return the object as Vm trait object + /// Example + /// # extern crate hypervisor; + /// # use hypervisor::MshvHypervisor; + /// use hypervisor::MshvVm; + /// let hypervisor = MshvHypervisor::new().unwrap(); + /// let vm = hypervisor.create_vm().unwrap() + /// + fn create_vm(&self) -> hypervisor::Result> { + let fd: VmFd; + loop { + match self.mshv.create_vm() { + Ok(res) => fd = res, + Err(e) => { + if e.errno() == libc::EINTR { + // If the error returned is EINTR, which means the + // ioctl has been interrupted, we have to retry as + // this can't be considered as a regular error. + continue; + } else { + return Err(hypervisor::HypervisorError::VmCreate(e.into())); + } + } + } + break; + } + + let msr_list = self.get_msr_list()?; + let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize; + let mut msrs = MsrEntries::new(num_msrs); + let indices = msr_list.as_slice(); + let msr_entries = msrs.as_mut_slice(); + for (pos, index) in indices.iter().enumerate() { + msr_entries[pos].index = *index; + } + let vm_fd = Arc::new(fd); + + Ok(Arc::new(MshvVm { fd: vm_fd, msrs })) + } + /// + /// Get the supported CpuID + /// + fn get_cpuid(&self) -> hypervisor::Result { + Ok(CpuId::new(1 as usize)) + } + #[cfg(target_arch = "x86_64")] + /// + /// Retrieve the list of MSRs supported by KVM. + /// + fn get_msr_list(&self) -> hypervisor::Result { + self.mshv + .get_msr_index_list() + .map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into())) + } +} + +/// Vcpu struct for Microsoft Hypervisor +pub struct MshvVcpu { + fd: VcpuFd, + vp_index: u8, + cpuid: CpuId, + msrs: MsrEntries, +} + +/// Implementation of Vcpu trait for Microsoft Hypervisor +/// Example: +/// #[cfg(feature = "mshv")] +/// extern crate hypervisor +/// let mshv = hypervisor::mshv::MshvHypervisor::new().unwrap(); +/// let hypervisor: Arc = Arc::new(mshv); +/// let vm = hypervisor.create_vm().expect("new VM fd creation failed"); +/// let vcpu = vm.create_vcpu(0).unwrap(); +/// vcpu.get/set().unwrap() +/// +impl cpu::Vcpu for MshvVcpu { + #[cfg(target_arch = "x86_64")] + /// + /// Returns the vCPU general purpose registers. + /// + fn get_regs(&self) -> cpu::Result { + self.fd + .get_regs() + .map_err(|e| cpu::HypervisorCpuError::GetStandardRegs(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// Sets the vCPU general purpose registers. + /// + fn set_regs(&self, regs: &StandardRegisters) -> cpu::Result<()> { + self.fd + .set_regs(regs) + .map_err(|e| cpu::HypervisorCpuError::SetStandardRegs(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// Returns the vCPU special registers. + /// + fn get_sregs(&self) -> cpu::Result { + self.fd + .get_sregs() + .map_err(|e| cpu::HypervisorCpuError::GetSpecialRegs(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// Sets the vCPU special registers. + /// + fn set_sregs(&self, sregs: &SpecialRegisters) -> cpu::Result<()> { + self.fd + .set_sregs(sregs) + .map_err(|e| cpu::HypervisorCpuError::SetSpecialRegs(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// Returns the floating point state (FPU) from the vCPU. + /// + fn get_fpu(&self) -> cpu::Result { + self.fd + .get_fpu() + .map_err(|e| cpu::HypervisorCpuError::GetFloatingPointRegs(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// Set the floating point state (FPU) of a vCPU. + /// + fn set_fpu(&self, fpu: &FpuState) -> cpu::Result<()> { + self.fd + .set_fpu(fpu) + .map_err(|e| cpu::HypervisorCpuError::SetFloatingPointRegs(e.into())) + } + + #[cfg(target_arch = "x86_64")] + /// + /// Returns the model-specific registers (MSR) for this vCPU. + /// + fn get_msrs(&self, msrs: &mut MsrEntries) -> cpu::Result { + self.fd + .get_msrs(msrs) + .map_err(|e| cpu::HypervisorCpuError::GetMsrEntries(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// Setup the model-specific registers (MSR) for this vCPU. + /// Returns the number of MSR entries actually written. + /// + fn set_msrs(&self, msrs: &MsrEntries) -> cpu::Result { + self.fd + .set_msrs(msrs) + .map_err(|e| cpu::HypervisorCpuError::SetMsrEntries(e.into())) + } + + #[cfg(target_arch = "x86_64")] + /// + /// X86 specific call that returns the vcpu's current "xcrs". + /// + fn get_xcrs(&self) -> cpu::Result { + self.fd + .get_xcrs() + .map_err(|e| cpu::HypervisorCpuError::GetXcsr(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// X86 specific call that sets the vcpu's current "xcrs". + /// + fn set_xcrs(&self, xcrs: &ExtendedControlRegisters) -> cpu::Result<()> { + self.fd + .set_xcrs(&xcrs) + .map_err(|e| cpu::HypervisorCpuError::SetXcsr(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// Returns currently pending exceptions, interrupts, and NMIs as well as related + /// states of the vcpu. + /// + fn get_vcpu_events(&self) -> cpu::Result { + self.fd + .get_vcpu_events() + .map_err(|e| cpu::HypervisorCpuError::GetVcpuEvents(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// Sets pending exceptions, interrupts, and NMIs as well as related states + /// of the vcpu. + /// + fn set_vcpu_events(&self, events: &VcpuEvents) -> cpu::Result<()> { + self.fd + .set_vcpu_events(events) + .map_err(|e| cpu::HypervisorCpuError::SetVcpuEvents(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// X86 specific call to enable HyperV SynIC + /// + fn enable_hyperv_synic(&self) -> cpu::Result<()> { + /* We always have SynIC enabled on MSHV */ + Ok(()) + } + fn run(&self) -> std::result::Result { + Ok(cpu::VmExit::Ignore) + } + #[cfg(target_arch = "x86_64")] + /// + /// X86 specific call to setup the CPUID registers. + /// + fn set_cpuid2(&self, cpuid: &CpuId) -> cpu::Result<()> { + Ok(()) + } + #[cfg(target_arch = "x86_64")] + /// + /// X86 specific call to retrieve the CPUID registers. + /// + fn get_cpuid2(&self, num_entries: usize) -> cpu::Result { + Ok(self.cpuid.clone()) + } + #[cfg(target_arch = "x86_64")] + /// + /// Returns the state of the LAPIC (Local Advanced Programmable Interrupt Controller). + /// + fn get_lapic(&self) -> cpu::Result { + self.fd + .get_lapic() + .map_err(|e| cpu::HypervisorCpuError::GetlapicState(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// Sets the state of the LAPIC (Local Advanced Programmable Interrupt Controller). + /// + fn set_lapic(&self, lapic: &LapicState) -> cpu::Result<()> { + self.fd + .set_lapic(lapic) + .map_err(|e| cpu::HypervisorCpuError::SetLapicState(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// X86 specific call that returns the vcpu's current "xsave struct". + /// + fn get_xsave(&self) -> cpu::Result { + self.fd + .get_xsave() + .map_err(|e| cpu::HypervisorCpuError::GetXsaveState(e.into())) + } + #[cfg(target_arch = "x86_64")] + /// + /// X86 specific call that sets the vcpu's current "xsave struct". + /// + fn set_xsave(&self, xsave: &Xsave) -> cpu::Result<()> { + self.fd + .set_xsave(*xsave) + .map_err(|e| cpu::HypervisorCpuError::SetXsaveState(e.into())) + } + fn set_state(&self, state: &CpuState) -> cpu::Result<()> { + Ok(()) + } + fn state(&self) -> cpu::Result { + unimplemented!(); + } +} +/// Wrapper over Mshv VM ioctls. +pub struct MshvVm { + fd: Arc, + msrs: MsrEntries, +} +/// +/// Implementation of Vm trait for Mshv +/// Example: +/// #[cfg(feature = "mshv")] +/// # extern crate hypervisor; +/// # use hypervisor::MshvHypervisor; +/// let mshv = MshvHypervisor::new().unwrap(); +/// let hypervisor: Arc = Arc::new(mshv); +/// let vm = hypervisor.create_vm().expect("new VM fd creation failed"); +/// vm.set/get().unwrap() +/// +impl vm::Vm for MshvVm { + #[cfg(target_arch = "x86_64")] + /// + /// Sets the address of the three-page region in the VM's address space. + /// + fn set_tss_address(&self, offset: usize) -> vm::Result<()> { + Ok(()) + } + /// + /// Creates an in-kernel interrupt controller. + /// + fn create_irq_chip(&self) -> vm::Result<()> { + Ok(()) + } + /// + /// Registers an event that will, when signaled, trigger the `gsi` IRQ. + /// + fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> { + Ok(()) + } + /// + /// Unregisters an event that will, when signaled, trigger the `gsi` IRQ. + /// + fn unregister_irqfd(&self, _fd: &EventFd, gsi: u32) -> vm::Result<()> { + Ok(()) + } + /// + /// Creates a VcpuFd object from a vcpu RawFd. + /// + fn create_vcpu( + &self, + id: u8, + vmmops: Option>>, + ) -> vm::Result> { + let vcpu_fd = self + .fd + .create_vcpu(id) + .map_err(|e| vm::HypervisorVmError::CreateVcpu(e.into()))?; + let vcpu = MshvVcpu { + fd: vcpu_fd, + vp_index: id, + cpuid: CpuId::new(1 as usize), + msrs: self.msrs.clone(), + }; + Ok(Arc::new(vcpu)) + } + #[cfg(target_arch = "x86_64")] + fn enable_split_irq(&self) -> vm::Result<()> { + Ok(()) + } + fn register_ioevent( + &self, + fd: &EventFd, + addr: &IoEventAddress, + datamatch: Option, + ) -> vm::Result<()> { + Ok(()) + } + /// Unregister an event from a certain address it has been previously registered to. + fn unregister_ioevent(&self, fd: &EventFd, addr: &IoEventAddress) -> vm::Result<()> { + Ok(()) + } + + /// Creates/modifies a guest physical memory slot. + fn set_user_memory_region(&self, user_memory_region: MemoryRegion) -> vm::Result<()> { + self.fd + .map_user_memory(user_memory_region) + .map_err(|e| vm::HypervisorVmError::SetUserMemory(e.into()))?; + Ok(()) + } + + fn make_user_memory_region( + &self, + _slot: u32, + guest_phys_addr: u64, + memory_size: u64, + userspace_addr: u64, + readonly: bool, + log_dirty_pages: bool, + ) -> MemoryRegion { + let mut flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_EXECUTABLE; + if !readonly { + flags |= HV_MAP_GPA_WRITABLE; + } + + mshv_user_mem_region { + flags, + guest_pfn: guest_phys_addr >> PAGE_SHIFT, + size: memory_size, + userspace_addr: userspace_addr as u64, + } + } + + fn create_passthrough_device(&self) -> vm::Result> { + Err(vm::HypervisorVmError::CreatePassthroughDevice(anyhow!( + "No passthrough support" + ))) + } + + fn set_gsi_routing(&self, irq_routing: &[IrqRoutingEntry]) -> vm::Result<()> { + Ok(()) + } + /// + /// Get the Vm state. Return VM specific data + /// + fn state(&self) -> vm::Result { + unimplemented!(); + } + /// + /// Set the VM state + /// + fn set_state(&self, state: VmState) -> vm::Result<()> { + Ok(()) + } + /// + /// Get dirty pages bitmap (one bit per page) + /// + fn get_dirty_log(&self, slot: u32, memory_size: u64) -> vm::Result> { + Err(vm::HypervisorVmError::GetDirtyLog(anyhow!( + "get_dirty_log not implemented" + ))) + } +} +pub use hv_cpuid_entry as CpuIdEntry; #[derive(Copy, Clone, Debug)] pub struct MshvIrqRoutingMsi { @@ -31,3 +486,5 @@ pub struct MshvIrqRoutingEntry { pub route: MshvIrqRouting, } pub type IrqRoutingEntry = MshvIrqRoutingEntry; + +pub const CPUID_FLAG_VALID_INDEX: u32 = 0; diff --git a/hypervisor/src/vm.rs b/hypervisor/src/vm.rs index 3e8e818a9..9dd582ef4 100644 --- a/hypervisor/src/vm.rs +++ b/hypervisor/src/vm.rs @@ -15,8 +15,12 @@ use crate::device::Device; #[cfg(all(feature = "kvm", target_arch = "x86_64"))] use crate::ClockData; #[cfg(feature = "kvm")] +use crate::CreateDevice; +#[cfg(feature = "mshv")] +use crate::HvState as VmState; +#[cfg(feature = "kvm")] use crate::KvmVmState as VmState; -use crate::{CreateDevice, IoEventAddress, IrqRoutingEntry, MemoryRegion}; +use crate::{IoEventAddress, IrqRoutingEntry, MemoryRegion}; #[cfg(feature = "kvm")] use kvm_ioctls::Cap; use std::sync::Arc; @@ -26,6 +30,7 @@ use vmm_sys_util::eventfd::EventFd; /// /// I/O events data matches (32 or 64 bits). /// +#[derive(Debug)] pub enum DataMatch { DataMatch32(u32), DataMatch64(u64), @@ -119,7 +124,6 @@ pub enum HypervisorVmError { /// #[error("Failed to create passthrough device: {0}")] CreatePassthroughDevice(#[source] anyhow::Error), - /// /// Write to Guest memory /// #[error("Failed to write to guest memory: {0}")]