From b52966a12c12a62a50fcb9afb63545b326e28c06 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Fri, 28 Apr 2023 20:44:07 +0200 Subject: [PATCH] cpu: Implement AMD compatible topology handling cpu: Pass APIC id explicitly where needed topology: Set subleaf number explicitly Signed-off-by: Anatol Belski --- arch/src/x86_64/mod.rs | 81 +++++++++++++++++++++++++++++++++++++++--- vmm/src/cpu.rs | 66 +++++++++++++++++++++++++--------- vmm/src/lib.rs | 2 -- vmm/src/vm.rs | 1 - 4 files changed, 125 insertions(+), 25 deletions(-) diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index 884d0b504..ba65b9423 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -552,7 +552,6 @@ impl CpuidFeatureEntry { pub fn generate_common_cpuid( hypervisor: &Arc, - topology: Option<(u8, u8, u8)>, sgx_epc_sections: Option>, phys_bits: u8, kvm_hyperv: bool, @@ -615,10 +614,6 @@ pub fn generate_common_cpuid( CpuidPatch::patch_cpuid(&mut cpuid, cpuid_patches); - if let Some(t) = topology { - update_cpuid_topology(&mut cpuid, t.0, t.1, t.2); - } - if let Some(sgx_epc_sections) = sgx_epc_sections { update_cpuid_sgx(&mut cpuid, sgx_epc_sections)?; } @@ -761,11 +756,26 @@ pub fn configure_vcpu( boot_setup: Option<(EntryPoint, &GuestMemoryAtomic)>, cpuid: Vec, kvm_hyperv: bool, + cpu_vendor: CpuVendor, + topology: Option<(u8, u8, u8)>, ) -> super::Result<()> { // Per vCPU CPUID changes; common are handled via generate_common_cpuid() let mut cpuid = cpuid; 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)); + if matches!(cpu_vendor, CpuVendor::AMD) { + CpuidPatch::set_cpuid_reg( + &mut cpuid, + 0x8000_001e, + Some(0), + CpuidReg::EAX, + u32::from(id), + ); + } + + if let Some(t) = topology { + update_cpuid_topology(&mut cpuid, t.0, t.1, t.2, cpu_vendor, id); + } // Set ApicId in cpuid for each vcpu // SAFETY: get host cpuid when eax=1 @@ -1160,6 +1170,8 @@ fn update_cpuid_topology( threads_per_core: u8, cores_per_die: u8, dies_per_package: u8, + cpu_vendor: CpuVendor, + id: u8, ) { let thread_width = 8 - (threads_per_core - 1).leading_zeros(); let core_width = (8 - (cores_per_die - 1).leading_zeros()) + thread_width; @@ -1216,6 +1228,65 @@ fn update_cpuid_topology( u32::from(dies_per_package * cores_per_die * threads_per_core), ); CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(2), CpuidReg::ECX, 5 << 8); + + if matches!(cpu_vendor, CpuVendor::AMD) { + CpuidPatch::set_cpuid_reg( + cpuid, + 0x8000_001e, + Some(0), + CpuidReg::EBX, + ((threads_per_core as u32 - 1) << 8) | (id as u32 & 0xff), + ); + CpuidPatch::set_cpuid_reg( + cpuid, + 0x8000_001e, + Some(0), + CpuidReg::ECX, + ((dies_per_package as u32 - 1) << 8) | (thread_width + die_width) & 0xff, + ); + CpuidPatch::set_cpuid_reg(cpuid, 0x8000_001e, Some(0), CpuidReg::EDX, 0); + if cores_per_die * threads_per_core > 1 { + CpuidPatch::set_cpuid_reg( + cpuid, + 0x8000_0001, + Some(0), + CpuidReg::ECX, + (1u32 << 1) | (1u32 << 22), + ); + CpuidPatch::set_cpuid_reg( + cpuid, + 0x0000_0001, + Some(0), + CpuidReg::EBX, + ((id as u32) << 24) + | (8 << 8) + | (((cores_per_die * threads_per_core) as u32) << 16), + ); + let cpuid_patches = vec![ + // Patch tsc deadline timer bit + CpuidPatch { + function: 1, + index: 0, + flags_bit: None, + eax_bit: None, + ebx_bit: None, + ecx_bit: None, + edx_bit: Some(28), + }, + ]; + CpuidPatch::patch_cpuid(cpuid, cpuid_patches); + CpuidPatch::set_cpuid_reg( + cpuid, + 0x8000_0008, + Some(0), + CpuidReg::ECX, + ((thread_width + core_width + die_width) << 12) + | ((cores_per_die * threads_per_core) - 1) as u32, + ); + } else { + CpuidPatch::set_cpuid_reg(cpuid, 0x8000_0008, Some(0), CpuidReg::ECX, 0u32); + } + } } // The goal is to update the CPUID sub-leaves to reflect the number of EPC diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 9e3039f83..ec37449b6 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -56,6 +56,8 @@ use hypervisor::kvm::kvm_bindings; use hypervisor::kvm::kvm_ioctls::Cap; #[cfg(feature = "tdx")] use hypervisor::kvm::{TdxExitDetails, TdxExitStatus}; +#[cfg(target_arch = "x86_64")] +use hypervisor::CpuVendor; use hypervisor::{CpuState, HypervisorCpuError, HypervisorType, VmExit, VmOps}; use libc::{c_void, siginfo_t}; #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] @@ -85,7 +87,6 @@ use vm_migration::{ use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::signal::{register_signal_handler, SIGRTMIN}; use zerocopy::AsBytes; - #[cfg(all(target_arch = "aarch64", feature = "guest_debug"))] /// Extract the specified bits of a 64-bit integer. /// For example, to extrace 2 bits from offset 1 (zero based) of `6u64`, @@ -315,6 +316,8 @@ pub struct Vcpu { #[cfg(target_arch = "aarch64")] mpidr: u64, saved_state: Option, + #[cfg(target_arch = "x86_64")] + vendor: CpuVendor, } impl Vcpu { @@ -325,10 +328,12 @@ impl Vcpu { /// * `id` - Represents the CPU number between [0, max vcpus). /// * `vm` - The virtual machine this vcpu will get attached to. /// * `vm_ops` - Optional object for exit handling. + /// * `cpu_vendor` - CPU vendor as reported by __cpuid(0x0) pub fn new( id: u8, vm: &Arc, vm_ops: Option>, + #[cfg(target_arch = "x86_64")] cpu_vendor: CpuVendor, ) -> Result { let vcpu = vm .create_vcpu(id, vm_ops) @@ -340,6 +345,8 @@ impl Vcpu { #[cfg(target_arch = "aarch64")] mpidr: 0, saved_state: None, + #[cfg(target_arch = "x86_64")] + vendor: cpu_vendor, }) } @@ -356,6 +363,7 @@ impl Vcpu { boot_setup: Option<(EntryPoint, &GuestMemoryAtomic)>, #[cfg(target_arch = "x86_64")] cpuid: Vec, #[cfg(target_arch = "x86_64")] kvm_hyperv: bool, + #[cfg(target_arch = "x86_64")] topology: Option<(u8, u8, u8)>, ) -> Result<()> { #[cfg(target_arch = "aarch64")] { @@ -365,8 +373,16 @@ impl Vcpu { } info!("Configuring vCPU: cpu_id = {}", self.id); #[cfg(target_arch = "x86_64")] - arch::configure_vcpu(&self.vcpu, self.id, boot_setup, cpuid, kvm_hyperv) - .map_err(Error::VcpuConfiguration)?; + arch::configure_vcpu( + &self.vcpu, + self.id, + boot_setup, + cpuid, + kvm_hyperv, + self.vendor, + topology, + ) + .map_err(Error::VcpuConfiguration)?; Ok(()) } @@ -463,6 +479,8 @@ pub struct CpuManager { proximity_domain_per_cpu: BTreeMap, affinity: BTreeMap>, dynamic: bool, + #[cfg(target_arch = "x86_64")] + cpu_vendor: CpuVendor, } const CPU_ENABLE_FLAG: usize = 0; @@ -619,6 +637,8 @@ impl CpuManager { let mut vcpu_states = Vec::with_capacity(usize::from(config.max_vcpus)); vcpu_states.resize_with(usize::from(config.max_vcpus), VcpuState::default); let hypervisor_type = hypervisor.hypervisor_type(); + #[cfg(target_arch = "x86_64")] + let cpu_vendor = hypervisor.get_cpu_vendor(); #[cfg(target_arch = "x86_64")] if config.features.amx { @@ -700,6 +720,8 @@ impl CpuManager { proximity_domain_per_cpu, affinity, dynamic, + #[cfg(target_arch = "x86_64")] + cpu_vendor, }))) } @@ -717,22 +739,10 @@ impl CpuManager { .as_ref() .map(|sgx_epc_region| sgx_epc_region.epc_sections().values().cloned().collect()); - let topology = self.config.topology.clone().map_or_else( - || { - #[cfg(feature = "mshv")] - if matches!(hypervisor.hypervisor_type(), HypervisorType::Mshv) { - return Some((1, self.boot_vcpus(), 1)); - } - None - }, - |t| Some((t.threads_per_core, t.cores_per_die, t.dies_per_package)), - ); - self.cpuid = { let phys_bits = physical_bits(hypervisor, self.config.max_phys_bits); arch::generate_common_cpuid( hypervisor, - topology, sgx_epc_sections, phys_bits, self.config.kvm_hyperv, @@ -748,7 +758,13 @@ impl CpuManager { fn create_vcpu(&mut self, cpu_id: u8, snapshot: Option) -> Result>> { info!("Creating vCPU: cpu_id = {}", cpu_id); - let mut vcpu = Vcpu::new(cpu_id, &self.vm, Some(self.vm_ops.clone()))?; + let mut vcpu = Vcpu::new( + cpu_id, + &self.vm, + Some(self.vm_ops.clone()), + #[cfg(target_arch = "x86_64")] + self.cpu_vendor, + )?; if let Some(snapshot) = snapshot { // AArch64 vCPUs should be initialized after created. @@ -784,7 +800,23 @@ impl CpuManager { assert!(!self.cpuid.is_empty()); #[cfg(target_arch = "x86_64")] - vcpu.configure(boot_setup, self.cpuid.clone(), self.config.kvm_hyperv)?; + let topology = self.config.topology.clone().map_or_else( + || { + #[cfg(feature = "mshv")] + if matches!(self.hypervisor_type, HypervisorType::Mshv) { + return Some((1, self.boot_vcpus(), 1)); + } + None + }, + |t| Some((t.threads_per_core, t.cores_per_die, t.dies_per_package)), + ); + #[cfg(target_arch = "x86_64")] + vcpu.configure( + boot_setup, + self.cpuid.clone(), + self.config.kvm_hyperv, + topology, + )?; #[cfg(target_arch = "aarch64")] vcpu.configure(&self.vm, boot_setup)?; diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index f685d405f..a1fe11237 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -1668,7 +1668,6 @@ impl Vmm { arch::generate_common_cpuid( &hypervisor, None, - None, phys_bits, vm_config.lock().unwrap().cpus.kvm_hyperv, #[cfg(feature = "tdx")] @@ -1858,7 +1857,6 @@ impl Vmm { arch::generate_common_cpuid( &self.hypervisor.clone(), None, - None, phys_bits, vm_config.cpus.kvm_hyperv, #[cfg(feature = "tdx")] diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 2b829df52..d3219529d 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -2402,7 +2402,6 @@ impl Snapshottable for Vm { arch::generate_common_cpuid( &self.hypervisor, None, - None, phys_bits, self.config.lock().unwrap().cpus.kvm_hyperv, #[cfg(feature = "tdx")]