mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-11 12:35:26 +00:00
The registers for cpuid got refactored into a new `cpuid` member. Update the haxm tests to work with the new structure. TEST=Ran `tools/run_tests --target=host --arch=win64` by temporarily TEST=enabling `haxm` in `Cargo.toml`. Change-Id: I6a70be72fc49015ed2301b422a18b88b374e17c1 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3737458 Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Commit-Queue: Vaibhav Nagarnaik <vnagarnaik@google.com>
356 lines
11 KiB
Rust
356 lines
11 KiB
Rust
// Copyright 2020 The Chromium OS Authors. All rights reserved.
|
|
// 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::{CpuidResult, __cpuid};
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use base::{AsRawDescriptor, RawDescriptor, Result, SafeDescriptor};
|
|
|
|
use crate::{CpuId, CpuIdEntry, Hypervisor, HypervisorCap, HypervisorX86_64};
|
|
|
|
mod haxm_sys;
|
|
use haxm_sys::*;
|
|
// This is a HAXM-specific capability, so it's not present in the VmCap enum, and the
|
|
// register_vm_log function does not exist on the Vm trait. But windows.rs will use it when
|
|
// creating Haxm Vm instances, so we expose the cap constant here.
|
|
pub use haxm_sys::HAX_CAP_VM_LOG;
|
|
|
|
mod vcpu;
|
|
pub use vcpu::*;
|
|
mod vm;
|
|
pub use vm::*;
|
|
|
|
#[cfg(windows)]
|
|
mod win;
|
|
#[cfg(windows)]
|
|
use win::*;
|
|
|
|
#[cfg(unix)]
|
|
mod unix;
|
|
#[cfg(unix)]
|
|
use unix::*;
|
|
|
|
static USE_GHAXM: AtomicBool = AtomicBool::new(true);
|
|
|
|
/// Returns whether ghaxm variant is in use vs. the upstream haxm variant.
|
|
pub fn get_use_ghaxm() -> bool {
|
|
USE_GHAXM.load(Ordering::Relaxed)
|
|
}
|
|
|
|
/// Sets whether to use ghaxm variant vs. the upstream haxm variant.
|
|
pub fn set_use_ghaxm(use_ghaxm: bool) {
|
|
USE_GHAXM.store(use_ghaxm, Ordering::Relaxed);
|
|
}
|
|
|
|
pub struct Haxm {
|
|
haxm: SafeDescriptor,
|
|
}
|
|
|
|
impl AsRawDescriptor for Haxm {
|
|
fn as_raw_descriptor(&self) -> RawDescriptor {
|
|
self.haxm.as_raw_descriptor()
|
|
}
|
|
}
|
|
|
|
impl Haxm {
|
|
/// Opens HAXM device and returns a Haxm object on success.
|
|
pub fn new() -> Result<Haxm> {
|
|
Ok(Haxm {
|
|
haxm: open_haxm_device(get_use_ghaxm())?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Hypervisor for Haxm {
|
|
fn check_capability(&self, cap: HypervisorCap) -> bool {
|
|
match cap {
|
|
HypervisorCap::UserMemory => true,
|
|
// under haxm, guests rely on this leaf to calibrate their
|
|
// clocksource.
|
|
HypervisorCap::CalibratedTscLeafRequired => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Makes a shallow clone of this `Hypervisor`.
|
|
fn try_clone(&self) -> Result<Self> {
|
|
Ok(Haxm {
|
|
haxm: self.haxm.try_clone()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl HypervisorX86_64 for Haxm {
|
|
fn get_supported_cpuid(&self) -> Result<CpuId> {
|
|
// Start with cpuids that HAXM supports from
|
|
// https://github.com/intel/haxm/blob/v7.6.1/core/cpuid.c#L170
|
|
let mut supported_features_1_ecx = (Feature1Ecx::SSE3
|
|
| Feature1Ecx::SSSE3
|
|
| Feature1Ecx::PCID
|
|
| Feature1Ecx::SSE41
|
|
| Feature1Ecx::SSE42
|
|
| Feature1Ecx::CMPXCHG16B
|
|
| Feature1Ecx::MOVBE
|
|
| Feature1Ecx::AESNI
|
|
| Feature1Ecx::PCLMULQDQ
|
|
| Feature1Ecx::POPCNT
|
|
| Feature1Ecx::FMA
|
|
| Feature1Ecx::XSAVE
|
|
| Feature1Ecx::OSXSAVE
|
|
| Feature1Ecx::AVX
|
|
| Feature1Ecx::F16C
|
|
| Feature1Ecx::RDRAND)
|
|
.bits();
|
|
let mut supported_features_1_edx = (Feature1Edx::PAT
|
|
| Feature1Edx::FPU
|
|
| Feature1Edx::VME
|
|
| Feature1Edx::DE
|
|
| Feature1Edx::TSC
|
|
| Feature1Edx::MSR
|
|
| Feature1Edx::PAE
|
|
| Feature1Edx::MCE
|
|
| Feature1Edx::CX8
|
|
| Feature1Edx::APIC
|
|
| Feature1Edx::SEP
|
|
| Feature1Edx::MTRR
|
|
| Feature1Edx::PGE
|
|
| Feature1Edx::MCA
|
|
| Feature1Edx::CMOV
|
|
| Feature1Edx::CLFSH
|
|
| Feature1Edx::MMX
|
|
| Feature1Edx::FXSR
|
|
| Feature1Edx::SSE
|
|
| Feature1Edx::SSE2
|
|
| Feature1Edx::SS
|
|
| Feature1Edx::PSE
|
|
| Feature1Edx::HTT)
|
|
.bits();
|
|
|
|
let mut supported_features_80000001_edx = (Feature80000001Edx::NX
|
|
| Feature80000001Edx::SYSCALL
|
|
| Feature80000001Edx::RDTSCP
|
|
| Feature80000001Edx::EM64T)
|
|
.bits();
|
|
|
|
let mut supported_features_80000001_ecx =
|
|
(Feature80000001Ecx::LAHF | Feature80000001Ecx::ABM | Feature80000001Ecx::PREFETCHW)
|
|
.bits();
|
|
|
|
let result = unsafe { __cpuid(0x1) };
|
|
|
|
// Filter HAXM supported cpuids by host-supported cpuids
|
|
supported_features_1_ecx &= result.ecx;
|
|
supported_features_1_edx &= result.edx;
|
|
|
|
let result = unsafe { __cpuid(0x80000001) };
|
|
|
|
supported_features_80000001_edx &= result.edx;
|
|
supported_features_80000001_ecx &= result.ecx;
|
|
|
|
let cpuid_7 = unsafe { __cpuid(0x7) };
|
|
let cpuid_15 = unsafe { __cpuid(0x15) };
|
|
let cpuid_16 = unsafe { __cpuid(0x16) };
|
|
|
|
Ok(CpuId {
|
|
cpu_id_entries: vec![
|
|
CpuIdEntry {
|
|
function: 0x1,
|
|
index: 0,
|
|
flags: 0,
|
|
cpuid: CpuidResult {
|
|
eax: 0,
|
|
ebx: 0,
|
|
ecx: supported_features_1_ecx,
|
|
edx: supported_features_1_edx,
|
|
},
|
|
},
|
|
CpuIdEntry {
|
|
function: 0x7,
|
|
index: 0,
|
|
flags: 0,
|
|
cpuid: CpuidResult {
|
|
eax: cpuid_7.eax,
|
|
ebx: cpuid_7.ebx,
|
|
ecx: cpuid_7.ecx,
|
|
edx: cpuid_7.edx,
|
|
},
|
|
},
|
|
CpuIdEntry {
|
|
function: 0x15,
|
|
index: 0,
|
|
flags: 0,
|
|
cpuid: CpuidResult {
|
|
eax: cpuid_15.eax,
|
|
ebx: cpuid_15.ebx,
|
|
ecx: cpuid_15.ecx,
|
|
edx: cpuid_15.edx,
|
|
},
|
|
},
|
|
CpuIdEntry {
|
|
function: 0x16,
|
|
index: 0,
|
|
flags: 0,
|
|
cpuid: CpuidResult {
|
|
eax: cpuid_16.eax,
|
|
ebx: cpuid_16.ebx,
|
|
ecx: cpuid_16.ecx,
|
|
edx: cpuid_16.edx,
|
|
},
|
|
},
|
|
CpuIdEntry {
|
|
function: 0x80000001,
|
|
index: 0,
|
|
flags: 0,
|
|
cpuid: CpuidResult {
|
|
eax: 0,
|
|
ebx: 0,
|
|
ecx: supported_features_80000001_ecx,
|
|
edx: supported_features_80000001_edx,
|
|
},
|
|
},
|
|
],
|
|
})
|
|
}
|
|
|
|
fn get_emulated_cpuid(&self) -> Result<CpuId> {
|
|
// HAXM does not emulate any cpuids that the host does not support
|
|
Ok(CpuId::new(0))
|
|
}
|
|
|
|
/// Gets the list of supported MSRs.
|
|
fn get_msr_index_list(&self) -> Result<Vec<u32>> {
|
|
// HAXM supported MSRs come from
|
|
// https://github.com/intel/haxm/blob/v7.6.1/core/vcpu.c#L3296
|
|
let mut msrs = vec![
|
|
IA32_TSC,
|
|
IA32_FEATURE_CONTROL,
|
|
IA32_PLATFORM_ID,
|
|
IA32_APIC_BASE,
|
|
IA32_EFER,
|
|
IA32_STAR,
|
|
IA32_LSTAR,
|
|
IA32_CSTAR,
|
|
IA32_SF_MASK,
|
|
IA32_KERNEL_GS_BASE,
|
|
IA32_TSC_AUX,
|
|
IA32_FS_BASE,
|
|
IA32_GS_BASE,
|
|
IA32_SYSENTER_CS,
|
|
IA32_SYSENTER_ESP,
|
|
IA32_SYSENTER_EIP,
|
|
IA32_MTRRCAP,
|
|
MTRRFIX64K_00000,
|
|
IA32_CR_PAT,
|
|
IA32_MTRR_DEF_TYPE,
|
|
IA32_MCG_CAP,
|
|
IA32_MCG_STATUS,
|
|
IA32_MCG_CTL,
|
|
IA32_MC0_CTL,
|
|
IA32_P5_MC_TYPE,
|
|
IA32_MC0_STATUS,
|
|
IA32_P5_MC_ADDR,
|
|
IA32_MC0_ADDR,
|
|
IA32_MC0_MISC,
|
|
IA32_THERM_DIODE_OFFSET,
|
|
IA32_FSB_FREQ,
|
|
IA32_TEMP_TARGET,
|
|
IA32_BBL_CR_CTL3,
|
|
IA32_DEBUGCTL,
|
|
IA32_CPUID_FEATURE_MASK,
|
|
IA32_EBC_FREQUENCY_ID,
|
|
IA32_EBC_HARD_POWERON,
|
|
IA32_EBC_SOFT_POWERON,
|
|
IA32_BIOS_SIGN_ID,
|
|
IA32_MISC_ENABLE,
|
|
IA32_PERF_CAPABILITIES,
|
|
IA32_PMC0,
|
|
IA32_PMC1,
|
|
IA32_PMC2,
|
|
IA32_PMC3,
|
|
IA32_PERFEVTSEL0,
|
|
IA32_PERFEVTSEL1,
|
|
IA32_PERFEVTSEL2,
|
|
IA32_PERFEVTSEL3,
|
|
];
|
|
msrs.extend(IA32_MTRR_PHYSBASE0..IA32_MTRR_PHYSMASK9);
|
|
msrs.extend(MTRRFIX16K_80000..MTRRFIX16K_A0000);
|
|
msrs.extend(MTRRFIX4K_C0000..MTRRFIX4K_F8000);
|
|
msrs.extend(IA32_MC0_CTL2..IA32_MC8_CTL2);
|
|
Ok(msrs)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn new_haxm() {
|
|
Haxm::new().expect("failed to instantiate HAXM");
|
|
}
|
|
|
|
#[test]
|
|
fn check_capability() {
|
|
let haxm = Haxm::new().expect("failed to instantiate HAXM");
|
|
assert!(haxm.check_capability(HypervisorCap::UserMemory));
|
|
assert!(!haxm.check_capability(HypervisorCap::ImmediateExit));
|
|
}
|
|
|
|
#[test]
|
|
fn check_supported_cpuid() {
|
|
let haxm = Haxm::new().expect("failed to instantiate HAXM");
|
|
let cpuid = haxm
|
|
.get_supported_cpuid()
|
|
.expect("failed to get supported cupid");
|
|
|
|
// HAXM returns five leaves: 0x1, 0x7, 0x15, 0x16, and 0x80000001
|
|
assert_eq!(cpuid.cpu_id_entries.len(), 5);
|
|
|
|
// Check some simple cpuids that we know should be enabled
|
|
let entry = cpuid
|
|
.cpu_id_entries
|
|
.iter()
|
|
.find(|entry| entry.function == 0x1)
|
|
.expect("failed to get leaf 0x1");
|
|
|
|
// FPU should definitely exist
|
|
assert_ne!(entry.cpuid.edx & Feature1Edx::FPU.bits(), 0);
|
|
// SSSE3 almost certainly set
|
|
assert_ne!(entry.cpuid.ecx & Feature1Ecx::SSSE3.bits(), 0);
|
|
// VMX should not be set, HAXM does not support nested virt
|
|
assert_eq!(entry.cpuid.ecx & Feature1Ecx::VMX.bits(), 0);
|
|
|
|
// Check that leaf 0x15 is in the results
|
|
cpuid
|
|
.cpu_id_entries
|
|
.iter()
|
|
.find(|entry| entry.function == 0x15)
|
|
.expect("failed to get leaf 0x15");
|
|
|
|
// Check that leaf 0x16 is in the results
|
|
cpuid
|
|
.cpu_id_entries
|
|
.iter()
|
|
.find(|entry| entry.function == 0x16)
|
|
.expect("failed to get leaf 0x16");
|
|
|
|
let entry = cpuid
|
|
.cpu_id_entries
|
|
.iter()
|
|
.find(|entry| entry.function == 0x80000001)
|
|
.expect("failed to get leaf 0x80000001");
|
|
// NX should be set, Windows 8+ and HAXM require it
|
|
assert_ne!(entry.cpuid.edx & Feature80000001Edx::NX.bits(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn check_msr_index_list() {
|
|
let haxm = Haxm::new().expect("failed to instantiate HAXM");
|
|
let msr_index_list = haxm
|
|
.get_msr_index_list()
|
|
.expect("failed to get HAXM msr index list");
|
|
|
|
assert!(msr_index_list.contains(&IA32_TSC));
|
|
}
|
|
}
|