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
@@ -37,6 +37,7 @@ use vm_memory::{
|
||||
GuestMemoryRegion,
|
||||
};
|
||||
|
||||
use crate::x86_64::cpu_profile::cpuid_adjustments::MissingCpuidEntriesError;
|
||||
use crate::{CpuProfile, GuestMemoryMmap, InitramfsConfig, RegionType};
|
||||
|
||||
// While modern architectures support more than 255 CPUs via x2APIC,
|
||||
@@ -152,6 +153,22 @@ pub enum Error {
|
||||
#[error("Error checking CPUID compatibility")]
|
||||
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("Error writing EBDA address")]
|
||||
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(
|
||||
hypervisor: &dyn hypervisor::Hypervisor,
|
||||
config: &CpuidConfig,
|
||||
@@ -603,6 +629,83 @@ pub fn generate_common_cpuid(
|
||||
"Generating guest CPUID for with physical address size: {}",
|
||||
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)]
|
||||
let mut cpuid_patches = vec![
|
||||
// Patch hypervisor bit
|
||||
@@ -628,10 +731,7 @@ pub fn generate_common_cpuid(
|
||||
];
|
||||
|
||||
#[cfg(feature = "kvm")]
|
||||
if matches!(
|
||||
hypervisor.hypervisor_type(),
|
||||
hypervisor::HypervisorType::Kvm
|
||||
) {
|
||||
if matches!(hypervisor_type, hypervisor::HypervisorType::Kvm) {
|
||||
// Patch tsc deadline timer bit
|
||||
cpuid_patches.push(CpuidPatch {
|
||||
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);
|
||||
|
||||
#[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
|
||||
for entry in cpuid.as_mut_slice().iter_mut() {
|
||||
#[allow(unused_unsafe)]
|
||||
@@ -676,25 +760,6 @@ pub fn generate_common_cpuid(
|
||||
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).
|
||||
0x1d if !config.amx => {
|
||||
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 {
|
||||
// Remove conflicting entries
|
||||
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)]
|
||||
|
||||
Reference in New Issue
Block a user