mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-12-24 22:55:19 +00:00
arch, hypervisor: Populate CPUID leaf 0x4000_0010 (TSC frequency)
This hypervisor leaf includes details of the TSC frequency if that is available from KVM. This can be used to efficiently calculate time passed when there is an invariant TSC. TEST=Run `cpuid` in the guest and observe the frequency populated. Fixes: #5178 Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
parent
3b0d2e796b
commit
39a81c596f
@ -16,7 +16,7 @@ use crate::GuestMemoryMmap;
|
||||
use crate::InitramfsConfig;
|
||||
use crate::RegionType;
|
||||
use hypervisor::arch::x86::{CpuIdEntry, CPUID_FLAG_VALID_INDEX};
|
||||
use hypervisor::HypervisorError;
|
||||
use hypervisor::{HypervisorCpuError, HypervisorError};
|
||||
use linux_loader::loader::bootparam::boot_params;
|
||||
use linux_loader::loader::elf::start_info::{
|
||||
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
|
||||
@ -36,6 +36,7 @@ pub mod tdx;
|
||||
const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit.
|
||||
const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit.
|
||||
const MTRR_EDX_BIT: u8 = 12; // Hypervisor ecx bit.
|
||||
const INVARIANT_TSC_EDX_BIT: u8 = 8; // Invariant TSC bit on 0x8000_0007 EDX
|
||||
|
||||
// KVM feature bits
|
||||
const KVM_FEATURE_ASYNC_PF_INT_BIT: u8 = 14;
|
||||
@ -191,6 +192,9 @@ pub enum Error {
|
||||
// Error writing EBDA address
|
||||
EbdaSetup(vm_memory::GuestMemoryError),
|
||||
|
||||
// Error getting CPU TSC frequency
|
||||
GetTscFrequency(HypervisorCpuError),
|
||||
|
||||
/// Error retrieving TDX capabilities through the hypervisor (kvm/mshv) API
|
||||
#[cfg(feature = "tdx")]
|
||||
TdxCapabilities(HypervisorError),
|
||||
@ -749,6 +753,34 @@ pub fn configure_vcpu(
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id));
|
||||
|
||||
// The TSC frequency CPUID leaf should not be included when running with HyperV emulation
|
||||
if !kvm_hyperv {
|
||||
if let Some(tsc_khz) = vcpu.tsc_khz().map_err(Error::GetTscFrequency)? {
|
||||
// Need to check that the TSC doesn't vary with dynamic frequency
|
||||
// SAFETY: cpuid called with valid leaves
|
||||
if unsafe { std::arch::x86_64::__cpuid(0x8000_0007) }.edx
|
||||
& (1u32 << INVARIANT_TSC_EDX_BIT)
|
||||
> 0
|
||||
{
|
||||
CpuidPatch::set_cpuid_reg(
|
||||
&mut cpuid,
|
||||
0x4000_0000,
|
||||
None,
|
||||
CpuidReg::EAX,
|
||||
0x4000_0010,
|
||||
);
|
||||
cpuid.retain(|c| c.function != 0x4000_0010);
|
||||
cpuid.push(CpuIdEntry {
|
||||
function: 0x4000_0010,
|
||||
eax: tsc_khz,
|
||||
ebx: 1000000, /* LAPIC resolution of 1ns (freq: 1GHz) is hardcoded in KVM's
|
||||
* APIC_BUS_CYCLE_NS */
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
vcpu.set_cpuid2(&cpuid)
|
||||
.map_err(|e| Error::SetSupportedCpusFailed(e.into()))?;
|
||||
|
||||
|
@ -237,6 +237,12 @@ pub enum HypervisorCpuError {
|
||||
///
|
||||
#[error("Failed to initialize PMU")]
|
||||
InitializePmu,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Error getting TSC frequency
|
||||
///
|
||||
#[error("Failed to get TSC frequency: {0}")]
|
||||
GetTscKhz(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -432,4 +438,11 @@ pub trait Vcpu: Send + Sync {
|
||||
/// Return the list of initial MSR entries for a VCPU
|
||||
///
|
||||
fn boot_msr_entries(&self) -> Vec<MsrEntry>;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Get the frequency of the TSC if available
|
||||
///
|
||||
fn tsc_khz(&self) -> Result<Option<u32>> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
@ -2152,6 +2152,23 @@ impl cpu::Vcpu for KvmVcpu {
|
||||
.set_device_attr(&cpu_attr)
|
||||
.map_err(|_| cpu::HypervisorCpuError::InitializePmu)
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
///
|
||||
/// Get the frequency of the TSC if available
|
||||
///
|
||||
fn tsc_khz(&self) -> cpu::Result<Option<u32>> {
|
||||
match self.fd.get_tsc_khz() {
|
||||
Err(e) => {
|
||||
if e.errno() == libc::EIO {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(cpu::HypervisorCpuError::GetTscKhz(e.into()))
|
||||
}
|
||||
}
|
||||
Ok(v) => Ok(Some(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KvmVcpu {
|
||||
|
@ -50,6 +50,9 @@ pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> {
|
||||
if !kvm.check_extension(Cap::ImmediateExit) {
|
||||
return Err(KvmError::CapabilityMissing(Cap::ImmediateExit));
|
||||
}
|
||||
if !kvm.check_extension(Cap::GetTscKhz) {
|
||||
return Err(KvmError::CapabilityMissing(Cap::GetTscKhz));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
|
@ -341,6 +341,7 @@ fn create_vmm_ioctl_seccomp_rule_kvm() -> Result<Vec<SeccompRule>, BackendError>
|
||||
const KVM_GET_MSR_INDEX_LIST: u64 = 0xc004_ae02;
|
||||
const KVM_GET_MSRS: u64 = 0xc008_ae88;
|
||||
const KVM_GET_SREGS: u64 = 0x8138_ae83;
|
||||
const KVM_GET_TSC_KHZ: u64 = 0xaea3;
|
||||
const KVM_GET_XCRS: u64 = 0x8188_aea6;
|
||||
const KVM_GET_XSAVE: u64 = 0x9000_aea4;
|
||||
const KVM_KVMCLOCK_CTRL: u64 = 0xaead;
|
||||
@ -367,6 +368,7 @@ fn create_vmm_ioctl_seccomp_rule_kvm() -> Result<Vec<SeccompRule>, BackendError>
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_MSR_INDEX_LIST)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_MSRS)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_SREGS)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_TSC_KHZ)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_XCRS,)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_XSAVE,)?],
|
||||
and![Cond::new(1, ArgLen::Dword, Eq, KVM_KVMCLOCK_CTRL)?],
|
||||
|
Loading…
Reference in New Issue
Block a user