hypervisor: Trait changes for VmX86_64

There are 3 new methods added to the trait:
- `handle_cpuid`
- `get_tsc_offset`
- `set_tsc_offset`

These methods allow handling of cpu exit for reading CPUID which is
required for Hyper-V support. The other 2 methods related to TSC are
required for special Hyper-V handling.

BUG=b:213150327
TEST=Compiled crosvm

Change-Id: Ibc95163d9625883521a56ec9a1573725d0f41711
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3630709
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Vaibhav Nagarnaik <vnagarnaik@google.com>
This commit is contained in:
Vaibhav Nagarnaik 2022-05-05 18:42:43 +00:00 committed by Chromeos LUCI
parent 0465b61b1e
commit 609872d779
2 changed files with 72 additions and 5 deletions

View file

@ -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<u64> {
// 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 {

View file

@ -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<u64>;
/// 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<u64> {
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(&regs)
}
/// 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