diff --git a/.github/workflows/quality.yaml b/.github/workflows/quality.yaml index c1889a491c..19a4981a4d 100644 --- a/.github/workflows/quality.yaml +++ b/.github/workflows/quality.yaml @@ -143,4 +143,4 @@ jobs: steps: - uses: actions/checkout@v4 # Executes "typos ." - - uses: crate-ci/typos@v1.33.1 + - uses: crate-ci/typos@v1.34.0 diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 333a65d9c4..16b393cc2f 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -12,6 +12,7 @@ extern crate log; use std::collections::BTreeMap; +use std::str::FromStr; use std::sync::Arc; use std::{fmt, result}; @@ -59,6 +60,29 @@ pub enum Error { /// Type for returning public functions outcome. pub type Result = result::Result; +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum CpuProfile { + #[default] + Host, + #[cfg(target_arch = "x86_64")] + CascadeLakeServerV1, +} + +// TODO: Probably better to derive this +impl FromStr for CpuProfile { + // TODO: Use a proper error type + type Err = &'static str; + fn from_str(s: &str) -> result::Result { + match s { + "host" => Ok(Self::Host), + #[cfg(target_arch = "x86_64")] + "cascadelake-server-v1" => Ok(Self::CascadeLakeServerV1), + _ => Err("invalid cpu profile"), + } + } +} + /// Type for memory region types. #[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)] pub enum RegionType { diff --git a/arch/src/x86_64/cpu_profile_feature_flags.rs b/arch/src/x86_64/cpu_profile_feature_flags.rs new file mode 100644 index 0000000000..062417791e --- /dev/null +++ b/arch/src/x86_64/cpu_profile_feature_flags.rs @@ -0,0 +1,112 @@ +use hypervisor::arch::x86::CpuIdEntry; + +use super::{CpuIdFeatureFlags, CpuidReg}; + +pub(super) struct CascadeLakeServerV1CpuIdFeatures { + edx_1: CpuIdFeatureFlags<1, 0, { CpuidReg::EDX as u8 }>, + ecx_1: CpuIdFeatureFlags<1, 0, { CpuidReg::ECX as u8 }>, + edx_8000_0001h: CpuIdFeatureFlags<0x8000_0001, 0, { CpuidReg::EDX as u8 }>, + ecx_8000_0001h: CpuIdFeatureFlags<0x8000_0001, 0, { CpuidReg::ECX as u8 }>, + ebx_7_0: CpuIdFeatureFlags<7, 0, { CpuidReg::EBX as u8 }>, + ecx_7_0: CpuIdFeatureFlags<7, 0, { CpuidReg::ECX as u8 }>, + edx_7_0: CpuIdFeatureFlags<7, 0, { CpuidReg::EDX as u8 }>, + eax_0dh: CpuIdFeatureFlags<0xd, 1, { CpuidReg::EAX as u8 }>, +} + +impl CascadeLakeServerV1CpuIdFeatures { + pub(super) const fn new() -> Self { + use CpuIdFeatureFlags as FF; + // Placing this in a const block ensures compile time evaluation and we get isntant feedback + // (even from the LSP) if the lists contain arguments that are not defined for the given + // function, index, register triple via `crate::x86_64::impl_cpuid_feature_flags!` + const { + Self { + edx_1: FF::<1, 0, { CpuidReg::EDX as u8 }>::from_names(&[ + "vme", "sse2", "sse", "fxsr", "mmx", "clflush", "pse36", "pat", "cmov", "mca", + "pge", "mtrr", "sep", "apic", "cx8", "mce", "pae", "msr", "tsc", "pse", "de", + "fpu", + ]), + + ecx_1: FF::<1, 0, { CpuidReg::ECX as u8 }>::from_names(&[ + "avx", + "xsave", + "aes", + "popcnt", + "x2apic", + "sse4.2", + "sse4.1", + "cx16", + "ssse3", + "pclmulqdq", + "pni", + "tsc-deadline", + "fma", + "movbe", + "pcid", + "f16c", + "rdrand", + ]), + + edx_8000_0001h: FF::<0x8000_0001, 0, { CpuidReg::EDX as u8 }>::from_names(&[ + "lm", "pdpe1gb", "rdtscp", "nx", "syscall", + ]), + ecx_8000_0001h: FF::<0x8000_0001, 0, { CpuidReg::ECX as u8 }>::from_names(&[ + "abm", + "lahf-lm", + "3dnowprefetch", + ]), + ebx_7_0: FF::<7, 0, { CpuidReg::EBX as u8 }>::from_names(&[ + "fsgsbase", + "bmi1", + "hle", + "avx2", + "smep", + "bmi2", + "erms", + "invpcid", + "rtm", + "rdseed", + "adx", + "smap", + "clwb", + "avx512f", + "avx512dq", + "avx512bw", + "avx512cd", + "avx512vl", + "clflushopt", + ]), + ecx_7_0: FF::<7, 0, { CpuidReg::ECX as u8 }>::from_names(&["pku", "avx512vnni"]), + edx_7_0: FF::<7, 0, { CpuidReg::EDX as u8 }>::from_names(&["spec-ctrl", "ssbd"]), + eax_0dh: FF::<0xd, 1, { CpuidReg::EAX as u8 }>::from_names(&[ + "xsaveopt", "xsavec", "xgetbv1", + ]), + } + } + } + + /// Restricts the given entries by performing bitwise intersections of registers + /// per set of matching parameters. + pub(super) fn restrict(self, cpuid: &mut [CpuIdEntry]) { + // NOTE: This might get a bit repetetive when we get more structs in this module. + // Might be worth introducing a proc macro for this at some point... + let Self { + edx_1, + ecx_1, + edx_8000_0001h, + ecx_8000_0001h, + ebx_7_0, + ecx_7_0, + edx_7_0, + eax_0dh, + } = self; + edx_1.intersect_matching(cpuid); + ecx_1.intersect_matching(cpuid); + edx_8000_0001h.intersect_matching(cpuid); + ecx_8000_0001h.intersect_matching(cpuid); + ebx_7_0.intersect_matching(cpuid); + ecx_7_0.intersect_matching(cpuid); + edx_7_0.intersect_matching(cpuid); + eax_0dh.intersect_matching(cpuid); + } +} diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index baa984c94b..4098d9b17d 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -7,10 +7,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD-3-Clause file. use std::sync::Arc; +mod cpu_profile_feature_flags; pub mod interrupts; pub mod layout; mod mpspec; mod mptable; + pub mod regs; use std::collections::BTreeMap; use std::mem; @@ -27,7 +29,7 @@ use vm_memory::{ GuestMemoryRegion, GuestUsize, }; -use crate::{GuestMemoryMmap, InitramfsConfig, RegionType}; +use crate::{CpuProfile, GuestMemoryMmap, InitramfsConfig, RegionType}; mod smbios; use std::arch::x86_64; #[cfg(feature = "tdx")] @@ -127,6 +129,7 @@ pub struct CpuidConfig { #[cfg(feature = "tdx")] pub tdx: bool, pub amx: bool, + pub profile: CpuProfile, } #[derive(Debug, Error)] @@ -229,6 +232,7 @@ pub fn get_x2apic_id(cpu_id: u32, topology: Option<(u8, u8, u8)>) -> u32 { cpu_id } +#[repr(u8)] #[derive(Copy, Clone, Debug)] pub enum CpuidReg { EAX, @@ -237,6 +241,497 @@ pub enum CpuidReg { EDX, } +/// A bitset of CPUID feature flags for a given leaf, sub-leaf and register triple +/// (or function, index, register in KVM terms). +struct CpuIdFeatureFlags(u32); + +impl std::ops::Not + for CpuIdFeatureFlags +{ + type Output = Self; + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl CpuIdFeatureFlags { + fn intersect_matching(&self, cpuid: &mut [CpuIdEntry]) { + if let Some(entry) = cpuid.iter_mut().find(|entry| { + entry.function == FUNCTION + && entry.index == INDEX + // We only care about the cpuid entries with a valid index flag in KVM terms. + // TODO: Is this indeed the case? + && entry.flags == CPUID_FLAG_VALID_INDEX + }) { + let mut updated = 0; + if REG == { CpuidReg::EAX as u8 } { + entry.eax &= self.0; + updated = entry.eax; + } else if REG == { CpuidReg::EBX as u8 } { + entry.ebx &= self.0; + updated = entry.ebx; + } else if REG == { CpuidReg::ECX as u8 } { + entry.ecx &= self.0; + updated = entry.ecx; + } else if REG == { CpuidReg::EDX as u8 } { + entry.edx &= self.0; + updated = entry.edx; + } else { + // Unfortunately we cannot use enums as const generic parameters yet, hence we check for this + // here. + error!("BUG: CpuIdFeatureFlags constructed with invalid register value"); + } + // The job is done, but also check if the updated register contains all set feature flags, + // if not report them. + let mut missing_bits = updated ^ self.0; + if missing_bits != 0 { + // iterate over the missing bits and log a warning + let mut bit_positions = Vec::new(); + while missing_bits != 0 { + let idx = missing_bits.trailing_zeros() as usize; + bit_positions.push(u8::try_from(idx).expect( + "idx is at most 32 hence it can be represented as a u8 without problems", + )); + let least_significant_bit = missing_bits & missing_bits.wrapping_neg(); + missing_bits ^= least_significant_bit; + } + // TODO: Use a proper register name rather than REG and consider returning this as an error instead of logging here + log::warn!( + "the given cpuid entry identified by: \n + function = 0x{:08x} \ + index = 0x{:08x} \ + flags = 0x{:08x} \ + does not have the following bits set: {:?} in the register {:?} + even though the specified restriction permits it. + ", + entry.function, + entry.index, + entry.flags, + bit_positions, + REG + ); + } + } else { + // TODO: Better log or return an error here + log::warn!("no entry matched"); + } + } +} +macro_rules! null_to_empty_else_identity { + (NULL) => { + "" + }; + ($name:literal) => { + $name + }; +} + +macro_rules! impl_cpuid_feature_flags { + (function = $func:literal, index = $idx:literal, register = $reg:path, $($name_or_null:tt),+$(,)*) => { + impl CpuIdFeatureFlags<$func, $idx, {$reg as u8}> { + #[allow(dead_code)] + const SHORT_NAMES_PER_BIT: [&str; 32] = [$(null_to_empty_else_identity!($name_or_null)),+]; + + /// Construct a parametrized [`CpuIdFeatureFlags`] from a set of feature names. + /// + /// # Warning + /// + /// This function should only be called in const contexts to avoid runtime panics (and checks) as const generics does not yet + /// permit arrays (of string slices) there is currently no nice way to enforce this constraint. When Rust will permit arrays of string slices + /// as const generic parameters then we can ensure that the function is only evaluated at compile time (hence no possibilities for runtime panics). + #[allow(dead_code)] + const fn from_names(feature_names: &[&'static str]) -> Self { + if feature_names.len() > 32 { + panic!("There can be at most 32 feature flags in a CPUID register") + } + + let mut i = 0; + let mut set_flags = 0; + // Loop over the FEATURE_NAMES. We need to use while loops with indices due to + // const traits not yet being stable. + while i < feature_names.len() { + let feat_name = feature_names[i]; + // Loop until we find a matching feature in the predefined names + let mut j = 0; + // Unfortunately we need to check for equality byte for byte as this is a const function. + let feat_name_bytes= feat_name.as_bytes(); + while j < 32 { + let current_as_bytes = Self::SHORT_NAMES_PER_BIT[j].as_bytes(); + let length = current_as_bytes.len(); + if length != feat_name_bytes.len() { + // No point in checking for equality as the string slices have different lengths + j += 1; + // If j becomes 32 then the feature was not found. This indicates a bug in the program hence we panic. Unfortunately we + // cannot properly enforce this at compile time as of now (at least not without introducing loads of boiler plate). + if j == 32 { + panic!("the specified feature does not exists, or is not defined for the chosen parametrization of CpuIdFeatureFlags"); + } + continue; + } + let mut matches = true; + let mut k = 0; + while k < length { + matches &= (current_as_bytes[k] == feat_name_bytes[k]); + k += 1 + } + if matches { + set_flags |= (1u32 << j); + break; + } + j += 1; + if j == 32 { + panic!("the specified feature does not exists, or is not defined for the chosen parametrization of CpuIdFeatureFlags"); + } + } + i += 1; + } + Self(set_flags) + } + } + }; +} + +impl_cpuid_feature_flags!( + function = 1, + index = 0, + register = CpuidReg::EDX, + "fpu", + "vme", + "de", + "pse", + "tsc", + "msr", + "pae", + "mce", + "cx8", + "apic", + NULL, + "sep", + "mtrr", + "pge", + "mca", + "cmov", + "pat", + "pse36", + "pn", /* Intel psn */ + "clflush", /* Intel clfsh */ + NULL, + "ds", /* Intel dts */ + "acpi", + "mmx", + "fxsr", + "sse", + "sse2", + "ss", + "ht", /* Intel htt */ + "tm", + "ia64", + "pbe", +); + +impl_cpuid_feature_flags!( + function = 1, + index = 0, + register = CpuidReg::ECX, + "pni", /* Intel,AMD sse3 */ + "pclmulqdq", + "dtes64", + "monitor", + "ds-cpl", + "vmx", + "smx", + "est", + "tm2", + "ssse3", + "cid", + NULL, + "fma", + "cx16", + "xtpr", + "pdcm", + NULL, + "pcid", + "dca", + "sse4.1", + "sse4.2", + "x2apic", + "movbe", + "popcnt", + "tsc-deadline", + "aes", + "xsave", + NULL, /* osxsave */ + "avx", + "f16c", + "rdrand", + "hypervisor", +); +impl_cpuid_feature_flags!( + function = 0x8000_0001, + index = 0, + register = CpuidReg::EDX, + NULL, /* fpu */ + NULL, /* vme */ + NULL, /* de */ + NULL, /* pse */ + NULL, /* tsc */ + NULL, /* msr */ + NULL, /* pae */ + NULL, /* mce */ + NULL, /* cx8 */ + NULL, /* apic */ + NULL, + "syscall", + NULL, /* mtrr */ + NULL, /* pge */ + NULL, /* mca */ + NULL, /* cmov */ + NULL, /* pat */ + NULL, /* pse36 */ + NULL, + NULL, /* Linux mp */ + "nx", + NULL, + "mmxext", + NULL, /* mmx */ + NULL, /* fxsr */ + "fxsr-opt", + "pdpe1gb", + "rdtscp", + NULL, + "lm", + "3dnowext", + "3dnow", +); + +impl_cpuid_feature_flags!( + function = 0x8000_0001, + index = 0, + register = CpuidReg::ECX, + "lahf-lm", + "cmp-legacy", + "svm", + "extapic", + "cr8legacy", + "abm", + "sse4a", + "misalignsse", + "3dnowprefetch", + "osvw", + "ibs", + "xop", + "skinit", + "wdt", + NULL, + "lwp", + "fma4", + "tce", + NULL, + "nodeid-msr", + NULL, + "tbm", + "topoext", + "perfctr-core", + "perfctr-nb", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +); + +impl_cpuid_feature_flags!( + function = 7, + index = 0, + register = CpuidReg::EBX, + "fsgsbase", + "tsc-adjust", + "sgx", + "bmi1", + "hle", + "avx2", + "fdp-excptn-only", + "smep", + "bmi2", + "erms", + "invpcid", + "rtm", + NULL, + "zero-fcs-fds", + "mpx", + NULL, + "avx512f", + "avx512dq", + "rdseed", + "adx", + "smap", + "avx512ifma", + "pcommit", + "clflushopt", + "clwb", + "intel-pt", + "avx512pf", + "avx512er", + "avx512cd", + "sha-ni", + "avx512bw", + "avx512vl", +); + +impl_cpuid_feature_flags!( + function = 7, + index = 0, + register = CpuidReg::ECX, + NULL, + "avx512vbmi", + "umip", + "pku", + NULL, /* ospke */ + "waitpkg", + "avx512vbmi2", + NULL, + "gfni", + "vaes", + "vpclmulqdq", + "avx512vnni", + "avx512bitalg", + NULL, + "avx512-vpopcntdq", + NULL, + "la57", + NULL, + NULL, + NULL, + NULL, + NULL, + "rdpid", + NULL, + "bus-lock-detect", + "cldemote", + NULL, + "movdiri", + "movdir64b", + NULL, + "sgxlc", + "pks", +); + +impl_cpuid_feature_flags!( + function = 7, + index = 0, + register = CpuidReg::EDX, + NULL, + NULL, + "avx512-4vnniw", + "avx512-4fmaps", + "fsrm", + NULL, + NULL, + NULL, + "avx512-vp2intersect", + NULL, + "md-clear", + NULL, + NULL, + NULL, + "serialize", + NULL, + "tsx-ldtrk", + NULL, + NULL, /* pconfig */ + "arch-lbr", + NULL, + NULL, + "amx-bf16", + "avx512-fp16", + "amx-tile", + "amx-int8", + "spec-ctrl", + "stibp", + "flush-l1d", + "arch-capabilities", + "core-capability", + "ssbd", +); + +impl_cpuid_feature_flags!( + function = 6, + index = 0, + register = CpuidReg::EAX, + NULL, + NULL, + "arat", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +); + +impl_cpuid_feature_flags!( + function = 0xd, + index = 1, + register = CpuidReg::EAX, + "xsaveopt", + "xsavec", + "xgetbv1", + "xsaves", + "xfd", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +); + pub struct CpuidPatch { pub function: u32, pub index: u32, @@ -660,6 +1155,15 @@ pub fn generate_common_cpuid( .get_supported_cpuid() .map_err(Error::CpuidGetSupported)?; + // Restrict the supported CPUID to the features supported by the cpu profile + match config.profile { + CpuProfile::Host => { + // When this is set we do nothing + } + CpuProfile::CascadeLakeServerV1 => { + cpu_profile_feature_flags::CascadeLakeServerV1CpuIdFeatures::new().restrict(&mut cpuid); + } + } CpuidPatch::patch_cpuid(&mut cpuid, cpuid_patches); if let Some(sgx_epc_sections) = &config.sgx_epc_sections { diff --git a/docs/macvtap-bridge.md b/docs/macvtap-bridge.md index 75173ee1ca..5161eb7bb6 100644 --- a/docs/macvtap-bridge.md +++ b/docs/macvtap-bridge.md @@ -18,7 +18,7 @@ tapindex=$(< /sys/class/net/macvtap0/ifindex) tapdevice="/dev/tap$tapindex" # Ensure that we can access this device -sudo chown "$UID.$UID" "$tapdevice" +sudo chown "$UID:$UID" "$tapdevice" # Use --net fd=3 to point to fd 3 which the shell has opened to point to the /dev/tapN device target/debug/cloud-hypervisor \ diff --git a/vmm/src/config.rs b/vmm/src/config.rs index b2d940a66b..87ce0594b2 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; use std::result; use std::str::FromStr; +use arch::CpuProfile; use clap::ArgMatches; use option_parser::{ ByteSized, IntegerList, OptionParser, OptionParserError, StringList, Toggle, Tuple, @@ -542,6 +543,7 @@ impl FromStr for CpuTopology { impl CpusConfig { pub fn parse(cpus: &str) -> Result { + // TODO: Verify that this works with the new profile option let mut parser = OptionParser::new(); parser .add("boot") @@ -550,7 +552,8 @@ impl CpusConfig { .add("kvm_hyperv") .add("max_phys_bits") .add("affinity") - .add("features"); + .add("features") + .add("profile"); parser.parse(cpus).map_err(Error::ParseCpus)?; let boot_vcpus: u8 = parser @@ -586,6 +589,10 @@ impl CpusConfig { .convert::("features") .map_err(Error::ParseCpus)? .unwrap_or_default(); + let profile: CpuProfile = parser + .convert("profile") + .map_err(Error::ParseCpus)? + .unwrap_or_default(); // Some ugliness here as the features being checked might be disabled // at compile time causing the below allow and the need to specify the // ref type in the match. @@ -612,6 +619,7 @@ impl CpusConfig { max_phys_bits, affinity, features, + profile, }) } } diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index e26946538e..1263e0f446 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -729,6 +729,9 @@ impl CpuManager { } } } + // TODO: Are there more CPU features that need explicit kernel support to work? + // If so we propose adding each of them to `CpuFeatures` and also dealing with + // here. let proximity_domain_per_cpu: BTreeMap = { let mut cpu_list = Vec::new(); @@ -809,6 +812,7 @@ impl CpuManager { #[cfg(feature = "tdx")] tdx, amx: self.config.features.amx, + profile: self.config.profile, }, ) .map_err(Error::CommonCpuId)? diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 2cf1cb6e95..ec5f4a9042 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -1125,7 +1125,12 @@ impl Vmm { ))); }; - let amx = vm_config.lock().unwrap().cpus.features.amx; + let (amx, cpu_profile) = { + let guard = vm_config.lock().unwrap(); + let amx = guard.cpus.features.amx; + let profile = guard.cpus.profile; + (amx, profile) + }; let phys_bits = vm::physical_bits(&hypervisor, vm_config.lock().unwrap().cpus.max_phys_bits); arch::generate_common_cpuid( @@ -1137,6 +1142,7 @@ impl Vmm { #[cfg(feature = "tdx")] tdx: false, amx, + profile: cpu_profile, }, ) .map_err(|e| { @@ -1276,6 +1282,7 @@ impl Vmm { #[cfg(feature = "tdx")] tdx: false, amx: vm_config.cpus.features.amx, + profile: vm_config.cpus.profile, }, ) .map_err(|e| { @@ -1296,6 +1303,7 @@ impl Vmm { vm_config: Arc>, prefault: bool, ) -> std::result::Result<(), VmError> { + // TODO: Do we need to check for compatibility with the cpu profile here as well? let snapshot = recv_vm_state(source_url).map_err(VmError::Restore)?; #[cfg(all(feature = "kvm", target_arch = "x86_64"))] let vm_snapshot = get_vm_snapshot(&snapshot).map_err(VmError::Restore)?; @@ -2333,6 +2341,8 @@ const DEVICE_MANAGER_SNAPSHOT_ID: &str = "device-manager"; #[cfg(test)] mod unit_tests { + use arch::CpuProfile; + use super::*; #[cfg(target_arch = "x86_64")] use crate::vm_config::DebugConsoleConfig; @@ -2366,6 +2376,7 @@ mod unit_tests { max_phys_bits: 46, affinity: None, features: CpuFeatures::default(), + profile: CpuProfile::default(), }, memory: MemoryConfig { size: 536_870_912, diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index d7bba25cc0..7527888b45 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -2773,11 +2773,10 @@ impl Snapshottable for Vm { #[cfg(all(feature = "kvm", target_arch = "x86_64"))] let common_cpuid = { - let amx = self.config.lock().unwrap().cpus.features.amx; - let phys_bits = physical_bits( - &self.hypervisor, - self.config.lock().unwrap().cpus.max_phys_bits, - ); + let guard = self.config.lock().unwrap(); + let amx = guard.cpus.features.amx; + let phys_bits = physical_bits(&self.hypervisor, guard.cpus.max_phys_bits); + let profile = guard.cpus.profile; arch::generate_common_cpuid( &self.hypervisor, &arch::CpuidConfig { @@ -2787,6 +2786,7 @@ impl Snapshottable for Vm { #[cfg(feature = "tdx")] tdx: false, amx, + profile, }, ) .map_err(|e| { diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs index a2c5b996b4..135edfda0d 100644 --- a/vmm/src/vm_config.rs +++ b/vmm/src/vm_config.rs @@ -6,6 +6,7 @@ use std::net::{IpAddr, Ipv4Addr}; use std::path::PathBuf; use std::{fs, result}; +use arch::CpuProfile; use net_util::MacAddr; use serde::{Deserialize, Serialize}; use virtio_devices::RateLimiterConfig; @@ -65,6 +66,8 @@ pub struct CpusConfig { pub affinity: Option>, #[serde(default)] pub features: CpuFeatures, + #[serde(default)] + pub profile: CpuProfile, } pub const DEFAULT_VCPUS: u8 = 1; @@ -79,6 +82,7 @@ impl Default for CpusConfig { max_phys_bits: DEFAULT_MAX_PHYS_BITS, affinity: None, features: CpuFeatures::default(), + profile: CpuProfile::default(), } } }