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:
Zide Chen 2022-06-29 10:08:23 -07:00 committed by crosvm LUCI
parent 5eba4ce565
commit 69775f0346
13 changed files with 191 additions and 151 deletions

View file

@ -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(())

View file

@ -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

View file

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

View file

@ -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

View file

@ -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;

View file

@ -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))?;
}

View file

@ -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,

View file

@ -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.

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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)?;

View file

@ -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();