mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
arch: Apply CPU profiles
We refactor `generate_common_cpuid` to take CPU profiles into account. Signed-off-by: Oliver Anderson <oliver.anderson@cyberus-technology.de> On-behalf-of: SAP oliver.anderson@sap.com
This commit is contained in:
committed by
Rob Bradford
parent
ed3a42bc9d
commit
dbf489c1e5
+134
-56
@@ -37,6 +37,7 @@ use vm_memory::{
|
|||||||
GuestMemoryRegion,
|
GuestMemoryRegion,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::x86_64::cpu_profile::cpuid_adjustments::MissingCpuidEntriesError;
|
||||||
use crate::{CpuProfile, GuestMemoryMmap, InitramfsConfig, RegionType};
|
use crate::{CpuProfile, GuestMemoryMmap, InitramfsConfig, RegionType};
|
||||||
|
|
||||||
// While modern architectures support more than 255 CPUs via x2APIC,
|
// While modern architectures support more than 255 CPUs via x2APIC,
|
||||||
@@ -152,6 +153,22 @@ pub enum Error {
|
|||||||
#[error("Error checking CPUID compatibility")]
|
#[error("Error checking CPUID compatibility")]
|
||||||
CpuidCheckCompatibility,
|
CpuidCheckCompatibility,
|
||||||
|
|
||||||
|
/// Error checking if CPUID is compatible with profile
|
||||||
|
#[error(
|
||||||
|
"The selected CPU profile cannot be utilized because the host's CPUID entries are not compatible with the profile"
|
||||||
|
)]
|
||||||
|
CpuProfileCpuidIncompatibility,
|
||||||
|
|
||||||
|
/// Error because TDX cannot be enabled when a custom (non host) CPU profile has been selected
|
||||||
|
#[error("TDX cannot be enabled when a custom CPU profile has been selected")]
|
||||||
|
CpuProfileTdxIncompatibility,
|
||||||
|
|
||||||
|
/// Error when trying to apply a CPU profile because a necessary CPUID entry was not found
|
||||||
|
#[error(
|
||||||
|
"The selected CPU profile cannot be utilized because a necessary CPUID entry was not found"
|
||||||
|
)]
|
||||||
|
MissingExpectedCpuidEntry(#[source] MissingCpuidEntriesError),
|
||||||
|
|
||||||
// Error writing EBDA address
|
// Error writing EBDA address
|
||||||
#[error("Error writing EBDA address")]
|
#[error("Error writing EBDA address")]
|
||||||
EbdaSetup(#[source] vm_memory::GuestMemoryError),
|
EbdaSetup(#[source] vm_memory::GuestMemoryError),
|
||||||
@@ -578,6 +595,15 @@ impl CpuidFeatureEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate the CPUID entries intended for every vCPU.
|
||||||
|
///
|
||||||
|
/// ## CPU profiles
|
||||||
|
///
|
||||||
|
/// This function takes the CPU profile given in `config` into account and returns compatible CPUID entries
|
||||||
|
/// if possible.
|
||||||
|
///
|
||||||
|
/// An error is returned when the CPUID entries obtained from the hypervisor do not satisfy the requirements
|
||||||
|
/// to apply the selected CPU profile.
|
||||||
pub fn generate_common_cpuid(
|
pub fn generate_common_cpuid(
|
||||||
hypervisor: &dyn hypervisor::Hypervisor,
|
hypervisor: &dyn hypervisor::Hypervisor,
|
||||||
config: &CpuidConfig,
|
config: &CpuidConfig,
|
||||||
@@ -603,6 +629,83 @@ pub fn generate_common_cpuid(
|
|||||||
"Generating guest CPUID for with physical address size: {}",
|
"Generating guest CPUID for with physical address size: {}",
|
||||||
config.phys_bits
|
config.phys_bits
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Supported CPUID
|
||||||
|
let mut cpuid = hypervisor
|
||||||
|
.get_supported_cpuid()
|
||||||
|
.map_err(Error::CpuidGetSupported)?;
|
||||||
|
|
||||||
|
let is_non_host_profile = !matches!(config.profile, CpuProfile::Host);
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
if config.tdx {
|
||||||
|
if is_non_host_profile {
|
||||||
|
// TDX is not supported by CPU profiles other than host for the time being.
|
||||||
|
return Err(Error::CpuProfileTdxIncompatibility.into());
|
||||||
|
}
|
||||||
|
common_cpuid_tdx_configuration(&mut cpuid, hypervisor)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy CPU identification string
|
||||||
|
for i in 0x8000_0002..=0x8000_0004 {
|
||||||
|
cpuid.retain(|c| c.function != i);
|
||||||
|
// SAFETY: call cpuid with valid leaves
|
||||||
|
#[allow(unused_unsafe)]
|
||||||
|
let leaf = unsafe { std::arch::x86_64::__cpuid(i) };
|
||||||
|
cpuid.push(CpuIdEntry {
|
||||||
|
function: i,
|
||||||
|
eax: leaf.eax,
|
||||||
|
ebx: leaf.ebx,
|
||||||
|
ecx: leaf.ecx,
|
||||||
|
edx: leaf.edx,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let cpuid_profile = if is_non_host_profile {
|
||||||
|
let cpuid_profile = config
|
||||||
|
.profile
|
||||||
|
.adjust_cpuid(cpuid.clone(), config.amx, hypervisor.get_cpu_vendor())
|
||||||
|
.map_err(Error::MissingExpectedCpuidEntry)?;
|
||||||
|
|
||||||
|
required_common_cpuid_updates(
|
||||||
|
cpuid_profile,
|
||||||
|
config,
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
hypervisor.hypervisor_type(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let cpuid_host = required_common_cpuid_updates(
|
||||||
|
cpuid,
|
||||||
|
config,
|
||||||
|
#[cfg(feature = "kvm")]
|
||||||
|
hypervisor.hypervisor_type(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// If we want to apply a CPU profile we need to check that it remains compatible with `cpuid_host`
|
||||||
|
if is_non_host_profile {
|
||||||
|
CpuidFeatureEntry::check_cpuid_compatibility_with_descriptions(
|
||||||
|
&cpuid_profile,
|
||||||
|
"CPU Profile",
|
||||||
|
&cpuid_host,
|
||||||
|
"Host VM",
|
||||||
|
)
|
||||||
|
.map_err(|_| Error::CpuProfileCpuidIncompatibility)?;
|
||||||
|
Ok(cpuid_profile)
|
||||||
|
} else {
|
||||||
|
Ok(cpuid_host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply updates to common CPUID (not vCPU specific) that are necessary regardless of
|
||||||
|
/// the chosen CPU profile.
|
||||||
|
fn required_common_cpuid_updates(
|
||||||
|
mut cpuid: Vec<CpuIdEntry>,
|
||||||
|
config: &CpuidConfig,
|
||||||
|
#[cfg(feature = "kvm")] hypervisor_type: hypervisor::HypervisorType,
|
||||||
|
) -> Vec<CpuIdEntry> {
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut cpuid_patches = vec![
|
let mut cpuid_patches = vec![
|
||||||
// Patch hypervisor bit
|
// Patch hypervisor bit
|
||||||
@@ -628,10 +731,7 @@ pub fn generate_common_cpuid(
|
|||||||
];
|
];
|
||||||
|
|
||||||
#[cfg(feature = "kvm")]
|
#[cfg(feature = "kvm")]
|
||||||
if matches!(
|
if matches!(hypervisor_type, hypervisor::HypervisorType::Kvm) {
|
||||||
hypervisor.hypervisor_type(),
|
|
||||||
hypervisor::HypervisorType::Kvm
|
|
||||||
) {
|
|
||||||
// Patch tsc deadline timer bit
|
// Patch tsc deadline timer bit
|
||||||
cpuid_patches.push(CpuidPatch {
|
cpuid_patches.push(CpuidPatch {
|
||||||
function: 1,
|
function: 1,
|
||||||
@@ -644,24 +744,8 @@ pub fn generate_common_cpuid(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Supported CPUID
|
|
||||||
let mut cpuid = hypervisor
|
|
||||||
.get_supported_cpuid()
|
|
||||||
.map_err(Error::CpuidGetSupported)?;
|
|
||||||
|
|
||||||
CpuidPatch::patch_cpuid(&mut cpuid, &cpuid_patches);
|
CpuidPatch::patch_cpuid(&mut cpuid, &cpuid_patches);
|
||||||
|
|
||||||
#[cfg(feature = "tdx")]
|
|
||||||
let tdx_capabilities = if config.tdx {
|
|
||||||
let caps = hypervisor
|
|
||||||
.tdx_capabilities()
|
|
||||||
.map_err(Error::TdxCapabilities)?;
|
|
||||||
info!("TDX capabilities {caps:#?}");
|
|
||||||
Some(caps)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update some existing CPUID
|
// Update some existing CPUID
|
||||||
for entry in cpuid.as_mut_slice().iter_mut() {
|
for entry in cpuid.as_mut_slice().iter_mut() {
|
||||||
#[allow(unused_unsafe)]
|
#[allow(unused_unsafe)]
|
||||||
@@ -676,25 +760,6 @@ pub fn generate_common_cpuid(
|
|||||||
entry.edx &= !(1 << AMX_COMPLEX);
|
entry.edx &= !(1 << AMX_COMPLEX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xd =>
|
|
||||||
{
|
|
||||||
#[cfg(feature = "tdx")]
|
|
||||||
if let Some(caps) = &tdx_capabilities {
|
|
||||||
let xcr0_mask: u64 = 0x82ff;
|
|
||||||
let xss_mask: u64 = !xcr0_mask;
|
|
||||||
if entry.index == 0 {
|
|
||||||
entry.eax &= (caps.xfam_fixed0 as u32) & (xcr0_mask as u32);
|
|
||||||
entry.eax |= (caps.xfam_fixed1 as u32) & (xcr0_mask as u32);
|
|
||||||
entry.edx &= ((caps.xfam_fixed0 & xcr0_mask) >> 32) as u32;
|
|
||||||
entry.edx |= ((caps.xfam_fixed1 & xcr0_mask) >> 32) as u32;
|
|
||||||
} else if entry.index == 1 {
|
|
||||||
entry.ecx &= (caps.xfam_fixed0 as u32) & (xss_mask as u32);
|
|
||||||
entry.ecx |= (caps.xfam_fixed1 as u32) & (xss_mask as u32);
|
|
||||||
entry.edx &= ((caps.xfam_fixed0 & xss_mask) >> 32) as u32;
|
|
||||||
entry.edx |= ((caps.xfam_fixed1 & xss_mask) >> 32) as u32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Tile Information (purely AMX related).
|
// Tile Information (purely AMX related).
|
||||||
0x1d if !config.amx => {
|
0x1d if !config.amx => {
|
||||||
entry.eax = 0;
|
entry.eax = 0;
|
||||||
@@ -766,22 +831,6 @@ pub fn generate_common_cpuid(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy CPU identification string
|
|
||||||
for i in 0x8000_0002..=0x8000_0004 {
|
|
||||||
cpuid.retain(|c| c.function != i);
|
|
||||||
// SAFETY: call cpuid with valid leaves
|
|
||||||
#[allow(unused_unsafe)]
|
|
||||||
let leaf = unsafe { std::arch::x86_64::__cpuid(i) };
|
|
||||||
cpuid.push(CpuIdEntry {
|
|
||||||
function: i,
|
|
||||||
eax: leaf.eax,
|
|
||||||
ebx: leaf.ebx,
|
|
||||||
ecx: leaf.ecx,
|
|
||||||
edx: leaf.edx,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.kvm_hyperv {
|
if config.kvm_hyperv {
|
||||||
// Remove conflicting entries
|
// Remove conflicting entries
|
||||||
cpuid.retain(|c| c.function != 0x4000_0000);
|
cpuid.retain(|c| c.function != 0x4000_0000);
|
||||||
@@ -829,7 +878,36 @@ pub fn generate_common_cpuid(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(cpuid)
|
cpuid
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tdx")]
|
||||||
|
fn common_cpuid_tdx_configuration(
|
||||||
|
cpuid: &mut [CpuIdEntry],
|
||||||
|
hypervisor: &dyn hypervisor::Hypervisor,
|
||||||
|
) -> super::Result<()> {
|
||||||
|
let caps = hypervisor
|
||||||
|
.tdx_capabilities()
|
||||||
|
.map_err(Error::TdxCapabilities)?;
|
||||||
|
info!("TDX capabilities {caps:#?}");
|
||||||
|
|
||||||
|
for entry in cpuid.iter_mut().filter(|entry| entry.function == 0xd) {
|
||||||
|
let xcr0_mask: u64 = 0x82ff;
|
||||||
|
let xss_mask: u64 = !xcr0_mask;
|
||||||
|
if entry.index == 0 {
|
||||||
|
entry.eax &= (caps.xfam_fixed0 as u32) & (xcr0_mask as u32);
|
||||||
|
entry.eax |= (caps.xfam_fixed1 as u32) & (xcr0_mask as u32);
|
||||||
|
entry.edx &= ((caps.xfam_fixed0 & xcr0_mask) >> 32) as u32;
|
||||||
|
entry.edx |= ((caps.xfam_fixed1 & xcr0_mask) >> 32) as u32;
|
||||||
|
} else if entry.index == 1 {
|
||||||
|
entry.ecx &= (caps.xfam_fixed0 as u32) & (xss_mask as u32);
|
||||||
|
entry.ecx |= (caps.xfam_fixed1 as u32) & (xss_mask as u32);
|
||||||
|
entry.edx &= ((caps.xfam_fixed0 & xss_mask) >> 32) as u32;
|
||||||
|
entry.edx |= ((caps.xfam_fixed1 & xss_mask) >> 32) as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
|||||||
Reference in New Issue
Block a user