diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs index 37f807b9dd..3004a125fa 100644 --- a/aarch64/src/lib.rs +++ b/aarch64/src/lib.rs @@ -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, ) -> std::result::Result<(), Self::Error> { // AArch64 doesn't configure vcpus on the vcpu thread, so nothing to do here. Ok(()) diff --git a/arch/src/lib.rs b/arch/src/lib.rs index ff8ff22746..84a28fa01c 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -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( 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, ) -> Result<(), Self::Error>; /// Configures and add a pci device into vm diff --git a/hypervisor/src/aarch64.rs b/hypervisor/src/aarch64.rs index 63db4a199c..821735a4b0 100644 --- a/hypervisor/src/aarch64.rs +++ b/hypervisor/src/aarch64.rs @@ -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 { diff --git a/hypervisor/src/x86_64.rs b/hypervisor/src/x86_64.rs index feab14358a..409e962109 100644 --- a/hypervisor/src/x86_64.rs +++ b/hypervisor/src/x86_64.rs @@ -195,6 +195,48 @@ pub struct VcpuInitX86_64 { pub msrs: Vec, } +/// 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 diff --git a/src/crosvm/cmdline.rs b/src/crosvm/cmdline.rs index bc912371ef..3d9c61032e 100644 --- a/src/crosvm/cmdline.rs +++ b/src/crosvm/cmdline.rs @@ -610,6 +610,9 @@ pub struct RunCommand { /// directory with smbios_entry_point/DMI files pub dmi_path: Option, #[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 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; diff --git a/src/crosvm/config.rs b/src/crosvm/config.rs index d1c6c1635b..d2118f388d 100644 --- a/src/crosvm/config.rs +++ b/src/crosvm/config.rs @@ -1228,6 +1228,7 @@ pub struct Config { pub display_window_keyboard: bool, pub display_window_mouse: bool, pub dmi_path: Option, + pub enable_hwp: bool, pub enable_pnp_data: bool, pub executable_path: Option, #[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))?; } diff --git a/src/crosvm/sys/unix.rs b/src/crosvm/sys/unix.rs index 6db67e41db..7c221c185a 100644 --- a/src/crosvm/sys/unix.rs +++ b/src/crosvm/sys/unix.rs @@ -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( 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( 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( #[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, diff --git a/src/crosvm/sys/unix/vcpu.rs b/src/crosvm/sys/unix/vcpu.rs index 49647a4bd9..6e24143a46 100644 --- a/src/crosvm/sys/unix/vcpu.rs +++ b/src/crosvm/sys/unix/vcpu.rs @@ -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( 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, ) -> 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( run_rt: bool, vcpu_affinity: Vec, delay_rt: bool, - no_smt: bool, start_barrier: Arc, has_bios: bool, mut io_bus: Bus, @@ -594,10 +589,7 @@ pub fn run_vcpu( mpsc::Sender, >, enable_per_vm_core_scheduling: bool, - host_cpu_topology: bool, - enable_pnp_data: bool, - itmt: bool, - force_calibrated_tsc_leaf: bool, + cpu_config: Option, privileged_vm: bool, vcpu_cgroup_tasks_file: Option, userspace_msr: BTreeMap, @@ -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. diff --git a/src/sys/windows.rs b/src/sys/windows.rs index 6fb6c6149f..061fca6894 100644 --- a/src/sys/windows.rs +++ b/src/sys/windows.rs @@ -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, diff --git a/src/sys/windows/run_vcpu.rs b/src/sys/windows/run_vcpu.rs index 290a371913..8a380631c2 100644 --- a/src/sys/windows/run_vcpu.rs +++ b/src/sys/windows/run_vcpu.rs @@ -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, diff --git a/x86_64/src/cpuid.rs b/x86_64/src/cpuid.rs index d98a99016a..936b53e36d 100644 --- a/x86_64/src/cpuid.rs +++ b/x86_64/src/cpuid.rs @@ -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, - /// 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, diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index a10f2f960d..52759401d6 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -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, ) -> 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)?; diff --git a/x86_64/src/test_integration.rs b/x86_64/src/test_integration.rs index 33286fdde3..bf22f307c4 100644 --- a/x86_64/src/test_integration.rs +++ b/x86_64/src/test_integration.rs @@ -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();