From 1d6967437edc98716e545b82a28c788febfbe79a Mon Sep 17 00:00:00 2001 From: Steven Richman Date: Wed, 13 May 2020 17:22:33 -0700 Subject: hypervisor: add get/set_pvclock The clock functions on the Vm trait are for any arch, to support hypervisors that might have ARM pv clocks. The KVM implementation (x86 only) is mostly the same as before, but uses a hypervisor-agnostic ClockState struct instead of the KVM struct. BUG=chromium:1077058 TEST=cargo test -p hypervisor Change-Id: I0e77ae997d6a30851d28aeb5f73c9ef8ebc464a1 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2202742 Reviewed-by: Daniel Verkamp Tested-by: kokoro Commit-Queue: Steven Richman --- hypervisor/src/kvm/aarch64.rs | 18 ++++++- hypervisor/src/kvm/mod.rs | 12 ++++- hypervisor/src/kvm/x86_64.rs | 116 +++++++++++++++++++++++++++++++----------- hypervisor/src/lib.rs | 18 +++++++ 4 files changed, 132 insertions(+), 32 deletions(-) diff --git a/hypervisor/src/kvm/aarch64.rs b/hypervisor/src/kvm/aarch64.rs index 4f0398f..4e5b65c 100644 --- a/hypervisor/src/kvm/aarch64.rs +++ b/hypervisor/src/kvm/aarch64.rs @@ -2,10 +2,24 @@ // 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 libc::ENXIO; + +use sys_util::{Error, Result}; use super::{KvmVcpu, KvmVm}; -use crate::{VcpuAArch64, VmAArch64}; +use crate::{ClockState, VcpuAArch64, VmAArch64}; + +impl KvmVm { + /// Arch-specific implementation of `Vm::get_pvclock`. Always returns an error on AArch64. + pub fn get_pvclock_arch(&self) -> Result { + Err(Error::new(ENXIO)) + } + + /// Arch-specific implementation of `Vm::set_pvclock`. Always returns an error on AArch64. + pub fn set_pvclock_arch(&self, _state: &ClockState) -> Result<()> { + Err(Error::new(ENXIO)) + } +} impl VmAArch64 for KvmVm { type Vcpu = KvmVcpu; diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 0550792..5fdf124 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -24,7 +24,9 @@ use sys_util::{ GuestMemory, RawDescriptor, Result, SafeDescriptor, }; -use crate::{Hypervisor, HypervisorCap, MappedRegion, RunnableVcpu, Vcpu, VcpuExit, Vm}; +use crate::{ + ClockState, Hypervisor, HypervisorCap, MappedRegion, RunnableVcpu, Vcpu, VcpuExit, Vm, +}; // Wrapper around KVM_SET_USER_MEMORY_REGION ioctl, which creates, modifies, or deletes a mapping // from guest physical to host user pages. @@ -184,6 +186,14 @@ impl Vm for KvmVm { fn get_memory(&self) -> &GuestMemory { &self.guest_mem } + + fn get_pvclock(&self) -> Result { + self.get_pvclock_arch() + } + + fn set_pvclock(&self, state: &ClockState) -> Result<()> { + self.set_pvclock_arch(state) + } } impl AsRawDescriptor for KvmVm { diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs index 06774f4..f3fbc59 100644 --- a/hypervisor/src/kvm/x86_64.rs +++ b/hypervisor/src/kvm/x86_64.rs @@ -4,14 +4,17 @@ use std::convert::TryInto; -use kvm_sys::*; use libc::E2BIG; -use sys_util::{ioctl_with_mut_ptr, Error, Result}; + +use kvm_sys::*; +use sys_util::{ + errno_result, ioctl_with_mut_ptr, ioctl_with_mut_ref, ioctl_with_ref, Error, Result, +}; use super::{Kvm, KvmVcpu, KvmVm}; use crate::{ - CpuId, CpuIdEntry, HypervisorX86_64, IoapicRedirectionTableEntry, IoapicState, LapicState, - PicState, PitChannelState, PitState, Regs, VcpuX86_64, VmX86_64, + ClockState, CpuId, CpuIdEntry, HypervisorX86_64, IoapicRedirectionTableEntry, IoapicState, + LapicState, PicState, PitChannelState, PitState, Regs, VcpuX86_64, VmX86_64, }; type KvmCpuId = kvm::CpuId; @@ -54,6 +57,58 @@ impl Kvm { } } +impl HypervisorX86_64 for Kvm { + fn get_supported_cpuid(&self) -> Result { + self.get_cpuid(KVM_GET_SUPPORTED_CPUID()) + } + + fn get_emulated_cpuid(&self) -> Result { + self.get_cpuid(KVM_GET_EMULATED_CPUID()) + } +} + +impl KvmVm { + /// Arch-specific implementation of `Vm::get_pvclock`. + pub fn get_pvclock_arch(&self) -> Result { + // Safe because we know that our file is a VM fd, we know the kernel will only write correct + // amount of memory to our pointer, and we verify the return result. + let mut clock_data: kvm_clock_data = unsafe { std::mem::zeroed() }; + let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_CLOCK(), &mut clock_data) }; + if ret == 0 { + Ok(ClockState::from(clock_data)) + } else { + errno_result() + } + } + + /// Arch-specific implementation of `Vm::set_pvclock`. + pub fn set_pvclock_arch(&self, state: &ClockState) -> Result<()> { + let clock_data = kvm_clock_data::from(*state); + // Safe because we know that our file is a VM fd, we know the kernel will only read correct + // amount of memory from our pointer, and we verify the return result. + let ret = unsafe { ioctl_with_ref(self, KVM_SET_CLOCK(), &clock_data) }; + if ret == 0 { + Ok(()) + } else { + errno_result() + } + } +} + +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 {}) + } +} + impl<'a> From<&'a KvmCpuId> for CpuId { fn from(kvm_cpuid: &'a KvmCpuId) -> CpuId { let kvm_entries = kvm_cpuid.entries_slice(); @@ -74,27 +129,22 @@ impl<'a> From<&'a KvmCpuId> for CpuId { } } -impl HypervisorX86_64 for Kvm { - fn get_supported_cpuid(&self) -> Result { - self.get_cpuid(KVM_GET_SUPPORTED_CPUID()) - } - - fn get_emulated_cpuid(&self) -> Result { - self.get_cpuid(KVM_GET_EMULATED_CPUID()) - } -} - -impl VmX86_64 for KvmVm { - type Vcpu = KvmVcpu; - - fn create_vcpu(&self, id: usize) -> Result { - self.create_kvm_vcpu(id) +impl From for kvm_clock_data { + fn from(state: ClockState) -> Self { + kvm_clock_data { + clock: state.clock, + flags: state.flags, + ..Default::default() + } } } -impl VcpuX86_64 for KvmVcpu { - fn get_regs(&self) -> Result { - Ok(Regs {}) +impl From for ClockState { + fn from(clock_data: kvm_clock_data) -> Self { + ClockState { + clock: clock_data.clock, + flags: clock_data.flags, + } } } @@ -305,15 +355,13 @@ impl From<&kvm_pit_channel_state> for PitChannelState { #[cfg(test)] mod tests { + use super::*; use crate::{ - DeliveryMode, DeliveryStatus, DestinationMode, IoapicRedirectionTableEntry, IoapicState, - LapicState, PicInitState, PicState, PitChannelState, PitRWMode, PitRWState, PitState, - TriggerMode, + DeliveryMode, DeliveryStatus, DestinationMode, HypervisorX86_64, + IoapicRedirectionTableEntry, IoapicState, LapicState, PicInitState, PicState, + PitChannelState, PitRWMode, PitRWState, PitState, TriggerMode, Vm, }; - use kvm_sys::*; - - use super::Kvm; - use crate::HypervisorX86_64; + use sys_util::{GuestAddress, GuestMemory}; #[test] fn get_supported_cpuid() { @@ -501,4 +549,14 @@ mod tests { // convert back and compare assert_eq!(state, PitState::from(&kvm_state)); } + + #[test] + fn clock_handling() { + let kvm = Kvm::new().unwrap(); + let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap(); + let vm = KvmVm::new(&kvm, gm).unwrap(); + let mut clock_data = vm.get_pvclock().unwrap(); + clock_data.clock += 1000; + vm.set_pvclock(&clock_data).unwrap(); + } } diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 784af8c..ee0bcaf 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -33,6 +33,14 @@ pub trait Vm: Send + Sized { /// Gets the guest-mapped memory for the Vm. fn get_memory(&self) -> &GuestMemory; + + /// Retrieves the current timestamp of the paravirtual clock as seen by the current guest. + /// Only works on VMs that support `VmCap::PvClock`. + fn get_pvclock(&self) -> Result; + + /// Sets the current timestamp of the paravirtual clock as seen by the current guest. + /// Only works on VMs that support `VmCap::PvClock`. + fn set_pvclock(&self, state: &ClockState) -> Result<()>; } /// A wrapper around using a VCPU. @@ -83,4 +91,14 @@ pub enum VcpuExit { Unknown, } +/// A single route for an IRQ. pub struct IrqRoute {} + +/// The state of the paravirtual clock +#[derive(Debug, Default, Copy, Clone)] +pub struct ClockState { + /// Current pv clock timestamp, as seen by the guest + pub clock: u64, + /// Hypervisor-specific feature flags for the pv clock + pub flags: u32, +} -- cgit 1.4.1