summary refs log tree commit diff
diff options
context:
space:
mode:
authorSteven Richman <srichman@google.com>2020-05-13 17:22:33 -0700
committerCommit Bot <commit-bot@chromium.org>2020-06-03 12:32:41 +0000
commit1d6967437edc98716e545b82a28c788febfbe79a (patch)
tree45a1fd559229912614a1082295171bde0788bfa2
parentee215426f4e61474aea546b5c0b6c1f1040d5344 (diff)
downloadcrosvm-1d6967437edc98716e545b82a28c788febfbe79a.tar
crosvm-1d6967437edc98716e545b82a28c788febfbe79a.tar.gz
crosvm-1d6967437edc98716e545b82a28c788febfbe79a.tar.bz2
crosvm-1d6967437edc98716e545b82a28c788febfbe79a.tar.lz
crosvm-1d6967437edc98716e545b82a28c788febfbe79a.tar.xz
crosvm-1d6967437edc98716e545b82a28c788febfbe79a.tar.zst
crosvm-1d6967437edc98716e545b82a28c788febfbe79a.zip
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 <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Steven Richman <srichman@google.com>
-rw-r--r--hypervisor/src/kvm/aarch64.rs18
-rw-r--r--hypervisor/src/kvm/mod.rs12
-rw-r--r--hypervisor/src/kvm/x86_64.rs116
-rw-r--r--hypervisor/src/lib.rs18
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<ClockState> {
+        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<ClockState> {
+        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<CpuId> {
+        self.get_cpuid(KVM_GET_SUPPORTED_CPUID())
+    }
+
+    fn get_emulated_cpuid(&self) -> Result<CpuId> {
+        self.get_cpuid(KVM_GET_EMULATED_CPUID())
+    }
+}
+
+impl KvmVm {
+    /// Arch-specific implementation of `Vm::get_pvclock`.
+    pub fn get_pvclock_arch(&self) -> Result<ClockState> {
+        // 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::Vcpu> {
+        self.create_kvm_vcpu(id)
+    }
+}
+
+impl VcpuX86_64 for KvmVcpu {
+    fn get_regs(&self) -> Result<Regs> {
+        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<CpuId> {
-        self.get_cpuid(KVM_GET_SUPPORTED_CPUID())
-    }
-
-    fn get_emulated_cpuid(&self) -> Result<CpuId> {
-        self.get_cpuid(KVM_GET_EMULATED_CPUID())
-    }
-}
-
-impl VmX86_64 for KvmVm {
-    type Vcpu = KvmVcpu;
-
-    fn create_vcpu(&self, id: usize) -> Result<Self::Vcpu> {
-        self.create_kvm_vcpu(id)
+impl From<ClockState> 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<Regs> {
-        Ok(Regs {})
+impl From<kvm_clock_data> 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<ClockState>;
+
+    /// 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,
+}