mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-10 04:07:23 +00:00
x86_64: add new option enable-hwp to expose HWP feature to guests
Expose HWP (HW P-State) feature to the guest if enable-hwp is present and implement these dependencies: - enable-hwp depends on host-cpu-topology option - itmt depends on enable-hwp option Group the CPU configuration entries to a new struct CpuConfigArch, to simplify APIs in the vCPU configuration path. BUG=b:199380745 TEST=boot Redrix manatee and verified that intel_pstate driver works Change-Id: Icdd19190f6a7518492ff5fc54708af40288a1422 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3771065 Commit-Queue: Dmitry Torokhov <dtor@chromium.org> Reviewed-by: Dmitry Torokhov <dtor@chromium.org> Tested-by: Dmitry Torokhov <dtor@chromium.org> Reviewed-by: Junichi Uekawa <uekawa@chromium.org>
This commit is contained in:
parent
5eba4ce565
commit
69775f0346
13 changed files with 191 additions and 151 deletions
|
@ -34,6 +34,7 @@ use devices::PciAddress;
|
|||
use devices::PciConfigMmio;
|
||||
use devices::PciDevice;
|
||||
use devices::Serial;
|
||||
use hypervisor::CpuConfigAArch64;
|
||||
use hypervisor::DeviceKind;
|
||||
use hypervisor::Hypervisor;
|
||||
use hypervisor::HypervisorCap;
|
||||
|
@ -602,11 +603,7 @@ impl arch::LinuxArch for AArch64 {
|
|||
_vcpu_id: usize,
|
||||
_num_cpus: usize,
|
||||
_has_bios: bool,
|
||||
_no_smt: bool,
|
||||
_host_cpu_topology: bool,
|
||||
_enable_pnp_data: bool,
|
||||
_itmt: bool,
|
||||
_force_calibrated_tsc_leaf: bool,
|
||||
_cpu_config: Option<CpuConfigAArch64>,
|
||||
) -> std::result::Result<(), Self::Error> {
|
||||
// AArch64 doesn't configure vcpus on the vcpu thread, so nothing to do here.
|
||||
Ok(())
|
||||
|
|
|
@ -58,6 +58,10 @@ use devices::SerialParameters;
|
|||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
use gdbstub_arch::x86::reg::X86_64CoreRegs as GdbStubRegs;
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
use hypervisor::CpuConfigAArch64 as CpuConfigArch;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use hypervisor::CpuConfigX86_64 as CpuConfigArch;
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
use hypervisor::Hypervisor as HypervisorArch;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use hypervisor::HypervisorX86_64 as HypervisorArch;
|
||||
|
@ -275,11 +279,7 @@ pub trait LinuxArch {
|
|||
/// * `vcpu_id` - The id of the given `vcpu`.
|
||||
/// * `num_cpus` - Number of virtual CPUs the guest will have.
|
||||
/// * `has_bios` - Whether the `VmImage` is a `Bios` image
|
||||
/// * `no_smt` - Wheter diabling SMT.
|
||||
/// * `host_cpu_topology` - whether enabling host cpu topology.
|
||||
/// * `enable_pnp_data` - whether enabling PnP statistics data.
|
||||
/// * `itmt` - whether enabling ITMT scheduler
|
||||
/// * `force_calibrated_tsc_leaf` - whether to force using a calibrated TSC leaf (0x15).
|
||||
/// * `cpu_config` - CPU feature configurations.
|
||||
fn configure_vcpu<V: Vm>(
|
||||
vm: &V,
|
||||
hypervisor: &dyn HypervisorArch,
|
||||
|
@ -289,11 +289,7 @@ pub trait LinuxArch {
|
|||
vcpu_id: usize,
|
||||
num_cpus: usize,
|
||||
has_bios: bool,
|
||||
no_smt: bool,
|
||||
host_cpu_topology: bool,
|
||||
enable_pnp_data: bool,
|
||||
itmt: bool,
|
||||
force_calibrated_tsc_leaf: bool,
|
||||
cpu_config: Option<CpuConfigArch>,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
/// Configures and add a pci device into vm
|
||||
|
|
|
@ -131,6 +131,9 @@ impl_downcast!(VcpuAArch64);
|
|||
#[derive(Clone, Default)]
|
||||
pub struct VcpuInitAArch64 {}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CpuConfigAArch64 {}
|
||||
|
||||
// Convenience constructors for IrqRoutes
|
||||
impl IrqRoute {
|
||||
pub fn gic_irq_route(irq_num: u32) -> IrqRoute {
|
||||
|
|
|
@ -195,6 +195,48 @@ pub struct VcpuInitX86_64 {
|
|||
pub msrs: Vec<Register>,
|
||||
}
|
||||
|
||||
/// Hold the CPU feature configurations that are needed to setup a vCPU.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CpuConfigX86_64 {
|
||||
/// whether to force using a calibrated TSC leaf (0x15).
|
||||
pub force_calibrated_tsc_leaf: bool,
|
||||
|
||||
/// whether enabling host cpu topology.
|
||||
pub host_cpu_topology: bool,
|
||||
|
||||
/// whether expose HWP feature to the guest.
|
||||
pub enable_hwp: bool,
|
||||
|
||||
/// whether enabling host cpu topology.
|
||||
pub enable_pnp_data: bool,
|
||||
|
||||
/// Wheter diabling SMT (Simultaneous Multithreading).
|
||||
pub no_smt: bool,
|
||||
|
||||
/// whether enabling ITMT scheduler
|
||||
pub itmt: bool,
|
||||
}
|
||||
|
||||
impl CpuConfigX86_64 {
|
||||
pub fn new(
|
||||
force_calibrated_tsc_leaf: bool,
|
||||
host_cpu_topology: bool,
|
||||
enable_hwp: bool,
|
||||
enable_pnp_data: bool,
|
||||
no_smt: bool,
|
||||
itmt: bool,
|
||||
) -> Self {
|
||||
CpuConfigX86_64 {
|
||||
force_calibrated_tsc_leaf,
|
||||
host_cpu_topology,
|
||||
enable_hwp,
|
||||
enable_pnp_data,
|
||||
no_smt,
|
||||
itmt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
|
|
@ -610,6 +610,9 @@ pub struct RunCommand {
|
|||
/// directory with smbios_entry_point/DMI files
|
||||
pub dmi_path: Option<PathBuf>,
|
||||
#[argh(switch)]
|
||||
/// expose HWP feature to the guest
|
||||
pub enable_hwp: bool,
|
||||
#[argh(switch)]
|
||||
/// expose Power and Perfomance (PnP) data to guest and guest can show these PnP data
|
||||
pub enable_pnp_data: bool,
|
||||
#[argh(positional, arg_name = "KERNEL")]
|
||||
|
@ -1771,6 +1774,7 @@ impl TryFrom<RunCommand> for super::config::Config {
|
|||
|
||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
|
||||
{
|
||||
cfg.enable_hwp = cmd.enable_hwp;
|
||||
cfg.host_cpu_topology = cmd.host_cpu_topology;
|
||||
cfg.force_s2idle = cmd.s2idle;
|
||||
cfg.pcie_ecam = cmd.pcie_ecam;
|
||||
|
|
|
@ -1228,6 +1228,7 @@ pub struct Config {
|
|||
pub display_window_keyboard: bool,
|
||||
pub display_window_mouse: bool,
|
||||
pub dmi_path: Option<PathBuf>,
|
||||
pub enable_hwp: bool,
|
||||
pub enable_pnp_data: bool,
|
||||
pub executable_path: Option<Executable>,
|
||||
#[cfg(windows)]
|
||||
|
@ -1420,6 +1421,7 @@ impl Default for Config {
|
|||
display_window_keyboard: false,
|
||||
display_window_mouse: false,
|
||||
dmi_path: None,
|
||||
enable_hwp: false,
|
||||
enable_pnp_data: false,
|
||||
executable_path: None,
|
||||
#[cfg(windows)]
|
||||
|
@ -1689,6 +1691,10 @@ pub fn validate_config(cfg: &mut Config) -> std::result::Result<(), String> {
|
|||
}
|
||||
}
|
||||
}
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
if cfg.enable_hwp && !cfg.host_cpu_topology {
|
||||
return Err("setting `enable-hwp` requires `host-cpu-topology` is set.".to_string());
|
||||
}
|
||||
if cfg.enable_pnp_data {
|
||||
if !cfg.host_cpu_topology {
|
||||
return Err(
|
||||
|
@ -1736,6 +1742,9 @@ pub fn validate_config(cfg: &mut Config) -> std::result::Result<(), String> {
|
|||
return Err("`itmt` requires affinity to be set for every vCPU.".to_string());
|
||||
}
|
||||
}
|
||||
if !cfg.enable_hwp {
|
||||
return Err("setting `itmt` requires `enable-hwp` is set.".to_string());
|
||||
}
|
||||
set_itmt_msr_config(&mut cfg.userspace_msr)
|
||||
.map_err(|e| format!("the cpu doesn't support itmt {}", e))?;
|
||||
}
|
||||
|
|
|
@ -115,6 +115,8 @@ use gpu::*;
|
|||
use hypervisor::kvm::Kvm;
|
||||
use hypervisor::kvm::KvmVcpu;
|
||||
use hypervisor::kvm::KvmVm;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use hypervisor::CpuConfigX86_64;
|
||||
use hypervisor::HypervisorCap;
|
||||
use hypervisor::ProtectionType;
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
|
@ -2058,6 +2060,20 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
|
|||
Some(VcpuAffinity::PerVcpu(mut m)) => m.remove(&cpu_id).unwrap_or_default(),
|
||||
None => Default::default(),
|
||||
};
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
let cpu_config = Some(CpuConfigX86_64::new(
|
||||
cfg.force_calibrated_tsc_leaf,
|
||||
cfg.host_cpu_topology,
|
||||
cfg.enable_hwp,
|
||||
cfg.enable_pnp_data,
|
||||
cfg.no_smt,
|
||||
cfg.itmt,
|
||||
));
|
||||
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
let cpu_config = None;
|
||||
|
||||
let handle = vcpu::run_vcpu(
|
||||
cpu_id,
|
||||
vcpu_ids[cpu_id],
|
||||
|
@ -2072,7 +2088,6 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
|
|||
linux.rt_cpus.contains(&cpu_id),
|
||||
vcpu_affinity,
|
||||
linux.delay_rt,
|
||||
linux.no_smt,
|
||||
vcpu_thread_barrier.clone(),
|
||||
linux.has_bios,
|
||||
(*linux.io_bus).clone(),
|
||||
|
@ -2086,10 +2101,7 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
|
|||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
to_gdb_channel.clone(),
|
||||
cfg.per_vm_core_scheduling,
|
||||
cfg.host_cpu_topology,
|
||||
cfg.enable_pnp_data,
|
||||
cfg.itmt,
|
||||
cfg.force_calibrated_tsc_leaf,
|
||||
cpu_config,
|
||||
cfg.privileged_vm,
|
||||
match vcpu_cgroup_tasks_file {
|
||||
None => None,
|
||||
|
|
|
@ -29,6 +29,10 @@ use devices::IrqChipAArch64 as IrqChipArch;
|
|||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use devices::IrqChipX86_64 as IrqChipArch;
|
||||
use devices::VcpuRunState;
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
use hypervisor::CpuConfigAArch64 as CpuConfigArch;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use hypervisor::CpuConfigX86_64 as CpuConfigArch;
|
||||
use hypervisor::IoOperation;
|
||||
use hypervisor::IoParams;
|
||||
use hypervisor::Vcpu;
|
||||
|
@ -157,13 +161,9 @@ pub fn runnable_vcpu<V>(
|
|||
vm: impl VmArch,
|
||||
irq_chip: &mut dyn IrqChipArch,
|
||||
vcpu_count: usize,
|
||||
no_smt: bool,
|
||||
has_bios: bool,
|
||||
use_hypervisor_signals: bool,
|
||||
host_cpu_topology: bool,
|
||||
enable_pnp_data: bool,
|
||||
itmt: bool,
|
||||
force_calibrated_tsc_leaf: bool,
|
||||
cpu_config: Option<CpuConfigArch>,
|
||||
) -> Result<(V, VcpuRunHandle)>
|
||||
where
|
||||
V: VcpuArch,
|
||||
|
@ -197,11 +197,7 @@ where
|
|||
cpu_id,
|
||||
vcpu_count,
|
||||
has_bios,
|
||||
no_smt,
|
||||
host_cpu_topology,
|
||||
enable_pnp_data,
|
||||
itmt,
|
||||
force_calibrated_tsc_leaf,
|
||||
cpu_config,
|
||||
)
|
||||
.context("failed to configure vcpu")?;
|
||||
|
||||
|
@ -581,7 +577,6 @@ pub fn run_vcpu<V>(
|
|||
run_rt: bool,
|
||||
vcpu_affinity: Vec<usize>,
|
||||
delay_rt: bool,
|
||||
no_smt: bool,
|
||||
start_barrier: Arc<Barrier>,
|
||||
has_bios: bool,
|
||||
mut io_bus: Bus,
|
||||
|
@ -594,10 +589,7 @@ pub fn run_vcpu<V>(
|
|||
mpsc::Sender<VcpuDebugStatusMessage>,
|
||||
>,
|
||||
enable_per_vm_core_scheduling: bool,
|
||||
host_cpu_topology: bool,
|
||||
enable_pnp_data: bool,
|
||||
itmt: bool,
|
||||
force_calibrated_tsc_leaf: bool,
|
||||
cpu_config: Option<CpuConfigArch>,
|
||||
privileged_vm: bool,
|
||||
vcpu_cgroup_tasks_file: Option<File>,
|
||||
userspace_msr: BTreeMap<u32, MsrConfig>,
|
||||
|
@ -633,13 +625,9 @@ where
|
|||
vm,
|
||||
irq_chip.as_mut(),
|
||||
vcpu_count,
|
||||
no_smt,
|
||||
has_bios,
|
||||
use_hypervisor_signals,
|
||||
host_cpu_topology,
|
||||
enable_pnp_data,
|
||||
itmt,
|
||||
force_calibrated_tsc_leaf,
|
||||
cpu_config,
|
||||
);
|
||||
|
||||
// Add MSR handlers after CPU affinity setting.
|
||||
|
|
|
@ -130,6 +130,8 @@ use hypervisor::whpx::WhpxFeature;
|
|||
use hypervisor::whpx::WhpxVcpu;
|
||||
#[cfg(feature = "whpx")]
|
||||
use hypervisor::whpx::WhpxVm;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use hypervisor::CpuConfigX86_64;
|
||||
#[cfg(feature = "whpx")]
|
||||
use hypervisor::Hypervisor;
|
||||
#[cfg(feature = "whpx")]
|
||||
|
@ -1461,16 +1463,21 @@ fn create_whpx(
|
|||
info!("Creating Whpx");
|
||||
let whpx = Whpx::new()?;
|
||||
|
||||
let cpu_config = CpuConfigX86_64::new(
|
||||
force_calibrated_tsc_leaf,
|
||||
false, /* host_cpu_topology */
|
||||
false, /* enable_hwp */
|
||||
false, /* enable_pnp_data */
|
||||
no_smt,
|
||||
false, /* itmt */
|
||||
);
|
||||
|
||||
// context for non-cpu-specific cpuid results
|
||||
let ctx = CpuIdContext::new(
|
||||
0,
|
||||
cpu_count,
|
||||
no_smt,
|
||||
/*host_cpu_topology=*/ false,
|
||||
None,
|
||||
/* enable_pnp_data */ false,
|
||||
/* itmt */ false,
|
||||
force_calibrated_tsc_leaf,
|
||||
cpu_config,
|
||||
whpx.check_capability(HypervisorCap::CalibratedTscLeafRequired),
|
||||
__cpuid_count,
|
||||
__cpuid,
|
||||
|
|
|
@ -52,6 +52,8 @@ use devices::VcpuRunState;
|
|||
use futures::pin_mut;
|
||||
#[cfg(feature = "whpx")]
|
||||
use hypervisor::whpx::WhpxVcpu;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use hypervisor::CpuConfigX86_64;
|
||||
use hypervisor::HypervisorCap;
|
||||
use hypervisor::IoEventAddress;
|
||||
use hypervisor::IoOperation;
|
||||
|
@ -199,6 +201,19 @@ impl VcpuRunThread {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
let cpu_config = Some(CpuConfigX86_64::new(
|
||||
force_calibrated_tsc_leaf,
|
||||
host_cpu_topology,
|
||||
false, /* enable_hwp */
|
||||
false, /* enable_pnp_data */
|
||||
no_smt,
|
||||
false, /* itmt */
|
||||
));
|
||||
|
||||
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
|
||||
let cpu_config = None;
|
||||
|
||||
Arch::configure_vcpu(
|
||||
vm,
|
||||
vm.get_hypervisor(),
|
||||
|
@ -208,11 +223,7 @@ impl VcpuRunThread {
|
|||
cpu_id,
|
||||
vcpu_count,
|
||||
has_bios,
|
||||
no_smt,
|
||||
host_cpu_topology,
|
||||
/* enable_pnp_data */ false,
|
||||
/* itmt */ false,
|
||||
force_calibrated_tsc_leaf,
|
||||
cpu_config,
|
||||
)
|
||||
.exit_context(Exit::ConfigureVcpu, "failed to configure vcpu")?;
|
||||
|
||||
|
@ -295,16 +306,22 @@ impl VcpuRunThread {
|
|||
force_calibrated_tsc_leaf,
|
||||
);
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
let cpu_config = CpuConfigX86_64::new(
|
||||
force_calibrated_tsc_leaf,
|
||||
host_cpu_topology,
|
||||
false, /* enable_hwp */
|
||||
false, /* enable_pnp_data */
|
||||
no_smt,
|
||||
false, /* itmt */
|
||||
);
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
let cpuid_context = CpuIdContext::new(
|
||||
context.cpu_id,
|
||||
vcpu_count,
|
||||
no_smt,
|
||||
host_cpu_topology,
|
||||
Some(irq_chip.as_ref()),
|
||||
/* enable_pnp_data */ false,
|
||||
/* itmt */ false,
|
||||
force_calibrated_tsc_leaf,
|
||||
cpu_config,
|
||||
vm.get_hypervisor()
|
||||
.check_capability(HypervisorCap::CalibratedTscLeafRequired),
|
||||
__cpuid_count,
|
||||
|
|
|
@ -11,6 +11,7 @@ use std::result;
|
|||
use devices::Apic;
|
||||
use devices::IrqChipCap;
|
||||
use devices::IrqChipX86_64;
|
||||
use hypervisor::CpuConfigX86_64;
|
||||
use hypervisor::CpuIdEntry;
|
||||
use hypervisor::HypervisorCap;
|
||||
use hypervisor::HypervisorX86_64;
|
||||
|
@ -48,6 +49,7 @@ const ECX_HCFC_PERF_SHIFT: u32 = 0; // Presence of IA32_MPERF and IA32_APERF.
|
|||
const EAX_CPU_CORES_SHIFT: u32 = 26; // Index of cpu cores in the same physical package.
|
||||
const EDX_HYBRID_CPU_SHIFT: u32 = 15; // Hybrid. The processor is identified as a hybrid part.
|
||||
const EAX_HWP_SHIFT: u32 = 7; // Intel Hardware P-states.
|
||||
const EAX_HWP_NOTIFICATION_SHIFT: u32 = 8; // IA32_HWP_INTERRUPT MSR is supported
|
||||
const EAX_HWP_EPP_SHIFT: u32 = 10; // HWP Energy Perf. Preference.
|
||||
const EAX_ITMT_SHIFT: u32 = 14; // Intel Turbo Boost Max Technology 3.0 available.
|
||||
const EAX_CORE_TEMP: u32 = 0; // Core Temperature
|
||||
|
@ -60,8 +62,6 @@ pub struct CpuIdContext {
|
|||
vcpu_id: usize,
|
||||
/// The total number of vcpus on this VM.
|
||||
cpu_count: usize,
|
||||
/// Whether or not SMT should be disabled.
|
||||
no_smt: bool,
|
||||
/// Whether or not the IrqChip's APICs support X2APIC.
|
||||
x2apic: bool,
|
||||
/// Whether or not the IrqChip's APICs support a TSC deadline timer.
|
||||
|
@ -70,17 +70,10 @@ pub struct CpuIdContext {
|
|||
apic_frequency: u32,
|
||||
/// The TSC frequency in Hz, if it could be determined.
|
||||
tsc_frequency: Option<u64>,
|
||||
/// Whether to force the use of a calibrated TSC cpuid leaf (0x15) even if
|
||||
/// the hypervisor doesn't require it.
|
||||
force_calibrated_tsc_leaf: bool,
|
||||
/// Whether the hypervisor requires a calibrated TSC cpuid leaf (0x15).
|
||||
calibrated_tsc_leaf_required: bool,
|
||||
/// Whether or not VCPU IDs and APIC IDs should match host cpu IDs.
|
||||
host_cpu_topology: bool,
|
||||
/// Whether to expose core temperature, package temperature and APEF/MPERF to guest
|
||||
enable_pnp_data: bool,
|
||||
/// Enable Intel Turbo Boost Max Technology 3.0.
|
||||
itmt: bool,
|
||||
/// CPU feature configurations.
|
||||
cpu_config: CpuConfigX86_64,
|
||||
/// __cpuid_count or a fake function for test.
|
||||
cpuid_count: unsafe fn(u32, u32) -> CpuidResult,
|
||||
/// __cpuid or a fake function for test.
|
||||
|
@ -91,12 +84,8 @@ impl CpuIdContext {
|
|||
pub fn new(
|
||||
vcpu_id: usize,
|
||||
cpu_count: usize,
|
||||
no_smt: bool,
|
||||
host_cpu_topology: bool,
|
||||
irq_chip: Option<&dyn IrqChipX86_64>,
|
||||
enable_pnp_data: bool,
|
||||
itmt: bool,
|
||||
force_calibrated_tsc_leaf: bool,
|
||||
cpu_config: CpuConfigX86_64,
|
||||
calibrated_tsc_leaf_required: bool,
|
||||
cpuid_count: unsafe fn(u32, u32) -> CpuidResult,
|
||||
cpuid: unsafe fn(u32) -> CpuidResult,
|
||||
|
@ -104,18 +93,14 @@ impl CpuIdContext {
|
|||
CpuIdContext {
|
||||
vcpu_id,
|
||||
cpu_count,
|
||||
no_smt,
|
||||
x2apic: irq_chip.map_or(false, |chip| chip.check_capability(IrqChipCap::X2Apic)),
|
||||
tsc_deadline_timer: irq_chip.map_or(false, |chip| {
|
||||
chip.check_capability(IrqChipCap::TscDeadlineTimer)
|
||||
}),
|
||||
apic_frequency: irq_chip.map_or(Apic::frequency(), |chip| chip.lapic_frequency()),
|
||||
tsc_frequency: devices::tsc::tsc_frequency().ok(),
|
||||
force_calibrated_tsc_leaf,
|
||||
calibrated_tsc_leaf_required,
|
||||
host_cpu_topology,
|
||||
enable_pnp_data,
|
||||
itmt,
|
||||
cpu_config,
|
||||
cpuid_count,
|
||||
cpuid,
|
||||
}
|
||||
|
@ -150,7 +135,7 @@ pub fn adjust_cpuid(entry: &mut CpuIdEntry, ctx: &CpuIdContext) {
|
|||
entry.cpuid.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
|
||||
}
|
||||
|
||||
if ctx.host_cpu_topology {
|
||||
if ctx.cpu_config.host_cpu_topology {
|
||||
entry.cpuid.ebx |= EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT;
|
||||
|
||||
// Expose HT flag to Guest.
|
||||
|
@ -177,13 +162,13 @@ pub fn adjust_cpuid(entry: &mut CpuIdEntry, ctx: &CpuIdContext) {
|
|||
4 => {
|
||||
entry.cpuid = unsafe { (ctx.cpuid_count)(entry.function, entry.index) };
|
||||
|
||||
if ctx.host_cpu_topology {
|
||||
if ctx.cpu_config.host_cpu_topology {
|
||||
return;
|
||||
}
|
||||
|
||||
entry.cpuid.eax &= !0xFC000000;
|
||||
if ctx.cpu_count > 1 {
|
||||
let cpu_cores = if ctx.no_smt {
|
||||
let cpu_cores = if ctx.cpu_config.no_smt {
|
||||
ctx.cpu_count as u32
|
||||
} else if ctx.cpu_count % 2 == 0 {
|
||||
(ctx.cpu_count >> 1) as u32
|
||||
|
@ -194,32 +179,31 @@ pub fn adjust_cpuid(entry: &mut CpuIdEntry, ctx: &CpuIdContext) {
|
|||
}
|
||||
}
|
||||
6 => {
|
||||
// Clear X86 EPB feature. No frequency selection in the hypervisor.
|
||||
entry.cpuid.ecx &= !(1 << ECX_EPB_SHIFT);
|
||||
// Safe because we pass 6 for this call and the host
|
||||
// supports the `cpuid` instruction
|
||||
let result = unsafe { (ctx.cpuid)(entry.function) };
|
||||
|
||||
// Set ITMT related features.
|
||||
if ctx.itmt || ctx.enable_pnp_data {
|
||||
// Safe because we pass 6 for this call and the host
|
||||
// supports the `cpuid` instruction
|
||||
let result = unsafe { (ctx.cpuid)(entry.function) };
|
||||
if ctx.itmt {
|
||||
// Expose ITMT to guest.
|
||||
if ctx.cpu_config.enable_hwp {
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_HWP_SHIFT);
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_HWP_NOTIFICATION_SHIFT);
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_HWP_EPP_SHIFT);
|
||||
entry.cpuid.ecx |= result.ecx & (1 << ECX_EPB_SHIFT);
|
||||
|
||||
if ctx.cpu_config.itmt {
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_ITMT_SHIFT);
|
||||
// Expose HWP and HWP_EPP to guest.
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_HWP_SHIFT);
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_HWP_EPP_SHIFT);
|
||||
}
|
||||
if ctx.enable_pnp_data {
|
||||
// Expose core temperature, package temperature
|
||||
// and APEF/MPERF to guest
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_CORE_TEMP);
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_PKG_TEMP);
|
||||
entry.cpuid.ecx |= result.ecx & (1 << ECX_HCFC_PERF_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.cpu_config.enable_pnp_data {
|
||||
// Expose core temperature, package temperature
|
||||
// and APEF/MPERF to guest
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_CORE_TEMP);
|
||||
entry.cpuid.eax |= result.eax & (1 << EAX_PKG_TEMP);
|
||||
entry.cpuid.ecx |= result.ecx & (1 << ECX_HCFC_PERF_SHIFT);
|
||||
}
|
||||
}
|
||||
7 => {
|
||||
if ctx.host_cpu_topology && entry.index == 0 {
|
||||
if ctx.cpu_config.host_cpu_topology && entry.index == 0 {
|
||||
// Safe because we pass 7 and 0 for this call and the host supports the
|
||||
// `cpuid` instruction
|
||||
let result = unsafe { (ctx.cpuid_count)(entry.function, entry.index) };
|
||||
|
@ -228,7 +212,7 @@ pub fn adjust_cpuid(entry: &mut CpuIdEntry, ctx: &CpuIdContext) {
|
|||
}
|
||||
0x15 => {
|
||||
if ctx.calibrated_tsc_leaf_required
|
||||
|| ctx.force_calibrated_tsc_leaf {
|
||||
|| ctx.cpu_config.force_calibrated_tsc_leaf {
|
||||
|
||||
let cpuid_15 = ctx
|
||||
.tsc_frequency
|
||||
|
@ -238,7 +222,7 @@ pub fn adjust_cpuid(entry: &mut CpuIdEntry, ctx: &CpuIdContext) {
|
|||
if let Some(new_entry) = cpuid_15 {
|
||||
entry.cpuid = new_entry.cpuid;
|
||||
}
|
||||
} else if ctx.enable_pnp_data {
|
||||
} else if ctx.cpu_config.enable_pnp_data {
|
||||
// Expose TSC frequency to guest
|
||||
// Safe because we pass 0x15 for this call and the host
|
||||
// supports the `cpuid` instruction
|
||||
|
@ -247,14 +231,14 @@ pub fn adjust_cpuid(entry: &mut CpuIdEntry, ctx: &CpuIdContext) {
|
|||
}
|
||||
0x1A => {
|
||||
// Hybrid information leaf.
|
||||
if ctx.host_cpu_topology {
|
||||
if ctx.cpu_config.host_cpu_topology {
|
||||
// Safe because we pass 0x1A for this call and the host supports the
|
||||
// `cpuid` instruction
|
||||
entry.cpuid = unsafe { (ctx.cpuid)(entry.function) };
|
||||
}
|
||||
}
|
||||
0xB | 0x1F => {
|
||||
if ctx.host_cpu_topology {
|
||||
if ctx.cpu_config.host_cpu_topology {
|
||||
return;
|
||||
}
|
||||
// Extended topology enumeration / V2 Extended topology enumeration
|
||||
|
@ -263,7 +247,7 @@ pub fn adjust_cpuid(entry: &mut CpuIdEntry, ctx: &CpuIdContext) {
|
|||
// On AMD, these leaves are not used, so it is currently safe to leave in.
|
||||
entry.cpuid.edx = ctx.vcpu_id as u32; // x2APIC ID
|
||||
if entry.index == 0 {
|
||||
if ctx.no_smt || (ctx.cpu_count == 1) {
|
||||
if ctx.cpu_config.no_smt || (ctx.cpu_count == 1) {
|
||||
// Make it so that all VCPUs appear as different,
|
||||
// non-hyperthreaded cores on the same package.
|
||||
entry.cpuid.eax = 0; // Shift to get id of next level
|
||||
|
@ -334,22 +318,14 @@ fn filter_cpuid(cpuid: &mut hypervisor::CpuId, ctx: &CpuIdContext) {
|
|||
/// * `irq_chip` - `IrqChipX86_64` for adjusting appropriate IrqChip CPUID bits.
|
||||
/// * `vcpu` - `VcpuX86_64` for setting CPU ID.
|
||||
/// * `vcpu_id` - The vcpu index of `vcpu`.
|
||||
/// * `nrcpus` - The number of vcpus being used by this VM.
|
||||
/// * `no_smt` - The flag indicates whether vCPUs supports SMT.
|
||||
/// * `host_cpu_topology` - The flag indicates whether vCPUs use mirror CPU topology.
|
||||
/// * `enable_pnp_data` - The flag indicates whether vCPU shows PnP data.
|
||||
/// * `itmt` - The flag indicates whether vCPU use ITMT scheduling feature.
|
||||
/// * `cpu_config` - CPU feature configurations.
|
||||
pub fn setup_cpuid(
|
||||
hypervisor: &dyn HypervisorX86_64,
|
||||
irq_chip: &dyn IrqChipX86_64,
|
||||
vcpu: &dyn VcpuX86_64,
|
||||
vcpu_id: usize,
|
||||
nrcpus: usize,
|
||||
no_smt: bool,
|
||||
host_cpu_topology: bool,
|
||||
enable_pnp_data: bool,
|
||||
itmt: bool,
|
||||
force_calibrated_tsc_leaf: bool,
|
||||
cpu_config: CpuConfigX86_64,
|
||||
) -> Result<()> {
|
||||
let mut cpuid = hypervisor
|
||||
.get_supported_cpuid()
|
||||
|
@ -360,12 +336,8 @@ pub fn setup_cpuid(
|
|||
&CpuIdContext::new(
|
||||
vcpu_id,
|
||||
nrcpus,
|
||||
no_smt,
|
||||
host_cpu_topology,
|
||||
Some(irq_chip),
|
||||
enable_pnp_data,
|
||||
itmt,
|
||||
force_calibrated_tsc_leaf,
|
||||
cpu_config,
|
||||
hypervisor.check_capability(HypervisorCap::CalibratedTscLeafRequired),
|
||||
__cpuid_count,
|
||||
__cpuid,
|
||||
|
@ -443,17 +415,15 @@ mod tests {
|
|||
edx: 0,
|
||||
},
|
||||
});
|
||||
|
||||
let cpu_config = CpuConfigX86_64::new(false, false, false, false, false, false);
|
||||
filter_cpuid(
|
||||
&mut cpuid,
|
||||
&CpuIdContext::new(
|
||||
1,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
Some(&irq_chip),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
cpu_config,
|
||||
false,
|
||||
__cpuid_count,
|
||||
__cpuid,
|
||||
|
@ -489,18 +459,22 @@ mod tests {
|
|||
ecx: 0,
|
||||
edx: 0,
|
||||
};
|
||||
let cpu_config = CpuConfigX86_64 {
|
||||
force_calibrated_tsc_leaf: false,
|
||||
host_cpu_topology: true,
|
||||
enable_hwp: false,
|
||||
enable_pnp_data: false,
|
||||
no_smt: false,
|
||||
itmt: false,
|
||||
};
|
||||
let ctx = CpuIdContext {
|
||||
vcpu_id: 0,
|
||||
cpu_count: 0,
|
||||
no_smt: false,
|
||||
x2apic: false,
|
||||
tsc_deadline_timer: false,
|
||||
apic_frequency: 0,
|
||||
tsc_frequency: None,
|
||||
host_cpu_topology: true,
|
||||
enable_pnp_data: false,
|
||||
itmt: false,
|
||||
force_calibrated_tsc_leaf: false,
|
||||
cpu_config,
|
||||
calibrated_tsc_leaf_required: false,
|
||||
cpuid_count: fake_cpuid_count,
|
||||
cpuid: fake_cpuid,
|
||||
|
|
|
@ -112,6 +112,7 @@ use gdbstub_arch::x86::reg::X87FpuInternalRegs;
|
|||
use hypervisor::x86_64::Regs;
|
||||
#[cfg(all(target_arch = "x86_64", feature = "gdb"))]
|
||||
use hypervisor::x86_64::Sregs;
|
||||
use hypervisor::CpuConfigX86_64;
|
||||
use hypervisor::HypervisorX86_64;
|
||||
use hypervisor::ProtectionType;
|
||||
use hypervisor::VcpuInitX86_64;
|
||||
|
@ -202,6 +203,8 @@ pub enum Error {
|
|||
#[error("failed to insert device onto bus: {0}")]
|
||||
InsertBus(devices::BusError),
|
||||
#[error("the kernel extends past the end of RAM")]
|
||||
InvalidCpuConfig,
|
||||
#[error("invalid CPU config parameters")]
|
||||
KernelOffsetPastEnd,
|
||||
#[error("error loading bios: {0}")]
|
||||
LoadBios(io::Error),
|
||||
|
@ -860,26 +863,15 @@ impl arch::LinuxArch for X8664arch {
|
|||
vcpu_id: usize,
|
||||
num_cpus: usize,
|
||||
_has_bios: bool,
|
||||
no_smt: bool,
|
||||
host_cpu_topology: bool,
|
||||
enable_pnp_data: bool,
|
||||
itmt: bool,
|
||||
force_calibrated_tsc_leaf: bool,
|
||||
cpu_config: Option<CpuConfigX86_64>,
|
||||
) -> Result<()> {
|
||||
let cpu_config = match cpu_config {
|
||||
Some(config) => config,
|
||||
None => return Err(Error::InvalidCpuConfig),
|
||||
};
|
||||
if !vm.check_capability(VmCap::EarlyInitCpuid) {
|
||||
cpuid::setup_cpuid(
|
||||
hypervisor,
|
||||
irq_chip,
|
||||
vcpu,
|
||||
vcpu_id,
|
||||
num_cpus,
|
||||
no_smt,
|
||||
host_cpu_topology,
|
||||
enable_pnp_data,
|
||||
itmt,
|
||||
force_calibrated_tsc_leaf,
|
||||
)
|
||||
.map_err(Error::SetupCpuid)?;
|
||||
cpuid::setup_cpuid(hypervisor, irq_chip, vcpu, vcpu_id, num_cpus, cpu_config)
|
||||
.map_err(Error::SetupCpuid)?;
|
||||
}
|
||||
|
||||
vcpu.set_regs(&vcpu_init.regs).map_err(Error::WriteRegs)?;
|
||||
|
|
|
@ -20,6 +20,7 @@ use base::Event;
|
|||
use base::Tube;
|
||||
use devices::IrqChipX86_64;
|
||||
use devices::PciConfigIo;
|
||||
use hypervisor::CpuConfigX86_64;
|
||||
use hypervisor::HypervisorX86_64;
|
||||
use hypervisor::IoOperation;
|
||||
use hypervisor::IoParams;
|
||||
|
@ -253,11 +254,9 @@ where
|
|||
.add_vcpu(0, &vcpu)
|
||||
.expect("failed to add vcpu to irqchip");
|
||||
|
||||
let cpu_config = CpuConfigX86_64::new(false, false, false, false, false, false);
|
||||
if !vm.check_capability(VmCap::EarlyInitCpuid) {
|
||||
setup_cpuid(
|
||||
&hyp, &irq_chip, &vcpu, 0, 1, false, false, false, false, false,
|
||||
)
|
||||
.unwrap();
|
||||
setup_cpuid(&hyp, &irq_chip, &vcpu, 0, 1, cpu_config).unwrap();
|
||||
}
|
||||
|
||||
let mut msrs = long_mode_msrs();
|
||||
|
|
Loading…
Reference in a new issue