mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-12-22 05:35:20 +00:00
cpu: Implement AMD compatible topology handling
cpu: Pass APIC id explicitly where needed topology: Set subleaf number explicitly Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
parent
31686b91d3
commit
b52966a12c
@ -552,7 +552,6 @@ impl CpuidFeatureEntry {
|
|||||||
|
|
||||||
pub fn generate_common_cpuid(
|
pub fn generate_common_cpuid(
|
||||||
hypervisor: &Arc<dyn hypervisor::Hypervisor>,
|
hypervisor: &Arc<dyn hypervisor::Hypervisor>,
|
||||||
topology: Option<(u8, u8, u8)>,
|
|
||||||
sgx_epc_sections: Option<Vec<SgxEpcSection>>,
|
sgx_epc_sections: Option<Vec<SgxEpcSection>>,
|
||||||
phys_bits: u8,
|
phys_bits: u8,
|
||||||
kvm_hyperv: bool,
|
kvm_hyperv: bool,
|
||||||
@ -615,10 +614,6 @@ pub fn generate_common_cpuid(
|
|||||||
|
|
||||||
CpuidPatch::patch_cpuid(&mut cpuid, cpuid_patches);
|
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 {
|
if let Some(sgx_epc_sections) = sgx_epc_sections {
|
||||||
update_cpuid_sgx(&mut cpuid, sgx_epc_sections)?;
|
update_cpuid_sgx(&mut cpuid, sgx_epc_sections)?;
|
||||||
}
|
}
|
||||||
@ -761,11 +756,26 @@ pub fn configure_vcpu(
|
|||||||
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
||||||
cpuid: Vec<CpuIdEntry>,
|
cpuid: Vec<CpuIdEntry>,
|
||||||
kvm_hyperv: bool,
|
kvm_hyperv: bool,
|
||||||
|
cpu_vendor: CpuVendor,
|
||||||
|
topology: Option<(u8, u8, u8)>,
|
||||||
) -> super::Result<()> {
|
) -> super::Result<()> {
|
||||||
// Per vCPU CPUID changes; common are handled via generate_common_cpuid()
|
// Per vCPU CPUID changes; common are handled via generate_common_cpuid()
|
||||||
let mut cpuid = cpuid;
|
let mut cpuid = cpuid;
|
||||||
CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id));
|
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));
|
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
|
// Set ApicId in cpuid for each vcpu
|
||||||
// SAFETY: get host cpuid when eax=1
|
// SAFETY: get host cpuid when eax=1
|
||||||
@ -1160,6 +1170,8 @@ fn update_cpuid_topology(
|
|||||||
threads_per_core: u8,
|
threads_per_core: u8,
|
||||||
cores_per_die: u8,
|
cores_per_die: u8,
|
||||||
dies_per_package: u8,
|
dies_per_package: u8,
|
||||||
|
cpu_vendor: CpuVendor,
|
||||||
|
id: u8,
|
||||||
) {
|
) {
|
||||||
let thread_width = 8 - (threads_per_core - 1).leading_zeros();
|
let thread_width = 8 - (threads_per_core - 1).leading_zeros();
|
||||||
let core_width = (8 - (cores_per_die - 1).leading_zeros()) + thread_width;
|
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),
|
u32::from(dies_per_package * cores_per_die * threads_per_core),
|
||||||
);
|
);
|
||||||
CpuidPatch::set_cpuid_reg(cpuid, 0x1f, Some(2), CpuidReg::ECX, 5 << 8);
|
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
|
// The goal is to update the CPUID sub-leaves to reflect the number of EPC
|
||||||
|
@ -56,6 +56,8 @@ use hypervisor::kvm::kvm_bindings;
|
|||||||
use hypervisor::kvm::kvm_ioctls::Cap;
|
use hypervisor::kvm::kvm_ioctls::Cap;
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
use hypervisor::kvm::{TdxExitDetails, TdxExitStatus};
|
use hypervisor::kvm::{TdxExitDetails, TdxExitStatus};
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
use hypervisor::CpuVendor;
|
||||||
use hypervisor::{CpuState, HypervisorCpuError, HypervisorType, VmExit, VmOps};
|
use hypervisor::{CpuState, HypervisorCpuError, HypervisorType, VmExit, VmOps};
|
||||||
use libc::{c_void, siginfo_t};
|
use libc::{c_void, siginfo_t};
|
||||||
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
|
#[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::eventfd::EventFd;
|
||||||
use vmm_sys_util::signal::{register_signal_handler, SIGRTMIN};
|
use vmm_sys_util::signal::{register_signal_handler, SIGRTMIN};
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
#[cfg(all(target_arch = "aarch64", feature = "guest_debug"))]
|
#[cfg(all(target_arch = "aarch64", feature = "guest_debug"))]
|
||||||
/// Extract the specified bits of a 64-bit integer.
|
/// Extract the specified bits of a 64-bit integer.
|
||||||
/// For example, to extrace 2 bits from offset 1 (zero based) of `6u64`,
|
/// For example, to extrace 2 bits from offset 1 (zero based) of `6u64`,
|
||||||
@ -315,6 +316,8 @@ pub struct Vcpu {
|
|||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mpidr: u64,
|
mpidr: u64,
|
||||||
saved_state: Option<CpuState>,
|
saved_state: Option<CpuState>,
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
vendor: CpuVendor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vcpu {
|
impl Vcpu {
|
||||||
@ -325,10 +328,12 @@ impl Vcpu {
|
|||||||
/// * `id` - Represents the CPU number between [0, max vcpus).
|
/// * `id` - Represents the CPU number between [0, max vcpus).
|
||||||
/// * `vm` - The virtual machine this vcpu will get attached to.
|
/// * `vm` - The virtual machine this vcpu will get attached to.
|
||||||
/// * `vm_ops` - Optional object for exit handling.
|
/// * `vm_ops` - Optional object for exit handling.
|
||||||
|
/// * `cpu_vendor` - CPU vendor as reported by __cpuid(0x0)
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: u8,
|
id: u8,
|
||||||
vm: &Arc<dyn hypervisor::Vm>,
|
vm: &Arc<dyn hypervisor::Vm>,
|
||||||
vm_ops: Option<Arc<dyn VmOps>>,
|
vm_ops: Option<Arc<dyn VmOps>>,
|
||||||
|
#[cfg(target_arch = "x86_64")] cpu_vendor: CpuVendor,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let vcpu = vm
|
let vcpu = vm
|
||||||
.create_vcpu(id, vm_ops)
|
.create_vcpu(id, vm_ops)
|
||||||
@ -340,6 +345,8 @@ impl Vcpu {
|
|||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mpidr: 0,
|
mpidr: 0,
|
||||||
saved_state: None,
|
saved_state: None,
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
vendor: cpu_vendor,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,6 +363,7 @@ impl Vcpu {
|
|||||||
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
boot_setup: Option<(EntryPoint, &GuestMemoryAtomic<GuestMemoryMmap>)>,
|
||||||
#[cfg(target_arch = "x86_64")] cpuid: Vec<CpuIdEntry>,
|
#[cfg(target_arch = "x86_64")] cpuid: Vec<CpuIdEntry>,
|
||||||
#[cfg(target_arch = "x86_64")] kvm_hyperv: bool,
|
#[cfg(target_arch = "x86_64")] kvm_hyperv: bool,
|
||||||
|
#[cfg(target_arch = "x86_64")] topology: Option<(u8, u8, u8)>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
{
|
{
|
||||||
@ -365,8 +373,16 @@ impl Vcpu {
|
|||||||
}
|
}
|
||||||
info!("Configuring vCPU: cpu_id = {}", self.id);
|
info!("Configuring vCPU: cpu_id = {}", self.id);
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
arch::configure_vcpu(&self.vcpu, self.id, boot_setup, cpuid, kvm_hyperv)
|
arch::configure_vcpu(
|
||||||
.map_err(Error::VcpuConfiguration)?;
|
&self.vcpu,
|
||||||
|
self.id,
|
||||||
|
boot_setup,
|
||||||
|
cpuid,
|
||||||
|
kvm_hyperv,
|
||||||
|
self.vendor,
|
||||||
|
topology,
|
||||||
|
)
|
||||||
|
.map_err(Error::VcpuConfiguration)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -463,6 +479,8 @@ pub struct CpuManager {
|
|||||||
proximity_domain_per_cpu: BTreeMap<u8, u32>,
|
proximity_domain_per_cpu: BTreeMap<u8, u32>,
|
||||||
affinity: BTreeMap<u8, Vec<u8>>,
|
affinity: BTreeMap<u8, Vec<u8>>,
|
||||||
dynamic: bool,
|
dynamic: bool,
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
cpu_vendor: CpuVendor,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CPU_ENABLE_FLAG: usize = 0;
|
const CPU_ENABLE_FLAG: usize = 0;
|
||||||
@ -619,6 +637,8 @@ impl CpuManager {
|
|||||||
let mut vcpu_states = Vec::with_capacity(usize::from(config.max_vcpus));
|
let mut vcpu_states = Vec::with_capacity(usize::from(config.max_vcpus));
|
||||||
vcpu_states.resize_with(usize::from(config.max_vcpus), VcpuState::default);
|
vcpu_states.resize_with(usize::from(config.max_vcpus), VcpuState::default);
|
||||||
let hypervisor_type = hypervisor.hypervisor_type();
|
let hypervisor_type = hypervisor.hypervisor_type();
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
let cpu_vendor = hypervisor.get_cpu_vendor();
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
if config.features.amx {
|
if config.features.amx {
|
||||||
@ -700,6 +720,8 @@ impl CpuManager {
|
|||||||
proximity_domain_per_cpu,
|
proximity_domain_per_cpu,
|
||||||
affinity,
|
affinity,
|
||||||
dynamic,
|
dynamic,
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
cpu_vendor,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -717,22 +739,10 @@ impl CpuManager {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|sgx_epc_region| sgx_epc_region.epc_sections().values().cloned().collect());
|
.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 = {
|
self.cpuid = {
|
||||||
let phys_bits = physical_bits(hypervisor, self.config.max_phys_bits);
|
let phys_bits = physical_bits(hypervisor, self.config.max_phys_bits);
|
||||||
arch::generate_common_cpuid(
|
arch::generate_common_cpuid(
|
||||||
hypervisor,
|
hypervisor,
|
||||||
topology,
|
|
||||||
sgx_epc_sections,
|
sgx_epc_sections,
|
||||||
phys_bits,
|
phys_bits,
|
||||||
self.config.kvm_hyperv,
|
self.config.kvm_hyperv,
|
||||||
@ -748,7 +758,13 @@ impl CpuManager {
|
|||||||
fn create_vcpu(&mut self, cpu_id: u8, snapshot: Option<Snapshot>) -> Result<Arc<Mutex<Vcpu>>> {
|
fn create_vcpu(&mut self, cpu_id: u8, snapshot: Option<Snapshot>) -> Result<Arc<Mutex<Vcpu>>> {
|
||||||
info!("Creating vCPU: cpu_id = {}", cpu_id);
|
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 {
|
if let Some(snapshot) = snapshot {
|
||||||
// AArch64 vCPUs should be initialized after created.
|
// AArch64 vCPUs should be initialized after created.
|
||||||
@ -784,7 +800,23 @@ impl CpuManager {
|
|||||||
assert!(!self.cpuid.is_empty());
|
assert!(!self.cpuid.is_empty());
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[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")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
vcpu.configure(&self.vm, boot_setup)?;
|
vcpu.configure(&self.vm, boot_setup)?;
|
||||||
|
@ -1668,7 +1668,6 @@ impl Vmm {
|
|||||||
arch::generate_common_cpuid(
|
arch::generate_common_cpuid(
|
||||||
&hypervisor,
|
&hypervisor,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
phys_bits,
|
phys_bits,
|
||||||
vm_config.lock().unwrap().cpus.kvm_hyperv,
|
vm_config.lock().unwrap().cpus.kvm_hyperv,
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
@ -1858,7 +1857,6 @@ impl Vmm {
|
|||||||
arch::generate_common_cpuid(
|
arch::generate_common_cpuid(
|
||||||
&self.hypervisor.clone(),
|
&self.hypervisor.clone(),
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
phys_bits,
|
phys_bits,
|
||||||
vm_config.cpus.kvm_hyperv,
|
vm_config.cpus.kvm_hyperv,
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
|
@ -2402,7 +2402,6 @@ impl Snapshottable for Vm {
|
|||||||
arch::generate_common_cpuid(
|
arch::generate_common_cpuid(
|
||||||
&self.hypervisor,
|
&self.hypervisor,
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
phys_bits,
|
phys_bits,
|
||||||
self.config.lock().unwrap().cpus.kvm_hyperv,
|
self.config.lock().unwrap().cpus.kvm_hyperv,
|
||||||
#[cfg(feature = "tdx")]
|
#[cfg(feature = "tdx")]
|
||||||
|
Loading…
Reference in New Issue
Block a user