diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs index ac8b0add34..1bba94f28e 100644 --- a/hypervisor/src/kvm/x86_64.rs +++ b/hypervisor/src/kvm/x86_64.rs @@ -6,7 +6,7 @@ use std::arch::x86_64::__cpuid; use base::IoctlNr; -use libc::E2BIG; +use libc::{E2BIG, ENXIO}; use base::{ errno_result, error, ioctl, ioctl_with_mut_ptr, ioctl_with_mut_ref, ioctl_with_ptr, @@ -18,10 +18,11 @@ use vm_memory::GuestAddress; use super::{Kvm, KvmVcpu, KvmVm}; use crate::{ - ClockState, CpuId, CpuIdEntry, DebugRegs, DescriptorTable, DeviceKind, Fpu, HypervisorX86_64, - IoapicRedirectionTableEntry, IoapicState, IrqSourceChip, LapicState, PicSelect, PicState, - PitChannelState, PitState, ProtectionType, Register, Regs, Segment, Sregs, VcpuExit, - VcpuX86_64, VmCap, VmX86_64, MAX_IOAPIC_PINS, NUM_IOAPIC_PINS, + get_tsc_offset_from_msr, set_tsc_offset_via_msr, ClockState, CpuId, CpuIdEntry, DebugRegs, + DescriptorTable, DeviceKind, Fpu, HypervisorX86_64, IoapicRedirectionTableEntry, IoapicState, + IrqSourceChip, LapicState, PicSelect, PicState, PitChannelState, PitState, ProtectionType, + Register, Regs, Segment, Sregs, VcpuExit, VcpuX86_64, VmCap, VmX86_64, MAX_IOAPIC_PINS, + NUM_IOAPIC_PINS, }; type KvmCpuId = kvm::CpuId; @@ -697,6 +698,21 @@ impl VcpuX86_64 for KvmVcpu { errno_result() } } + + /// KVM does not support the VcpuExit::Cpuid exit type. + fn handle_cpuid(&mut self, _entry: &CpuIdEntry) -> Result<()> { + Err(Error::new(ENXIO)) + } + + fn get_tsc_offset(&self) -> Result { + // Use the default MSR-based implementation + get_tsc_offset_from_msr(self) + } + + fn set_tsc_offset(&self, offset: u64) -> Result<()> { + // Use the default MSR-based implementation + set_tsc_offset_via_msr(self, offset) + } } impl KvmVcpu { diff --git a/hypervisor/src/x86_64.rs b/hypervisor/src/x86_64.rs index b48ee9d486..a6b6f902c6 100644 --- a/hypervisor/src/x86_64.rs +++ b/hypervisor/src/x86_64.rs @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use std::arch::x86_64::_rdtsc; + use serde::{Deserialize, Serialize}; use base::{error, Result}; @@ -99,10 +101,59 @@ pub trait VcpuX86_64: Vcpu { /// Sets up debug registers and configure vcpu for handling guest debug events. fn set_guest_debug(&self, addrs: &[GuestAddress], enable_singlestep: bool) -> Result<()>; + + /// This function should be called after `Vcpu::run` returns `VcpuExit::Cpuid`, and `entry` + /// should represent the result of emulating the CPUID instruction. The `handle_cpuid` function + /// will then set the appropriate registers on the vcpu. + fn handle_cpuid(&mut self, entry: &CpuIdEntry) -> Result<()>; + + /// Get the guest->host TSC offset + fn get_tsc_offset(&self) -> Result; + + /// Set the guest->host TSC offset + fn set_tsc_offset(&self, offset: u64) -> Result<()>; } impl_downcast!(VcpuX86_64); +// TSC MSR +pub const MSR_IA32_TSC: u32 = 0x00000010; +/// Implementation of get_tsc_offset that uses VcpuX86_64::get_msrs. +pub(crate) fn get_tsc_offset_from_msr(vcpu: &impl VcpuX86_64) -> Result { + let mut regs = vec![Register { + id: crate::MSR_IA32_TSC, + value: 0, + }]; + + // Safe because _rdtsc takes no arguments + let host_before_tsc = unsafe { _rdtsc() }; + + // get guest TSC value from our hypervisor + vcpu.get_msrs(&mut regs)?; + + // Safe because _rdtsc takes no arguments + let host_after_tsc = unsafe { _rdtsc() }; + + // Average the before and after host tsc to get the best value + let host_tsc = ((host_before_tsc as u128 + host_after_tsc as u128) / 2) as u64; + + Ok(regs[0].value.wrapping_sub(host_tsc)) +} + +/// Implementation of get_tsc_offset that uses VcpuX86_64::get_msrs. +pub(crate) fn set_tsc_offset_via_msr(vcpu: &impl VcpuX86_64, offset: u64) -> Result<()> { + // Safe because _rdtsc takes no arguments + let host_tsc = unsafe { _rdtsc() }; + + let regs = vec![Register { + id: crate::MSR_IA32_TSC, + value: host_tsc.wrapping_add(offset), + }]; + + // set guest TSC value from our hypervisor + vcpu.set_msrs(®s) +} + /// A CpuId Entry contains supported feature information for the given processor. /// This can be modified by the hypervisor to pass additional information to the guest kernel /// about the hypervisor or vm. Information is returned in the eax, ebx, ecx and edx registers