diff --git a/arch/src/bin/generate-cpu-profile.rs b/arch/src/bin/generate-cpu-profile.rs index ee367906f6..4710fd277e 100644 --- a/arch/src/bin/generate-cpu-profile.rs +++ b/arch/src/bin/generate-cpu-profile.rs @@ -7,7 +7,6 @@ feature = "cpu_profile_generation", feature = "kvm" ))] -use std::io::BufWriter; use anyhow::Context; use clap::{Arg, Command}; @@ -27,12 +26,5 @@ fn main() -> anyhow::Result<()> { let profile_name = cmd_arg.get_one::("name").unwrap(); let hypervisor = hypervisor::new().context("Could not obtain hypervisor")?; - // TODO: Consider letting the user provide a file path as a target instead of writing to stdout. - // The way it is now should be sufficient for a PoC however. - let writer = BufWriter::new(std::io::stdout().lock()); - arch::x86_64::cpu_profile_generation::generate_profile_data( - writer, - hypervisor.as_ref(), - profile_name, - ) + arch::x86_64::cpu_profile_generation::generate_profile_data(hypervisor.as_ref(), profile_name) } diff --git a/arch/src/x86_64/cpu_profile.rs b/arch/src/x86_64/cpu_profile.rs index 70b60b0caa..a4c2cc7549 100644 --- a/arch/src/x86_64/cpu_profile.rs +++ b/arch/src/x86_64/cpu_profile.rs @@ -5,7 +5,7 @@ use std::io::Write; -use hypervisor::arch::x86::CpuIdEntry; +use hypervisor::arch::x86::{CpuIdEntry, MsrEntry}; use hypervisor::{CpuVendor, HypervisorType}; use log::error; use serde::ser::SerializeStruct; @@ -15,6 +15,7 @@ use thiserror::Error; use crate::deserialize_u32_hex; use crate::x86_64::CpuidReg; use crate::x86_64::cpuid_definitions::Parameters; +use crate::x86_64::msr_definitions::RegisterAddress; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "kebab-case")] @@ -30,7 +31,7 @@ pub enum CpuProfile { } impl CpuProfile { - /// Loads pre-generated data associated with a CPU profile. + /// Loads pre-generated CPUID data associated with a CPU profile. /// /// If the `amx` flag is false then the AMX tile state components will be /// zeroed out from the associated profile data. This is necessary because @@ -39,18 +40,18 @@ impl CpuProfile { // // We can only generate CPU profiles for the KVM hypervisor for the time being. #[cfg(feature = "kvm")] - pub(in crate::x86_64) fn data(&self, amx: bool) -> Option { - let mut data: CpuProfileData = match self { + pub(in crate::x86_64) fn cpuid_data(&self, amx: bool) -> Option { + let mut data: CpuIdProfileData = match self { Self::Host => None, Self::Skylake => Some( - serde_json::from_slice(include_bytes!("cpu_profiles/skylake.json")) + serde_json::from_slice(include_bytes!("cpu_profiles/skylake.cpuid.json")) .inspect_err(|e| { error!("BUG: could not deserialize CPU profile. Got error: {e:?}"); }) .expect("should be able to deserialize pre-generated data"), ), Self::SapphireRapids => Some( - serde_json::from_slice(include_bytes!("cpu_profiles/sapphire-rapids.json")) + serde_json::from_slice(include_bytes!("cpu_profiles/sapphire-rapids.cpuid.json")) .inspect_err(|e| { error!("BUG: could not deserialize CPU profile. Got error: {e:?}"); }) @@ -84,7 +85,7 @@ impl CpuProfile { } #[cfg(not(feature = "kvm"))] - pub(in crate::x86_64) fn data(&self, _amx: bool) -> Option { + pub(in crate::x86_64) fn cpuid_data(&self, _amx: bool) -> Option { if matches!(*self, Self::Host) { return None; } @@ -92,9 +93,40 @@ impl CpuProfile { // We will probably need one profile per hypervisor. unreachable!() } + + /// Loads pre-generated MSR data associated with a CPU profile. + #[cfg(feature = "kvm")] + pub(in crate::x86_64) fn msr_data(&self) -> Option { + match self { + Self::Host => None, + Self::Skylake => Some( + serde_json::from_slice(include_bytes!("cpu_profiles/skylake.msr.json")) + .inspect_err(|e| { + error!("BUG: could not deserialize CPU profile. Got error: {e:?}"); + }) + .expect("should be able to deserialize pre-generated data"), + ), + Self::SapphireRapids => Some( + serde_json::from_slice(include_bytes!("cpu_profiles/sapphire-rapids.msr.json")) + .inspect_err(|e| { + error!("BUG: could not deserialize CPU profile. Got error: {e:?}"); + }) + .expect("should be able to deserialize pre-generated data"), + ), + } + } + + #[cfg(not(feature = "kvm"))] + pub(in crate::x86_64) fn msr_data(&self) -> Option { + if matches!(*self, Self::Host) { + return None; + } + // CPU profiles are currently only available when using KVM as the hypervisor. + unreachable!() + } } -/// Every [`CpuProfile`] different from `Host` has associated [`CpuProfileData`]. +/// Every [`CpuProfile`] different from `Host` has associated [`CpuIdProfileData`]. /// /// New constructors of this struct may only be generated through the CHV CLI (when built from source with /// the `cpu-profile-generation` feature) which other hosts may then attempt to load in order to @@ -102,7 +134,7 @@ impl CpuProfile { /// CPU profile. #[derive(Debug, Clone, Serialize, Deserialize)] #[allow(dead_code)] -pub struct CpuProfileData { +pub struct CpuIdProfileData { /// The hypervisor used when generating this CPU profile. pub(in crate::x86_64) hypervisor: HypervisorType, /// The vendor of the CPU belonging to the host that generated this CPU profile. @@ -111,19 +143,6 @@ pub struct CpuProfileData { pub(in crate::x86_64) adjustments: Vec<(Parameters, CpuidOutputRegisterAdjustments)>, } -/* TODO: The [`CpuProfile`] struct will likely need a few more iterations. The following -section should explain why: - -# MSR restrictions - -CPU profiles also need to restrict which MSRs may be manipulated by the guest as various physical CPUs -can have differing supported MSRs. - -The CPU profile will thus necessarily need to contain some data related to MSR restrictions. That will -be taken care of in a follow up MR. - -*/ - /// Used for adjusting an entire cpuid output register (EAX, EBX, ECX or EDX) #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] pub(super) struct CpuidOutputRegisterAdjustments { @@ -276,10 +295,130 @@ impl CpuidOutputRegisterAdjustments { } } +#[derive(Debug, Clone)] +pub(in crate::x86_64) struct FeatureMsrAdjustment { + pub(in crate::x86_64) mask: u64, + pub(in crate::x86_64) replacements: u64, +} + +impl Serialize for FeatureMsrAdjustment { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut s = serializer.serialize_struct("FeatureMsrAdjustment", 2)?; + let mut serialize_field = |key, value| { + // two bytes for "0x" prefix and 16 for the hex encoded number + let mut buffer = [0_u8; 18]; + let _ = write!(&mut buffer[..], "{value:#018x}"); + let str = core::str::from_utf8(&buffer[..]) + .expect("the buffer should be filled with valid UTF-8 bytes"); + s.serialize_field(key, str) + }; + serialize_field("mask", self.mask)?; + serialize_field("replacements", self.replacements)?; + s.end() + } +} + +impl<'de> Deserialize<'de> for FeatureMsrAdjustment { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct ProvisionalFeatureMsrAdjustment<'a> { + #[serde(borrow)] + mask: &'a str, + #[serde(borrow)] + replacements: &'a str, + } + + let ProvisionalFeatureMsrAdjustment { mask, replacements } = + ProvisionalFeatureMsrAdjustment::deserialize(deserializer)?; + let parse_u64 = |hex: &str, field_name: &str| { + u64::from_str_radix(hex.strip_prefix("0x").unwrap_or(""), 16).map_err(|_| { + ::custom(format!("Unable to deserialize FeatureMsrAdjustment: could not deserialize {field_name} the value {hex} is not a hex encoded 64 bit integer")) + }) + }; + let mask = parse_u64(mask, "mask")?; + let replacements = parse_u64(replacements, "replacements")?; + Ok(FeatureMsrAdjustment { mask, replacements }) + } +} + +impl FeatureMsrAdjustment { + /// Returns a struct describing the Feature MSRs that should be set + /// and the ones that should be denied based on `adjustments` and the given + /// `feature_msrs`. + /// + /// # Errors + /// + /// The only way for this to error is if there exists one or more entries in + /// `adjustments` that do not have a corresponding entry in `feature_msrs`. + /// In this case the missing MSR will be logged and the unit type is returned + /// as the error variant. + pub(in crate::x86_64) fn adjust_to( + adjustments: &[(RegisterAddress, FeatureMsrAdjustment)], + feature_msrs: &[MsrEntry], + ) -> Result, ()> { + let mut output_feature_msrs = Vec::with_capacity(feature_msrs.len()); + for (reg_address, adjustment) in adjustments { + let Some(entry) = feature_msrs + .iter() + .find(|entry| entry.index == reg_address.0) + else { + error!( + "Did not find feature based MSR entry for MSR:={:#x}", + reg_address.0 + ); + return Err(()); + }; + // Adjust the entry and push it to outputs + { + let mut entry = *entry; + let data = entry.data; + entry.data = (adjustment.mask & data) | adjustment.replacements; + // TODO: Perhaps trace! would be a better log level? + log::debug!( + "adjusted MSR-based feature: register address:={:#x} value:={:#x}", + entry.index, + entry.data + ); + output_feature_msrs.push(entry); + } + } + Ok(output_feature_msrs) + } +} + +pub struct RequiredMsrUpdates { + pub msr_based_features: Vec, + pub denied_msrs: Vec, +} + +/// Every [`CpuProfile`] different from `Host` has associated [`MsrProfileData`]. +/// +/// New constructors of this struct may only be generated through the CHV CLI (when built from source with +/// the `cpu-profile-generation` feature) which other hosts may then attempt to load in order to +/// increase the likelihood of successful live migrations among all hosts that opted in to the given +/// CPU profile. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(in crate::x86_64) struct MsrProfileData { + pub(in crate::x86_64) cpu_vendor: CpuVendor, + pub(in crate::x86_64) hypervisor_type: HypervisorType, + pub(in crate::x86_64) adjustments: Vec<(RegisterAddress, FeatureMsrAdjustment)>, + pub(in crate::x86_64) permitted_msrs: Vec, +} + #[derive(Debug, Error)] #[error("Required CPUID entries not found")] pub struct MissingCpuidEntriesError; +#[derive(Debug, Error)] +#[error("Required MSR entries not found")] +pub struct MissingMsrEntriesError; + #[cfg(test)] mod tests { use proptest::prelude::*; diff --git a/arch/src/x86_64/cpu_profile_generation.rs b/arch/src/x86_64/cpu_profile_generation.rs index 8a74a2bca7..7ccbbd090d 100644 --- a/arch/src/x86_64/cpu_profile_generation.rs +++ b/arch/src/x86_64/cpu_profile_generation.rs @@ -2,19 +2,25 @@ // // SPDX-License-Identifier: Apache-2.0 // + +use std::collections::HashSet; +use std::fs::File; use std::io::Write; -use std::ops::RangeInclusive; +use std::ops::{BitOr, RangeInclusive, Shl}; +use std::path::PathBuf; use anyhow::{Context, anyhow}; -use hypervisor::arch::x86::CpuIdEntry; +use hypervisor::arch::x86::{CpuIdEntry, MsrEntry}; use hypervisor::{CpuVendor, Hypervisor, HypervisorError, HypervisorType}; +use log::warn; -use crate::x86_64::cpu_profile::CpuProfileData; +use crate::x86_64::cpu_profile::{CpuIdProfileData, FeatureMsrAdjustment, MsrProfileData}; #[cfg(feature = "kvm")] use crate::x86_64::cpuid_definitions::CpuidDefinitions; use crate::x86_64::cpuid_definitions::intel::INTEL_CPUID_DEFINITIONS; use crate::x86_64::cpuid_definitions::kvm::KVM_CPUID_DEFINITIONS; use crate::x86_64::cpuid_definitions::{Parameters, ProfilePolicy}; +use crate::x86_64::msr_definitions::{self, MsrDefinitions, RegisterAddress}; use crate::x86_64::{CpuidOutputRegisterAdjustments, CpuidReg}; /// Generate CPU profile data and convert it to a string, embeddable as Rust code, which is @@ -23,7 +29,6 @@ use crate::x86_64::{CpuidOutputRegisterAdjustments, CpuidReg}; // NOTE: The MVP only works with KVM as the hypervisor and Intel CPUs. #[cfg(feature = "kvm")] pub fn generate_profile_data( - mut writer: impl Write, hypervisor: &dyn Hypervisor, profile_name: &str, ) -> anyhow::Result<()> { @@ -45,16 +50,120 @@ pub fn generate_profile_data( let cpuid = overwrite_brand_string(cpuid, brand_string_bytes); let supported_cpuid_sorted = sort_entries(cpuid); - generate_cpu_profile_data_with( + let Files { + cpuid_data_file, + cpuid_data_license_file, + msr_data_file, + msr_data_license_file, + } = create_files(profile_name)?; + + generate_cpuid_profile_data_with( hypervisor_type, cpu_vendor, &supported_cpuid_sorted, &INTEL_CPUID_DEFINITIONS, &KVM_CPUID_DEFINITIONS, - &mut writer, + cpuid_data_file, + cpuid_data_license_file, + )?; + + let supported_feature_msrs = hypervisor.get_msr_based_features().context("CPU profile generation failed: Could not get the supported MSR-based features from the hypervisor")?; + let supported_msrs = hypervisor + .get_msr_index_list() + .context("CPU profile generation failed: Could not get MSR index list")? + .into_iter() + .collect(); + + generate_msr_profile_data_with( + MsrProfileDataParams { + hypervisor_type, + cpu_vendor, + processor_feature_msr_definitions: + &msr_definitions::intel::INTEL_MSR_FEATURE_DEFINITIONS, + supported_feature_msrs: &supported_feature_msrs, + supported_msrs, + permitted_architectural_msrs: &msr_definitions::intel::PERMITTED_IA32_MSRS[..], + permitted_hypervisor_msrs: &msr_definitions::kvm::PROFILE_PERMITTED_KVM_MSRS[..], + permitted_hyperv_msrs: &msr_definitions::hyperv::HYPERV_MSRS[..], + non_architectural_msrs: &msr_definitions::intel::NON_ARCHITECTURAL_INTEL_MSRS[..], + forbidden_architectural_msrs: &msr_definitions::intel::FORBIDDEN_IA32_MSR_RANGES[..], + forbidden_hypervisor_msrs: &msr_definitions::kvm::PROFILE_UNPERMITTED_MSRS[..], + }, + msr_data_file, + msr_data_license_file, ) } +struct Files { + cpuid_data_file: File, + cpuid_data_license_file: File, + msr_data_file: File, + msr_data_license_file: File, +} +/// Create empty files with names derived from the name given to the CPU profile. +/// The name will be lowercase and spaces are replaced with "-". +fn create_files(profile_name: &str) -> anyhow::Result { + let profile_file_name = { + let mut name = String::new(); + for part in profile_name.split_whitespace().map(|s| s.to_lowercase()) { + if !name.is_empty() { + name.push('-'); + } + name.push_str(&part); + } + name + }; + + let create_file = |path: PathBuf| { + File::create(path.clone()).with_context(|| { + format!( + "CPU profile generation failed: Could not create file:={}", + path.to_string_lossy() + ) + }) + }; + + let path_with_license = |mut path: PathBuf| { + path.as_mut_os_string().push(".license"); + path + }; + + let current_dir = std::env::current_dir() + .context("CPU profile generation failed: Unable to get the current working directory")?; + + let common_path = format!("arch/src/x86_64/cpu_profiles/{profile_file_name}"); + + let cpuid_profile_file_name = { + let mut path = current_dir.clone(); + path.push(format!("{common_path}.cpuid.json")); + path + }; + + let cpuid_data_file = create_file(cpuid_profile_file_name.clone())?; + + let cpuid_data_license_file_path = path_with_license(cpuid_profile_file_name); + + let cpuid_data_license_file = create_file(cpuid_data_license_file_path)?; + + let msr_profile_file_name = { + let mut path = current_dir; + path.push(format!("{common_path}.msr.json")); + path + }; + + let msr_data_file = create_file(msr_profile_file_name.clone())?; + + let msr_data_license_file_path = path_with_license(msr_profile_file_name); + let msr_data_license_file = create_file(msr_data_license_file_path)?; + + Ok(Files { + cpuid_data_file, + cpuid_data_license_file, + msr_data_file, + msr_data_license_file, + }) +} + /// Prepare the bytes which the brand string should consist of fn cpu_brand_string_bytes(cpu_vendor: CpuVendor, profile_name: &str) -> anyhow::Result<[u8; 48]> { let cpu_vendor_str: String = serde_json::to_string(&cpu_vendor) @@ -77,20 +186,21 @@ fn cpu_brand_string_bytes(cpu_vendor: CpuVendor, profile_name: &str) -> anyhow:: } Ok(brand_string_bytes) } -/// Computes [`CpuProfileData`] based on the given sorted vector of CPUID entries, hypervisor type, cpu_vendor +/// Computes [`CpuIdProfileData`] based on the given sorted vector of CPUID entries, hypervisor type, cpu_vendor /// and cpuid_definitions. /// -/// The computed [`CpuProfileData`] is then converted to a string representation, embeddable as Rust code, which is +/// The computed [`CpuIdProfileData`] is then converted to a string representation, embeddable as Rust code, which is /// then written by the given `writer`. /// // TODO: Consider making a snapshot test or two for this function. -fn generate_cpu_profile_data_with( +fn generate_cpuid_profile_data_with( hypervisor_type: HypervisorType, cpu_vendor: CpuVendor, supported_cpuid_sorted: &[CpuIdEntry], processor_cpuid_definitions: &CpuidDefinitions, hypervisor_cpuid_definitions: &CpuidDefinitions, - mut writer: &mut impl Write, + mut cpuid_data_file: impl Write, + cpuid_license_file: impl Write, ) -> anyhow::Result<()> { let mut adjustments: Vec<(Parameters, CpuidOutputRegisterAdjustments)> = Vec::new(); @@ -118,7 +228,7 @@ fn generate_cpu_profile_data_with( // The profile should take whatever we get from the host, hence there is no adjustment, but our // mask needs to retain all bits in the range of bits corresponding to this value let (first_bit_pos, last_bit_pos) = value.bits_range; - mask |= bit_range_mask(first_bit_pos, last_bit_pos); + mask |= bit_range_mask::(first_bit_pos, last_bit_pos); } ProfilePolicy::Static(overwrite_value) => { replacements |= overwrite_value << value.bits_range.0; @@ -128,7 +238,8 @@ fn generate_cpu_profile_data_with( let (first_bit_pos, last_bit_pos) = value.bits_range; if let Some(matching_register_value) = maybe_matching_register_output_value { - let extraction_mask = bit_range_mask(first_bit_pos, last_bit_pos); + let extraction_mask = + bit_range_mask::(first_bit_pos, last_bit_pos); let value = matching_register_value & extraction_mask; replacements |= value; } @@ -146,19 +257,224 @@ fn generate_cpu_profile_data_with( } } - let profile_data = CpuProfileData { + let cpuid_profile_data = CpuIdProfileData { hypervisor: hypervisor_type, cpu_vendor, adjustments, }; - serde_json::to_writer_pretty(&mut writer, &profile_data) - .context("failed to serialize the generated profile data to the given writer")?; - writer + serde_json::to_writer_pretty(&mut cpuid_data_file, &cpuid_profile_data) + .context("Cpu profile generation failed: Could not serialize the generated cpuid profile data to the given writer")?; + cpuid_data_file .flush() - .context("CPU profile generation failed: Unable to flush cpu profile data") + .context("CPU profile generation failed: Unable to flush cpuid profile data")?; + write_license_file(cpuid_license_file, "CPUID") +} + +struct MsrProfileDataParams<'a, const N: usize> { + hypervisor_type: HypervisorType, + cpu_vendor: CpuVendor, + processor_feature_msr_definitions: &'a MsrDefinitions, + + /// MSR-based features supported by the hardware and hypervisor used to + /// generate this CPU profile. + supported_feature_msrs: &'a [MsrEntry], + /// MSRs supported by the hardware and hypervisor used to generate this + /// CPU profile. + supported_msrs: HashSet, + /// A list of all architectural MSRs that are permitted if they are also + /// contained in `supported_msrs`. + permitted_architectural_msrs: &'a [u32], + /// MSRs defined by the hypervisor that are permitted if they are supported + /// by the hardware and hypervisor used when generating this CPU profile + /// + /// We let CHV make the final decision at runtime whether they should be + /// available to guests (currently via CPUID) + permitted_hypervisor_msrs: &'a [u32], + /// Hyper-V related MSRs. + /// + /// NOTE: We can only know if these are truly permitted when the profile is + ///applied at runtime, hence we include them in the profile regardless and + ///let CHV remove them if necessary upon applying the CPU profile. + permitted_hyperv_msrs: &'a [u32], + /// A list of known non-architectural MSRs. This list is only used to help + /// us detect MSRs that we might not be aware of. + non_architectural_msrs: &'a [u32], + /// A list of known ranges of architectural msrs, that should not be + /// permitted by any generated CPU profile. This list is only used to help + /// us detect MSRs that we might not be aware of. + forbidden_architectural_msrs: &'a [(u32, u32)], + /// A list of known hypervisor MSRS that should be forbidden. This list is + /// only used to help us detect MSRs that we might not be aware of. + forbidden_hypervisor_msrs: &'a [u32], } +fn generate_msr_profile_data_with<'a, const N: usize>( + MsrProfileDataParams { + hypervisor_type, + cpu_vendor, + processor_feature_msr_definitions, + supported_feature_msrs, + supported_msrs, + permitted_architectural_msrs, + permitted_hypervisor_msrs, + permitted_hyperv_msrs, + non_architectural_msrs, + forbidden_architectural_msrs, + forbidden_hypervisor_msrs, + }: MsrProfileDataParams<'a, N>, + mut msr_data_file: impl Write, + msr_license_file: impl Write, +) -> anyhow::Result<()> { + const KVM_GET_NOT_SET_MSRS: [RegisterAddress; 6] = [ + RegisterAddress::IA32_VMX_PINBASED_CTLS, + RegisterAddress::IA32_VMX_PROCBASED_CTLS, + RegisterAddress::IA32_VMX_EXIT_CTLS, + RegisterAddress::IA32_VMX_ENTRY_CTLS, + RegisterAddress::IA32_VMX_CR0_FIXED1, + RegisterAddress::IA32_VMX_CR4_FIXED1, + ]; + let mut entries_encountered = 0; + let mut adjustments = Vec::new(); + let mut permitted_msrs = HashSet::new(); + 'table: for (reg_addr, definitions) in processor_feature_msr_definitions.as_slice() { + let Some(entry) = supported_feature_msrs + .iter() + .find(|e| e.index == reg_addr.0) + else { + continue; + }; + entries_encountered += 1; + + // NOTE: For now this tool only supports KVM, but we insert this check so we don't forget + // about (possible) KVM specific behavior. + if hypervisor_type == HypervisorType::Kvm && KVM_GET_NOT_SET_MSRS.contains(reg_addr) { + // In this case we do not want to record an update, but just that the MSR is permitted. + permitted_msrs.insert(reg_addr.0); + continue; + } + + let value = entry.data; + let mut replacements = 0; + let mut mask = 0; + let mut bits_accounted_for = 0; + for msr_definitions::ValueDefinition { + policy, + bits_range: (first_bit_pos, last_bit_pos), + .. + } in definitions.as_slice().iter().copied() + { + let temp_mask = bit_range_mask::(first_bit_pos, last_bit_pos); + bits_accounted_for |= temp_mask; + match policy { + msr_definitions::ProfilePolicy::Deny => { + // This can only be applied to the entire MSR + assert_eq!(first_bit_pos, 0); + assert_eq!(last_bit_pos, 63); + continue 'table; + } + msr_definitions::ProfilePolicy::Inherit => { + replacements |= value & temp_mask; + } + msr_definitions::ProfilePolicy::Passthrough => { + mask |= temp_mask; + } + msr_definitions::ProfilePolicy::Static(overwrite_value) => { + replacements |= (overwrite_value) << (first_bit_pos); + } + } + } + // Reserved bit positions within an MSR value may get assigned meaning by hardware vendors in the future. + // For this reason we decide to have an "inherit" policy for these bits during profile generation. + let reserved_values = value & (!bits_accounted_for); + replacements |= reserved_values; + + permitted_msrs.insert(reg_addr.0); + adjustments.push((*reg_addr, FeatureMsrAdjustment { mask, replacements })); + } + + if entries_encountered != supported_feature_msrs.len() { + let unknown_register_address = supported_feature_msrs.iter().find(|entry| !processor_feature_msr_definitions.as_slice().iter().any(|(reg_addr, _)| reg_addr.0 == entry.index )).expect("We have checked that there should be at least one unknown supported MSR-based feature").index; + Err(anyhow!( + "CPU profile generation failed: The hardware and hypervisor supports MSR-based feature with register address:={unknown_register_address:#x}, but the CPU profile generation tool does not know what to do with this MSR. Please update the appropriate `MsrDefinitions` and try again." + ))?; + } + + for msr in permitted_architectural_msrs + .iter() + .chain(permitted_hypervisor_msrs) + .chain(permitted_hyperv_msrs) + { + if supported_msrs.contains(msr) { + let _ = permitted_msrs.insert(*msr); + } + } + + // Also check to see if there are any MSRs on the system that we are not aware off. In that case + // it might be a sign that this tool needs to update its definitions! + for msr in supported_msrs.difference(&permitted_msrs) { + let is_proc_feat_msr = processor_feature_msr_definitions + .as_slice() + .iter() + .any(|(reg_addr, _)| reg_addr.0 == *msr); + + let is_hypervisor_msr = forbidden_hypervisor_msrs.contains(msr); + + let is_architectural_msr = forbidden_architectural_msrs + .iter() + .any(|r| (r.0..=r.1).contains(msr)); + + let is_non_architectural_msr = non_architectural_msrs.contains(msr); + + if is_proc_feat_msr || is_hypervisor_msr || is_architectural_msr || is_non_architectural_msr + { + continue; + } + + // TODO: Make this a hard error before upstreaming + warn!( + "Encountered unknown MSR:={:#x} when generating CPU profile. This CPU profile generation tool might not be up-to-date", + *msr + ); + } + + let permitted_msrs: Vec = { + let mut permitted_msrs: Vec = permitted_msrs.into_iter().collect(); + permitted_msrs.sort(); + permitted_msrs.into_iter().map(RegisterAddress).collect() + }; + + let msr_profile_data = MsrProfileData { + hypervisor_type, + cpu_vendor, + adjustments, + permitted_msrs, + }; + + serde_json::to_writer_pretty(&mut msr_data_file, &msr_profile_data) + .context("Cpu profile generation failed: Could not serialize the generated MSR profile data to the given writer")?; + msr_data_file + .flush() + .context("CPU profile generation failed: Unable to flush MSR profile data")?; + write_license_file(msr_license_file, "MSR") +} + +fn write_license_file(mut license_file: impl Write, data_type: &str) -> anyhow::Result<()> { + let license_text = { + r#"SPDX-FileCopyrightText: 2025 Cyberus Technology GmbH + +SPDX-License-Identifier: Apache-2.0 +"# + }; + license_file + .write_all(license_text.as_bytes()) + .with_context(|| { + format!("CPU profile generation failed: Unable to write to {data_type} profile data license file") + })?; + license_file.flush().context(format!( + "CPU profile generation failed: Unable to flush {data_type} profile data license file" + )) +} /// Get as many of the supported CPUID entries from the hypervisor as possible. fn supported_cpuid(hypervisor: &dyn Hypervisor) -> anyhow::Result> { // Check for AMX compatibility. If this is supported we need to call arch_prctl before requesting the supported @@ -226,11 +542,16 @@ fn sort_entries(mut cpuid: Vec) -> Vec { }); cpuid } -/// Returns a `u32` where each bit between `first_bit_pos` and `last_bit_pos` is set (including both ends) and all other bits are 0. -fn bit_range_mask(first_bit_pos: u8, last_bit_pos: u8) -> u32 { - (first_bit_pos..=last_bit_pos).fold(0, |acc, next| acc | (1 << next)) -} +/// Returns a numeric value where each bit between `first_bit_pos` and `last_bit_pos` is set (including both ends) and all other bits are 0. +fn bit_range_mask(first_bit_pos: u8, last_bit_pos: u8) -> T +where + T: Shl, + T: BitOr, + T: From, +{ + (first_bit_pos..=last_bit_pos).fold(T::from(0_u8), |acc, next| acc | ((T::from(1_u8)) << next)) +} /// Returns a vector of exact parameter matches ((sub_leaf ..= sub_leaf), register_value) interleaved by /// the sub_leaf ranges specified by `param` that did not match any cpuid entry. fn extract_parameter_matches( diff --git a/arch/src/x86_64/cpu_profiles/skylake.json b/arch/src/x86_64/cpu_profiles/sapphire-rapids.cpuid.json similarity index 87% rename from arch/src/x86_64/cpu_profiles/skylake.json rename to arch/src/x86_64/cpu_profiles/sapphire-rapids.cpuid.json index ffd77fd00f..ae8bd9eab0 100644 --- a/arch/src/x86_64/cpu_profiles/skylake.json +++ b/arch/src/x86_64/cpu_profiles/sapphire-rapids.cpuid.json @@ -12,7 +12,7 @@ "register": "EAX" }, { - "replacements": "0x00000016", + "replacements": "0x00000020", "mask": "0x00000000" } ], @@ -68,7 +68,7 @@ "register": "EAX" }, { - "replacements": "0x00050654", + "replacements": "0x000806f8", "mask": "0x00000000" } ], @@ -97,7 +97,7 @@ }, { "replacements": "0x76fa3223", - "mask": "0x88000000" + "mask": "0x89000000" } ], [ @@ -110,7 +110,7 @@ "register": "EDX" }, { - "replacements": "0x078bfbff", + "replacements": "0x078bbb7f", "mask": "0x08000000" } ], @@ -628,7 +628,7 @@ "register": "EAX" }, { - "replacements": "0x00000000", + "replacements": "0x00000002", "mask": "0x00000000" } ], @@ -642,7 +642,7 @@ "register": "EBX" }, { - "replacements": "0xd19f07ab", + "replacements": "0xf1bf07ab", "mask": "0x00002040" } ], @@ -656,7 +656,7 @@ "register": "ECX" }, { - "replacements": "0x0000000c", + "replacements": "0x1b415f4e", "mask": "0x00000010" } ], @@ -670,7 +670,7 @@ "register": "EDX" }, { - "replacements": "0xa4000000", + "replacements": "0xa7c04010", "mask": "0x18000400" } ], @@ -684,7 +684,7 @@ "register": "EAX" }, { - "replacements": "0x00000000", + "replacements": "0x00001c30", "mask": "0x00000000" } ], @@ -740,7 +740,7 @@ "register": "EDX" }, { - "replacements": "0x00000000", + "replacements": "0x00000017", "mask": "0x00000000" } ], @@ -936,7 +936,7 @@ "register": "EAX" }, { - "replacements": "0x000002e7", + "replacements": "0x000602e7", "mask": "0x00000000" } ], @@ -992,7 +992,7 @@ "register": "EAX" }, { - "replacements": "0x0000000f", + "replacements": "0x0000001f", "mask": "0x00000000" } ], @@ -1085,7 +1085,7 @@ "leaf": "0xd", "sub_leaf": { "start": "0x3", - "end": "0x3" + "end": "0x4" }, "register": "EAX" }, @@ -1098,10 +1098,10 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x4", + "start": "0x3", "end": "0x4" }, - "register": "EAX" + "register": "EBX" }, { "replacements": "0x00000000", @@ -1113,9 +1113,9 @@ "leaf": "0xd", "sub_leaf": { "start": "0x3", - "end": "0x3" + "end": "0x4" }, - "register": "EBX" + "register": "ECX" }, { "replacements": "0x00000000", @@ -1126,10 +1126,10 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x4", + "start": "0x3", "end": "0x4" }, - "register": "EBX" + "register": "EDX" }, { "replacements": "0x00000000", @@ -1140,13 +1140,13 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x3", - "end": "0x3" + "start": "0x5", + "end": "0x5" }, - "register": "ECX" + "register": "EAX" }, { - "replacements": "0x00000000", + "replacements": "0x00000040", "mask": "0x00000000" } ], @@ -1154,13 +1154,13 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x4", - "end": "0x4" + "start": "0x6", + "end": "0x6" }, - "register": "ECX" + "register": "EAX" }, { - "replacements": "0x00000000", + "replacements": "0x00000200", "mask": "0x00000000" } ], @@ -1168,10 +1168,24 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x3", - "end": "0x3" + "start": "0x7", + "end": "0x7" }, - "register": "EDX" + "register": "EAX" + }, + { + "replacements": "0x00000400", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x8", + "end": "0x8" + }, + "register": "EAX" }, { "replacements": "0x00000000", @@ -1182,10 +1196,24 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x4", - "end": "0x4" + "start": "0x9", + "end": "0x9" }, - "register": "EDX" + "register": "EAX" + }, + { + "replacements": "0x00000008", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xa", + "end": "0xa" + }, + "register": "EAX" }, { "replacements": "0x00000000", @@ -1199,10 +1227,10 @@ "start": "0x5", "end": "0x5" }, - "register": "EAX" + "register": "EBX" }, { - "replacements": "0x00000040", + "replacements": "0x00000440", "mask": "0x00000000" } ], @@ -1213,10 +1241,10 @@ "start": "0x6", "end": "0x6" }, - "register": "EAX" + "register": "EBX" }, { - "replacements": "0x00000200", + "replacements": "0x00000480", "mask": "0x00000000" } ], @@ -1227,10 +1255,10 @@ "start": "0x7", "end": "0x7" }, - "register": "EAX" + "register": "EBX" }, { - "replacements": "0x00000400", + "replacements": "0x00000680", "mask": "0x00000000" } ], @@ -1241,7 +1269,7 @@ "start": "0x8", "end": "0x8" }, - "register": "EAX" + "register": "EBX" }, { "replacements": "0x00000000", @@ -1255,10 +1283,10 @@ "start": "0x9", "end": "0x9" }, - "register": "EAX" + "register": "EBX" }, { - "replacements": "0x00000008", + "replacements": "0x00000a80", "mask": "0x00000000" } ], @@ -1267,9 +1295,9 @@ "leaf": "0xd", "sub_leaf": { "start": "0xa", - "end": "0x3f" + "end": "0xa" }, - "register": "EAX" + "register": "EBX" }, { "replacements": "0x00000000", @@ -1283,10 +1311,10 @@ "start": "0x5", "end": "0x5" }, - "register": "EBX" + "register": "ECX" }, { - "replacements": "0x00000440", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1297,10 +1325,10 @@ "start": "0x6", "end": "0x6" }, - "register": "EBX" + "register": "ECX" }, { - "replacements": "0x00000480", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1311,10 +1339,10 @@ "start": "0x7", "end": "0x7" }, - "register": "EBX" + "register": "ECX" }, { - "replacements": "0x00000680", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1325,7 +1353,7 @@ "start": "0x8", "end": "0x8" }, - "register": "EBX" + "register": "ECX" }, { "replacements": "0x00000000", @@ -1339,10 +1367,10 @@ "start": "0x9", "end": "0x9" }, - "register": "EBX" + "register": "ECX" }, { - "replacements": "0x00000a80", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1351,7 +1379,35 @@ "leaf": "0xd", "sub_leaf": { "start": "0xa", - "end": "0x3f" + "end": "0xa" + }, + "register": "ECX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xb", + "end": "0xc" + }, + "register": "EAX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xb", + "end": "0xc" }, "register": "EBX" }, @@ -1364,8 +1420,8 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x5", - "end": "0x5" + "start": "0xb", + "end": "0xc" }, "register": "ECX" }, @@ -1378,10 +1434,10 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x6", - "end": "0x6" + "start": "0xb", + "end": "0xc" }, - "register": "ECX" + "register": "EDX" }, { "replacements": "0x00000000", @@ -1392,8 +1448,36 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x7", - "end": "0x7" + "start": "0xd", + "end": "0xd" + }, + "register": "EAX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xd", + "end": "0xd" + }, + "register": "EBX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xd", + "end": "0xd" }, "register": "ECX" }, @@ -1406,8 +1490,36 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x8", - "end": "0x8" + "start": "0xe", + "end": "0xe" + }, + "register": "EAX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xe", + "end": "0xe" + }, + "register": "EBX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xe", + "end": "0xe" }, "register": "ECX" }, @@ -1420,8 +1532,134 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x9", - "end": "0x9" + "start": "0xe", + "end": "0xe" + }, + "register": "EDX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xf", + "end": "0x10" + }, + "register": "EAX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x11", + "end": "0x11" + }, + "register": "EAX" + }, + { + "replacements": "0x00000040", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x12", + "end": "0x12" + }, + "register": "EAX" + }, + { + "replacements": "0x00002000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x13", + "end": "0x3f" + }, + "register": "EAX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xf", + "end": "0x10" + }, + "register": "EBX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x11", + "end": "0x11" + }, + "register": "EBX" + }, + { + "replacements": "0x00000ac0", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x12", + "end": "0x12" + }, + "register": "EBX" + }, + { + "replacements": "0x00000b00", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x13", + "end": "0x3f" + }, + "register": "EBX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xf", + "end": "0x10" }, "register": "ECX" }, @@ -1434,7 +1672,35 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0xa", + "start": "0x11", + "end": "0x11" + }, + "register": "ECX" + }, + { + "replacements": "0x00000002", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x12", + "end": "0x12" + }, + "register": "ECX" + }, + { + "replacements": "0x00000006", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x13", "end": "0x3f" }, "register": "ECX" @@ -1925,6 +2191,20 @@ "leaf": "0x18", "sub_leaf": { "start": "0x0", + "end": "0x0" + }, + "register": "EBX" + }, + { + "replacements": "0x00000000", + "mask": "0xffff070f" + } + ], + [ + { + "leaf": "0x18", + "sub_leaf": { + "start": "0x1", "end": "0xffffffff" }, "register": "EBX" @@ -1939,6 +2219,20 @@ "leaf": "0x18", "sub_leaf": { "start": "0x0", + "end": "0x0" + }, + "register": "ECX" + }, + { + "replacements": "0x00000000", + "mask": "0xffffffff" + } + ], + [ + { + "leaf": "0x18", + "sub_leaf": { + "start": "0x1", "end": "0xffffffff" }, "register": "ECX" @@ -1953,6 +2247,20 @@ "leaf": "0x18", "sub_leaf": { "start": "0x0", + "end": "0x0" + }, + "register": "EDX" + }, + { + "replacements": "0x00000000", + "mask": "0x03ffc1ff" + } + ], + [ + { + "leaf": "0x18", + "sub_leaf": { + "start": "0x1", "end": "0xffffffff" }, "register": "EDX" @@ -2014,7 +2322,7 @@ "register": "EAX" }, { - "replacements": "0x00000000", + "replacements": "0x00000001", "mask": "0x00000000" } ], @@ -2028,7 +2336,7 @@ "register": "EAX" }, { - "replacements": "0x00000000", + "replacements": "0x04002000", "mask": "0x00000000" } ], @@ -2042,7 +2350,7 @@ "register": "EBX" }, { - "replacements": "0x00000000", + "replacements": "0x00080040", "mask": "0x00000000" } ], @@ -2056,7 +2364,7 @@ "register": "ECX" }, { - "replacements": "0x00000000", + "replacements": "0x00000010", "mask": "0x00000000" } ], @@ -2084,7 +2392,7 @@ "register": "EBX" }, { - "replacements": "0x00000000", + "replacements": "0x00004010", "mask": "0x00000000" } ], @@ -2107,6 +2415,20 @@ "leaf": "0x1f", "sub_leaf": { "start": "0x0", + "end": "0x0" + }, + "register": "EAX" + }, + { + "replacements": "0x00000000", + "mask": "0x0000001f" + } + ], + [ + { + "leaf": "0x1f", + "sub_leaf": { + "start": "0x1", "end": "0xffffffff" }, "register": "EAX" @@ -2121,6 +2443,20 @@ "leaf": "0x1f", "sub_leaf": { "start": "0x0", + "end": "0x0" + }, + "register": "EBX" + }, + { + "replacements": "0x00000000", + "mask": "0x0000ffff" + } + ], + [ + { + "leaf": "0x1f", + "sub_leaf": { + "start": "0x1", "end": "0xffffffff" }, "register": "EBX" @@ -2135,6 +2471,20 @@ "leaf": "0x1f", "sub_leaf": { "start": "0x0", + "end": "0x0" + }, + "register": "ECX" + }, + { + "replacements": "0x00000000", + "mask": "0x0000ffff" + } + ], + [ + { + "leaf": "0x1f", + "sub_leaf": { + "start": "0x1", "end": "0xffffffff" }, "register": "ECX" @@ -2149,6 +2499,20 @@ "leaf": "0x1f", "sub_leaf": { "start": "0x0", + "end": "0x0" + }, + "register": "EDX" + }, + { + "replacements": "0x00000000", + "mask": "0xffffffff" + } + ], + [ + { + "leaf": "0x1f", + "sub_leaf": { + "start": "0x1", "end": "0xffffffff" }, "register": "EDX" @@ -2546,7 +2910,7 @@ "register": "EBX" }, { - "replacements": "0x6b53206c", + "replacements": "0x6153206c", "mask": "0x00000000" } ], @@ -2560,7 +2924,7 @@ "register": "ECX" }, { - "replacements": "0x6b616c79", + "replacements": "0x69687070", "mask": "0x00000000" } ], @@ -2574,7 +2938,7 @@ "register": "EDX" }, { - "replacements": "0x00000065", + "replacements": "0x52206572", "mask": "0x00000000" } ], @@ -2588,7 +2952,7 @@ "register": "EAX" }, { - "replacements": "0x00000000", + "replacements": "0x64697061", "mask": "0x00000000" } ], @@ -2602,7 +2966,7 @@ "register": "EBX" }, { - "replacements": "0x00000000", + "replacements": "0x00000073", "mask": "0x00000000" } ], @@ -2813,7 +3177,7 @@ }, { "replacements": "0x00000000", - "mask": "0x0103feff" + "mask": "0x0103fefe" } ], [ diff --git a/arch/src/x86_64/cpu_profiles/sapphire-rapids.json.license b/arch/src/x86_64/cpu_profiles/sapphire-rapids.cpuid.json.license similarity index 59% rename from arch/src/x86_64/cpu_profiles/sapphire-rapids.json.license rename to arch/src/x86_64/cpu_profiles/sapphire-rapids.cpuid.json.license index 7f7e3b5e1b..579657c531 100644 --- a/arch/src/x86_64/cpu_profiles/sapphire-rapids.json.license +++ b/arch/src/x86_64/cpu_profiles/sapphire-rapids.cpuid.json.license @@ -1,3 +1,3 @@ SPDX-FileCopyrightText: 2025 Cyberus Technology GmbH -SPDX-License-Identifier: Apache-2.0 +SPDX-License-Identifier: Apache-2.0 diff --git a/arch/src/x86_64/cpu_profiles/sapphire-rapids.msr.json b/arch/src/x86_64/cpu_profiles/sapphire-rapids.msr.json new file mode 100644 index 0000000000..8445c7af84 --- /dev/null +++ b/arch/src/x86_64/cpu_profiles/sapphire-rapids.msr.json @@ -0,0 +1,175 @@ +{ + "cpu_vendor": "Intel", + "hypervisor_type": "Kvm", + "adjustments": [ + [ + "0x8b", + { + "mask": "0xffffffff00000000", + "replacements": "0x0000000000000000" + } + ], + [ + "0x10a", + { + "mask": "0x400000001d10e170", + "replacements": "0x000000000008000b" + } + ], + [ + "0x480", + { + "mask": "0x0000000000000000", + "replacements": "0x00d8100011e57ed0" + } + ], + [ + "0x485", + { + "mask": "0x000000000000001f", + "replacements": "0x0000000020000060" + } + ], + [ + "0x486", + { + "mask": "0x0000000000000000", + "replacements": "0x0000000080000021" + } + ], + [ + "0x488", + { + "mask": "0x0000000000000000", + "replacements": "0x0000000000002000" + } + ], + [ + "0x48a", + { + "mask": "0x0000000000000000", + "replacements": "0x0000000000000032" + } + ], + [ + "0x48b", + { + "mask": "0x0000000000000000", + "replacements": "0x06137bff00000000" + } + ], + [ + "0x48c", + { + "mask": "0x0000000000000000", + "replacements": "0x00000f01063340c1" + } + ], + [ + "0x48d", + { + "mask": "0x0000000000000000", + "replacements": "0x000000ff00000016" + } + ], + [ + "0x48e", + { + "mask": "0x0000000000000000", + "replacements": "0xfff9fffe04006172" + } + ], + [ + "0x48f", + { + "mask": "0x0000000000000000", + "replacements": "0x007fefff00036dfb" + } + ], + [ + "0x490", + { + "mask": "0x0000000000000000", + "replacements": "0x0000d3ff000011fb" + } + ], + [ + "0x491", + { + "mask": "0x0000000000000000", + "replacements": "0x0000000000000001" + } + ] + ], + "permitted_msrs": [ + "0x10", + "0x3a", + "0x3b", + "0x48", + "0x8b", + "0x10a", + "0x174", + "0x175", + "0x176", + "0x1a0", + "0x1c4", + "0x1c5", + "0x277", + "0x480", + "0x481", + "0x482", + "0x483", + "0x484", + "0x485", + "0x486", + "0x487", + "0x488", + "0x489", + "0x48a", + "0x48b", + "0x48c", + "0x48d", + "0x48e", + "0x48f", + "0x490", + "0x491", + "0x6e0", + "0x40000000", + "0x40000001", + "0x40000002", + "0x40000003", + "0x40000010", + "0x40000020", + "0x40000021", + "0x40000022", + "0x40000023", + "0x40000073", + "0x40000080", + "0x400000b0", + "0x400000f1", + "0x400000f2", + "0x400000f3", + "0x400000f4", + "0x400000f5", + "0x40000100", + "0x40000101", + "0x40000102", + "0x40000103", + "0x40000104", + "0x40000105", + "0x4b564d00", + "0x4b564d01", + "0x4b564d02", + "0x4b564d03", + "0x4b564d04", + "0x4b564d05", + "0x4b564d06", + "0x4b564d07", + "0xc0000081", + "0xc0000082", + "0xc0000083", + "0xc0000084", + "0xc0000102", + "0xc0000103" + ] +} \ No newline at end of file diff --git a/arch/src/x86_64/cpu_profiles/skylake.json.license b/arch/src/x86_64/cpu_profiles/sapphire-rapids.msr.json.license similarity index 59% rename from arch/src/x86_64/cpu_profiles/skylake.json.license rename to arch/src/x86_64/cpu_profiles/sapphire-rapids.msr.json.license index 7f7e3b5e1b..579657c531 100644 --- a/arch/src/x86_64/cpu_profiles/skylake.json.license +++ b/arch/src/x86_64/cpu_profiles/sapphire-rapids.msr.json.license @@ -1,3 +1,3 @@ SPDX-FileCopyrightText: 2025 Cyberus Technology GmbH -SPDX-License-Identifier: Apache-2.0 +SPDX-License-Identifier: Apache-2.0 diff --git a/arch/src/x86_64/cpu_profiles/sapphire-rapids.json b/arch/src/x86_64/cpu_profiles/skylake.cpuid.json similarity index 94% rename from arch/src/x86_64/cpu_profiles/sapphire-rapids.json rename to arch/src/x86_64/cpu_profiles/skylake.cpuid.json index ecbf7bc28c..40f0dac88b 100644 --- a/arch/src/x86_64/cpu_profiles/sapphire-rapids.json +++ b/arch/src/x86_64/cpu_profiles/skylake.cpuid.json @@ -12,7 +12,7 @@ "register": "EAX" }, { - "replacements": "0x00000020", + "replacements": "0x00000016", "mask": "0x00000000" } ], @@ -68,7 +68,7 @@ "register": "EAX" }, { - "replacements": "0x000806f8", + "replacements": "0x00050654", "mask": "0x00000000" } ], @@ -97,7 +97,7 @@ }, { "replacements": "0x76fa3223", - "mask": "0x88000000" + "mask": "0x89000000" } ], [ @@ -110,7 +110,7 @@ "register": "EDX" }, { - "replacements": "0x078bfbff", + "replacements": "0x078bbb7f", "mask": "0x08000000" } ], @@ -628,7 +628,7 @@ "register": "EAX" }, { - "replacements": "0x00000002", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -642,7 +642,7 @@ "register": "EBX" }, { - "replacements": "0xf1bf07ab", + "replacements": "0xd19f07ab", "mask": "0x00002040" } ], @@ -656,7 +656,7 @@ "register": "ECX" }, { - "replacements": "0x1b415f6e", + "replacements": "0x0000000c", "mask": "0x00000010" } ], @@ -670,7 +670,7 @@ "register": "EDX" }, { - "replacements": "0xa7c04010", + "replacements": "0xa4000000", "mask": "0x18000400" } ], @@ -684,7 +684,7 @@ "register": "EAX" }, { - "replacements": "0x00001c30", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -740,7 +740,7 @@ "register": "EDX" }, { - "replacements": "0x00000017", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -936,7 +936,7 @@ "register": "EAX" }, { - "replacements": "0x000602e7", + "replacements": "0x000002e7", "mask": "0x00000000" } ], @@ -992,7 +992,7 @@ "register": "EAX" }, { - "replacements": "0x0000001f", + "replacements": "0x0000000f", "mask": "0x00000000" } ], @@ -1085,6 +1085,20 @@ "leaf": "0xd", "sub_leaf": { "start": "0x3", + "end": "0x3" + }, + "register": "EAX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x4", "end": "0x4" }, "register": "EAX" @@ -1099,6 +1113,20 @@ "leaf": "0xd", "sub_leaf": { "start": "0x3", + "end": "0x3" + }, + "register": "EBX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x4", "end": "0x4" }, "register": "EBX" @@ -1113,6 +1141,20 @@ "leaf": "0xd", "sub_leaf": { "start": "0x3", + "end": "0x3" + }, + "register": "ECX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x4", "end": "0x4" }, "register": "ECX" @@ -1127,6 +1169,20 @@ "leaf": "0xd", "sub_leaf": { "start": "0x3", + "end": "0x3" + }, + "register": "EDX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x4", "end": "0x4" }, "register": "EDX" @@ -1211,7 +1267,7 @@ "leaf": "0xd", "sub_leaf": { "start": "0xa", - "end": "0x10" + "end": "0xa" }, "register": "EAX" }, @@ -1224,13 +1280,13 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x11", - "end": "0x11" + "start": "0x5", + "end": "0x5" }, - "register": "EAX" + "register": "EBX" }, { - "replacements": "0x00000040", + "replacements": "0x00000440", "mask": "0x00000000" } ], @@ -1238,13 +1294,13 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x12", - "end": "0x12" + "start": "0x6", + "end": "0x6" }, - "register": "EAX" + "register": "EBX" }, { - "replacements": "0x00002000", + "replacements": "0x00000480", "mask": "0x00000000" } ], @@ -1252,10 +1308,52 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x13", - "end": "0x3f" + "start": "0x7", + "end": "0x7" }, - "register": "EAX" + "register": "EBX" + }, + { + "replacements": "0x00000680", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x8", + "end": "0x8" + }, + "register": "EBX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0x9", + "end": "0x9" + }, + "register": "EBX" + }, + { + "replacements": "0x00000a80", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xa", + "end": "0xa" + }, + "register": "EBX" }, { "replacements": "0x00000000", @@ -1269,10 +1367,10 @@ "start": "0x5", "end": "0x5" }, - "register": "EBX" + "register": "ECX" }, { - "replacements": "0x00000440", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1283,10 +1381,10 @@ "start": "0x6", "end": "0x6" }, - "register": "EBX" + "register": "ECX" }, { - "replacements": "0x00000480", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1297,10 +1395,10 @@ "start": "0x7", "end": "0x7" }, - "register": "EBX" + "register": "ECX" }, { - "replacements": "0x00000680", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1311,7 +1409,7 @@ "start": "0x8", "end": "0x8" }, - "register": "EBX" + "register": "ECX" }, { "replacements": "0x00000000", @@ -1325,10 +1423,10 @@ "start": "0x9", "end": "0x9" }, - "register": "EBX" + "register": "ECX" }, { - "replacements": "0x00000a80", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1337,9 +1435,9 @@ "leaf": "0xd", "sub_leaf": { "start": "0xa", - "end": "0x10" + "end": "0xa" }, - "register": "EBX" + "register": "ECX" }, { "replacements": "0x00000000", @@ -1350,13 +1448,13 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x11", - "end": "0x11" + "start": "0xb", + "end": "0xc" }, - "register": "EBX" + "register": "EAX" }, { - "replacements": "0x00000ac0", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1364,13 +1462,13 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x12", - "end": "0x12" + "start": "0xb", + "end": "0xc" }, "register": "EBX" }, { - "replacements": "0x00000b00", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1378,10 +1476,10 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x13", - "end": "0x3f" + "start": "0xb", + "end": "0xc" }, - "register": "EBX" + "register": "ECX" }, { "replacements": "0x00000000", @@ -1392,10 +1490,10 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x5", - "end": "0x5" + "start": "0xb", + "end": "0xc" }, - "register": "ECX" + "register": "EDX" }, { "replacements": "0x00000000", @@ -1406,10 +1504,10 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x6", - "end": "0x6" + "start": "0xd", + "end": "0xd" }, - "register": "ECX" + "register": "EAX" }, { "replacements": "0x00000000", @@ -1420,10 +1518,10 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x7", - "end": "0x7" + "start": "0xd", + "end": "0xd" }, - "register": "ECX" + "register": "EBX" }, { "replacements": "0x00000000", @@ -1434,8 +1532,8 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x8", - "end": "0x8" + "start": "0xd", + "end": "0xd" }, "register": "ECX" }, @@ -1448,10 +1546,10 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x9", - "end": "0x9" + "start": "0xe", + "end": "0xe" }, - "register": "ECX" + "register": "EAX" }, { "replacements": "0x00000000", @@ -1462,10 +1560,10 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0xa", - "end": "0x10" + "start": "0xe", + "end": "0xe" }, - "register": "ECX" + "register": "EBX" }, { "replacements": "0x00000000", @@ -1476,13 +1574,13 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x11", - "end": "0x11" + "start": "0xe", + "end": "0xe" }, "register": "ECX" }, { - "replacements": "0x00000002", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1490,13 +1588,27 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x12", - "end": "0x12" + "start": "0xe", + "end": "0xe" }, - "register": "ECX" + "register": "EDX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xf", + "end": "0x3f" + }, + "register": "EAX" }, { - "replacements": "0x00000006", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -1504,7 +1616,21 @@ { "leaf": "0xd", "sub_leaf": { - "start": "0x13", + "start": "0xf", + "end": "0x3f" + }, + "register": "EBX" + }, + { + "replacements": "0x00000000", + "mask": "0x00000000" + } + ], + [ + { + "leaf": "0xd", + "sub_leaf": { + "start": "0xf", "end": "0x3f" }, "register": "ECX" @@ -1995,20 +2121,6 @@ "leaf": "0x18", "sub_leaf": { "start": "0x0", - "end": "0x0" - }, - "register": "EBX" - }, - { - "replacements": "0x00000000", - "mask": "0xffff070f" - } - ], - [ - { - "leaf": "0x18", - "sub_leaf": { - "start": "0x1", "end": "0xffffffff" }, "register": "EBX" @@ -2023,20 +2135,6 @@ "leaf": "0x18", "sub_leaf": { "start": "0x0", - "end": "0x0" - }, - "register": "ECX" - }, - { - "replacements": "0x00000000", - "mask": "0xffffffff" - } - ], - [ - { - "leaf": "0x18", - "sub_leaf": { - "start": "0x1", "end": "0xffffffff" }, "register": "ECX" @@ -2051,20 +2149,6 @@ "leaf": "0x18", "sub_leaf": { "start": "0x0", - "end": "0x0" - }, - "register": "EDX" - }, - { - "replacements": "0x00000000", - "mask": "0x03ffc1ff" - } - ], - [ - { - "leaf": "0x18", - "sub_leaf": { - "start": "0x1", "end": "0xffffffff" }, "register": "EDX" @@ -2126,7 +2210,7 @@ "register": "EAX" }, { - "replacements": "0x00000001", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -2140,7 +2224,7 @@ "register": "EAX" }, { - "replacements": "0x04002000", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -2154,7 +2238,7 @@ "register": "EBX" }, { - "replacements": "0x00080040", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -2168,7 +2252,7 @@ "register": "ECX" }, { - "replacements": "0x00000010", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -2196,7 +2280,7 @@ "register": "EBX" }, { - "replacements": "0x00004010", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -2219,20 +2303,6 @@ "leaf": "0x1f", "sub_leaf": { "start": "0x0", - "end": "0x0" - }, - "register": "EAX" - }, - { - "replacements": "0x00000000", - "mask": "0x0000001f" - } - ], - [ - { - "leaf": "0x1f", - "sub_leaf": { - "start": "0x1", "end": "0xffffffff" }, "register": "EAX" @@ -2247,20 +2317,6 @@ "leaf": "0x1f", "sub_leaf": { "start": "0x0", - "end": "0x0" - }, - "register": "EBX" - }, - { - "replacements": "0x00000000", - "mask": "0x0000ffff" - } - ], - [ - { - "leaf": "0x1f", - "sub_leaf": { - "start": "0x1", "end": "0xffffffff" }, "register": "EBX" @@ -2275,20 +2331,6 @@ "leaf": "0x1f", "sub_leaf": { "start": "0x0", - "end": "0x0" - }, - "register": "ECX" - }, - { - "replacements": "0x00000000", - "mask": "0x0000ffff" - } - ], - [ - { - "leaf": "0x1f", - "sub_leaf": { - "start": "0x1", "end": "0xffffffff" }, "register": "ECX" @@ -2303,20 +2345,6 @@ "leaf": "0x1f", "sub_leaf": { "start": "0x0", - "end": "0x0" - }, - "register": "EDX" - }, - { - "replacements": "0x00000000", - "mask": "0xffffffff" - } - ], - [ - { - "leaf": "0x1f", - "sub_leaf": { - "start": "0x1", "end": "0xffffffff" }, "register": "EDX" @@ -2714,7 +2742,7 @@ "register": "EBX" }, { - "replacements": "0x6153206c", + "replacements": "0x6b53206c", "mask": "0x00000000" } ], @@ -2728,7 +2756,7 @@ "register": "ECX" }, { - "replacements": "0x69687070", + "replacements": "0x6b616c79", "mask": "0x00000000" } ], @@ -2742,7 +2770,7 @@ "register": "EDX" }, { - "replacements": "0x52206572", + "replacements": "0x00000065", "mask": "0x00000000" } ], @@ -2756,7 +2784,7 @@ "register": "EAX" }, { - "replacements": "0x64697061", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -2770,7 +2798,7 @@ "register": "EBX" }, { - "replacements": "0x00000073", + "replacements": "0x00000000", "mask": "0x00000000" } ], @@ -2981,7 +3009,7 @@ }, { "replacements": "0x00000000", - "mask": "0x0103feff" + "mask": "0x0103fefe" } ], [ diff --git a/arch/src/x86_64/cpu_profiles/skylake.cpuid.json.license b/arch/src/x86_64/cpu_profiles/skylake.cpuid.json.license new file mode 100644 index 0000000000..579657c531 --- /dev/null +++ b/arch/src/x86_64/cpu_profiles/skylake.cpuid.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2025 Cyberus Technology GmbH + +SPDX-License-Identifier: Apache-2.0 diff --git a/arch/src/x86_64/cpu_profiles/skylake.msr.json b/arch/src/x86_64/cpu_profiles/skylake.msr.json new file mode 100644 index 0000000000..7a0ff5d78b --- /dev/null +++ b/arch/src/x86_64/cpu_profiles/skylake.msr.json @@ -0,0 +1,173 @@ +{ + "cpu_vendor": "Intel", + "hypervisor_type": "Kvm", + "adjustments": [ + [ + "0x8b", + { + "mask": "0xffffffff00000000", + "replacements": "0x0000000000000000" + } + ], + [ + "0x10a", + { + "mask": "0x400000001d10e170", + "replacements": "0x000000000000000c" + } + ], + [ + "0x480", + { + "mask": "0x0000000000000000", + "replacements": "0x00d8100011e57ed0" + } + ], + [ + "0x485", + { + "mask": "0x000000000000001f", + "replacements": "0x0000000020000060" + } + ], + [ + "0x486", + { + "mask": "0x0000000000000000", + "replacements": "0x0000000080000021" + } + ], + [ + "0x488", + { + "mask": "0x0000000000000000", + "replacements": "0x0000000000002000" + } + ], + [ + "0x48a", + { + "mask": "0x0000000000000000", + "replacements": "0x0000000000000032" + } + ], + [ + "0x48b", + { + "mask": "0x0000000000000000", + "replacements": "0x02137bff00000000" + } + ], + [ + "0x48c", + { + "mask": "0x0000000000000000", + "replacements": "0x00000f0106334041" + } + ], + [ + "0x48d", + { + "mask": "0x0000000000000000", + "replacements": "0x000000ff00000016" + } + ], + [ + "0x48e", + { + "mask": "0x0000000000000000", + "replacements": "0xfff9fffe04006172" + } + ], + [ + "0x48f", + { + "mask": "0x0000000000000000", + "replacements": "0x007fefff00036dfb" + } + ], + [ + "0x490", + { + "mask": "0x0000000000000000", + "replacements": "0x0000d3ff000011fb" + } + ], + [ + "0x491", + { + "mask": "0x0000000000000000", + "replacements": "0x0000000000000001" + } + ] + ], + "permitted_msrs": [ + "0x10", + "0x3a", + "0x3b", + "0x48", + "0x8b", + "0x10a", + "0x174", + "0x175", + "0x176", + "0x1a0", + "0x277", + "0x480", + "0x481", + "0x482", + "0x483", + "0x484", + "0x485", + "0x486", + "0x487", + "0x488", + "0x489", + "0x48a", + "0x48b", + "0x48c", + "0x48d", + "0x48e", + "0x48f", + "0x490", + "0x491", + "0x6e0", + "0x40000000", + "0x40000001", + "0x40000002", + "0x40000003", + "0x40000010", + "0x40000020", + "0x40000021", + "0x40000022", + "0x40000023", + "0x40000073", + "0x40000080", + "0x400000b0", + "0x400000f1", + "0x400000f2", + "0x400000f3", + "0x400000f4", + "0x400000f5", + "0x40000100", + "0x40000101", + "0x40000102", + "0x40000103", + "0x40000104", + "0x40000105", + "0x4b564d00", + "0x4b564d01", + "0x4b564d02", + "0x4b564d03", + "0x4b564d04", + "0x4b564d05", + "0x4b564d06", + "0x4b564d07", + "0xc0000081", + "0xc0000082", + "0xc0000083", + "0xc0000084", + "0xc0000102", + "0xc0000103" + ] +} \ No newline at end of file diff --git a/arch/src/x86_64/cpu_profiles/skylake.msr.json.license b/arch/src/x86_64/cpu_profiles/skylake.msr.json.license new file mode 100644 index 0000000000..579657c531 --- /dev/null +++ b/arch/src/x86_64/cpu_profiles/skylake.msr.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2025 Cyberus Technology GmbH + +SPDX-License-Identifier: Apache-2.0 diff --git a/arch/src/x86_64/cpuid_definitions/intel.rs b/arch/src/x86_64/cpuid_definitions/intel.rs index d3d1e1fa24..8becc6b716 100644 --- a/arch/src/x86_64/cpuid_definitions/intel.rs +++ b/arch/src/x86_64/cpuid_definitions/intel.rs @@ -39,7 +39,7 @@ use super::{ /// a few of the short names and descriptions to be more inline with what is written in the /// aforementioned Intel manual. Finally we decided on a [`ProfilePolicy`] to be set for every /// single [`ValueDefinition`] and manually appended those. -pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { +pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<167> = const { CpuidDefinitions([ // ========================================================================================= // Basic CPUID Information @@ -332,7 +332,7 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { short: "tsc_deadline_timer", description: "APIC timer one-shot operation", bits_range: (24, 24), - policy: ProfilePolicy::Static(0), + policy: ProfilePolicy::Passthrough, }, ValueDefinition { short: "aes", @@ -432,7 +432,7 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { short: "mce", description: "Machine Check Exception", bits_range: (7, 7), - policy: ProfilePolicy::Inherit, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "cx8", @@ -469,7 +469,7 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { short: "mca", description: "Machine Check Architecture", bits_range: (14, 14), - policy: ProfilePolicy::Inherit, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "cmov", @@ -1430,11 +1430,12 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { bits_range: (4, 4), policy: ProfilePolicy::Passthrough, }, + // TODO: Revisit this decision. Setting this to 0 for now in order to be compatible with QEMU ValueDefinition { short: "waitpkg", description: "WAITPKG instructions", bits_range: (5, 5), - policy: ProfilePolicy::Inherit, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "avx512_vbmi2", @@ -1446,7 +1447,7 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { short: "cet_ss", description: "CET shadow stack features", bits_range: (7, 7), - policy: ProfilePolicy::Inherit, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "gfni", @@ -1668,7 +1669,7 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { short: "ibt", description: "CET indirect branch tracking", bits_range: (20, 20), - policy: ProfilePolicy::Inherit, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "amx_bf16", @@ -1709,6 +1710,10 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { policy: ProfilePolicy::Passthrough, }, // MSR related + // + // TODO: Is passthrough correct? + // If this bit is set then MSR IA32_FLUSH_CMD + // becomes available, otherwise it is not. ValueDefinition { short: "flush_l1d", description: "FLUSH L1D cache: IA32_FLUSH_CMD MSR", @@ -2005,7 +2010,7 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { short: "cet_sss", description: "CET supervisor shadow stacks safe to use", bits_range: (18, 18), - policy: ProfilePolicy::Inherit, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "avx10", @@ -2392,8 +2397,32 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { }, ValueDefinition { short: "xcr0_ia32_xss_bits", - description: "XCR0.IA32_XSS (bit 10 - 16) used for IA32_XSS", - bits_range: (10, 16), + description: "XCR0.IA32_XSS (bit 10) used for IA32_XSS", + bits_range: (10, 10), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "xcr0_ia32_xss_cet", + description: "XCR0.IA32_XSS (bits 11 - 12) used for IA32_XSS", + bits_range: (11, 12), + policy: ProfilePolicy::Static(0), + }, + ValueDefinition { + short: "xcr0_ia32_xss_bits", + description: "XCR0.IA32_XSS (bit 13) used for IA32_XSS", + bits_range: (13, 13), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "xcr0_ia32_xss_UINTR", + description: "XCR0.IA32_XSS (bit 14) used for UINTR in IA32_XSS", + bits_range: (14, 14), + policy: ProfilePolicy::Static(0), + }, + ValueDefinition { + short: "xcr0_ia32_xss_bits_15_16", + description: "XCR0.IA32_XSS (bit 15 - 16) used for IA32_XSS", + bits_range: (15, 16), policy: ProfilePolicy::Inherit, }, // NOTE: AMX currently requires opt-in, even for the host CPU profile. We still inherit this value for profiles and modify this value at runtime if AMX is not enabled by the user. @@ -2550,13 +2579,13 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { short: "xss_cet_u", description: "CET user state, supported", bits_range: (11, 11), - policy: ProfilePolicy::Inherit, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "xss_cet_p", description: "CET supervisor state, supported", bits_range: (12, 12), - policy: ProfilePolicy::Inherit, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "xss_hdc", @@ -2568,7 +2597,7 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { short: "xss_uintr", description: "UINTR state, supported", bits_range: (14, 14), - policy: ProfilePolicy::Inherit, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "xss_lbr", @@ -2719,12 +2748,224 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { policy: ProfilePolicy::Static(0), }]), ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(5, 10), + register: CpuidReg::EAX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "xsave_sz", + description: "Size of save area for subleaf-N feature, in bytes", + bits_range: (0, 31), + policy: ProfilePolicy::Inherit, + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(5, 10), + register: CpuidReg::EBX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "xsave_offset", + description: "Offset of save area for subleaf-N feature, in bytes", + bits_range: (0, 31), + policy: ProfilePolicy::Inherit, + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(5, 10), + register: CpuidReg::ECX, + }, + ValueDefinitions::new(&[ + ValueDefinition { + short: "is_xss_bit", + description: "Subleaf N describes an XSS bit, otherwise XCR0 bit", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "compacted_xsave_64byte_aligned", + description: "When compacted, subleaf-N feature XSAVE area is 64-byte aligned", + bits_range: (1, 1), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "xfd_faulting", + description: "Indicates support for xfd faulting", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit, + }, + ]), + ), + // We leave CET out of CPU profiles for the time being + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(11, 12), + register: CpuidReg::EAX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "0xd-11-12-eax-cet-zero", + description: "This leaf has been zeroed out because CET state components are disabled", + bits_range: (0, 31), + policy: ProfilePolicy::Static(0), + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(11, 12), + register: CpuidReg::EBX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "0xd-11-12-ebx-cet-zero", + description: "This leaf has been zeroed out because CET state components are disabled", + bits_range: (0, 31), + policy: ProfilePolicy::Static(0), + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(11, 12), + register: CpuidReg::ECX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "0xd-11-12-ecx-cet-zero", + description: "This leaf has been zeroed out because CET state components are disabled", + bits_range: (0, 31), + policy: ProfilePolicy::Static(0), + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(11, 12), + register: CpuidReg::EDX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "0xd-11-12-edx-cet-zero", + description: "This leaf has been zeroed out because CET state components are disabled", + bits_range: (0, 31), + policy: ProfilePolicy::Static(0), + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(13, 13), + register: CpuidReg::EAX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "xsave_sz", + description: "Size of save area for subleaf-N feature, in bytes", + bits_range: (0, 31), + policy: ProfilePolicy::Inherit, + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(13, 13), + register: CpuidReg::EBX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "xsave_offset", + description: "Offset of save area for subleaf-N feature, in bytes", + bits_range: (0, 31), + policy: ProfilePolicy::Inherit, + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(13, 13), + register: CpuidReg::ECX, + }, + ValueDefinitions::new(&[ + ValueDefinition { + short: "is_xss_bit", + description: "Subleaf N describes an XSS bit, otherwise XCR0 bit", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "compacted_xsave_64byte_aligned", + description: "When compacted, subleaf-N feature XSAVE area is 64-byte aligned", + bits_range: (1, 1), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "xfd_faulting", + description: "Indicates support for xfd faulting", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit, + }, + ]), + ), + // We decided to disable UINTR for CPU profiles, hence we zero out these sub-leaves + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(14, 14), + register: CpuidReg::EAX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "0xd-eax-uintr-zero", + description: "This leaf has been zeroed out because UINTR state components are disabled", + bits_range: (0, 31), + policy: ProfilePolicy::Static(0), + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(14, 14), + register: CpuidReg::EBX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "0xd-ebx-uintr-zero", + description: "This leaf has been zeroed out because UINTR state components are disabled", + bits_range: (0, 31), + policy: ProfilePolicy::Static(0), + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(14, 14), + register: CpuidReg::ECX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "0xd-ecx-uintr-zero", + description: "This leaf has been zeroed out because UINTR state components are disabled", + bits_range: (0, 31), + policy: ProfilePolicy::Static(0), + }]), + ), + ( + Parameters { + leaf: 0xd, + sub_leaf: RangeInclusive::new(14, 14), + register: CpuidReg::EDX, + }, + ValueDefinitions::new(&[ValueDefinition { + short: "0xd-edx-uintr-zero", + description: "This leaf has been zeroed out because UINTR state components are disabled", + bits_range: (0, 31), + policy: ProfilePolicy::Static(0), + }]), + ), // NOTE: Sub-leaves 17 & 18 are AMX related and we will alter the adjustments corresponding to // the policy declared here at runtime for those values. ( Parameters { leaf: 0xd, - sub_leaf: RangeInclusive::new(5, 63), + sub_leaf: RangeInclusive::new(15, 63), register: CpuidReg::EAX, }, ValueDefinitions::new(&[ValueDefinition { @@ -2737,7 +2978,7 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { ( Parameters { leaf: 0xd, - sub_leaf: RangeInclusive::new(5, 63), + sub_leaf: RangeInclusive::new(15, 63), register: CpuidReg::EBX, }, ValueDefinitions::new(&[ValueDefinition { @@ -2750,7 +2991,7 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { ( Parameters { leaf: 0xd, - sub_leaf: RangeInclusive::new(5, 63), + sub_leaf: RangeInclusive::new(15, 63), register: CpuidReg::ECX, }, ValueDefinitions::new(&[ @@ -4777,3 +5018,17 @@ pub static INTEL_CPUID_DEFINITIONS: CpuidDefinitions<153> = const { ), ]) }; + +/// Compile time check that the given `BIT` in the CPUID output register specified by `params` is not +/// declared to be overwritten by `0` for non-host CPU profiles. +pub const fn assert_not_denied_cpuid_feature(params: &Parameters) { + if let Some(defs) = INTEL_CPUID_DEFINITIONS.get(params) + && let Some(def) = defs.find_bit::() + { + assert!(!matches!(def.policy, ProfilePolicy::Static(0))); + } else { + panic!("Unable to lookup CPUID value definition with the given parameters and feature bit"); + } +} + +// TODO: Also include assert_denied_cpuid_feature diff --git a/arch/src/x86_64/cpuid_definitions/kvm.rs b/arch/src/x86_64/cpuid_definitions/kvm.rs index 3282e04222..ec7be3c472 100644 --- a/arch/src/x86_64/cpuid_definitions/kvm.rs +++ b/arch/src/x86_64/cpuid_definitions/kvm.rs @@ -82,11 +82,12 @@ pub const KVM_CPUID_DEFINITIONS: CpuidDefinitions<6> = const { register: CpuidReg::EAX, }, ValueDefinitions::new(&[ + // NOTE: This is deprecated and KVM now prefers CLOCKSOURCE2 ValueDefinition { short: "kvm_feature_clocksource", description: "kvmclock available at MSRs 0x11 and 0x12", bits_range: (0, 0), - policy: ProfilePolicy::Passthrough, + policy: ProfilePolicy::Static(0), }, ValueDefinition { short: "kvm_feature_nop_io_delay", @@ -207,3 +208,17 @@ pub const KVM_CPUID_DEFINITIONS: CpuidDefinitions<6> = const { ), ]) }; + +/// Compile time check that the given `BIT` in the CPUID output register specified by `params` is not +/// declared to be overwritten by `0` for non-host CPU profiles. +pub const fn assert_not_denied_cpuid_feature(params: &Parameters) { + if let Some(defs) = KVM_CPUID_DEFINITIONS.get(params) + && let Some(def) = defs.find_bit::() + { + assert!(!matches!(def.policy, ProfilePolicy::Static(0))); + } else { + panic!("Unable to lookup CPUID value definition with the given parameters and feature bit"); + } +} + +// TODO: Also include assert_denied_cpuid_feature diff --git a/arch/src/x86_64/cpuid_definitions/mod.rs b/arch/src/x86_64/cpuid_definitions/mod.rs index c959061f22..f45dc4a9e4 100644 --- a/arch/src/x86_64/cpuid_definitions/mod.rs +++ b/arch/src/x86_64/cpuid_definitions/mod.rs @@ -110,8 +110,6 @@ pub struct ValueDefinition { } /// Describes values within a register populated by the CPUID instruction with specific parameters. -/// -/// NOTE: The only way to interact with this value (beyond this crate) is via the const [`Self::as_slice()`](Self::as_slice) method. pub struct ValueDefinitions(&'static [ValueDefinition]); impl ValueDefinitions { /// Constructor permitting at most 32 entries. @@ -125,6 +123,22 @@ impl ValueDefinitions { pub const fn as_slice(&self) -> &'static [ValueDefinition] { self.0 } + + /// Lookup the [`ValueDefinition`] whose bits range contains the given `BIT`. + pub const fn find_bit(&self) -> Option<&ValueDefinition> { + let mut idx = 0; + let len = self.0.len(); + while idx < len { + let def = &self.0[idx]; + let start = def.bits_range.0; + let end = def.bits_range.1; + if (start <= BIT) & (end >= BIT) { + return Some(def); + } + idx += 1; + } + None + } } /// Describes multiple CPUID outputs. @@ -139,6 +153,40 @@ impl CpuidDefinitions { pub const fn as_slice(&self) -> &[(Parameters, ValueDefinitions); NUM_PARAMETERS] { &self.0 } + + /// Lookup the [`ValueDefinitions`] corresponding to the given `parameters`. + pub const fn get(&self, parameters: &Parameters) -> Option<&ValueDefinitions> { + let mut idx = 0; + let len = self.0.len(); + let leaf = parameters.leaf; + let sub_leaf_start = *parameters.sub_leaf.start(); + let sub_leaf_end = *parameters.sub_leaf.end(); + // Note that as of today const Rust is quite a bit more vorbose than normal Rust. + // This is why the following implementation doesn't look so idiomatic. + let is_eax = matches!(parameters.register, CpuidReg::EAX); + let is_ebx = matches!(parameters.register, CpuidReg::EBX); + let is_ecx = matches!(parameters.register, CpuidReg::ECX); + let is_edx = matches!(parameters.register, CpuidReg::EDX); + while idx < len { + let (param, defs) = &self.0[idx]; + let matching_leaf = leaf == param.leaf; + let matching_sub_leaf = (sub_leaf_start >= *param.sub_leaf.start()) + & (sub_leaf_end <= *param.sub_leaf.end()); + let matching_reg = { + match param.register { + CpuidReg::EAX => is_eax, + CpuidReg::EBX => is_ebx, + CpuidReg::ECX => is_ecx, + CpuidReg::EDX => is_edx, + } + }; + if matching_leaf & matching_sub_leaf & matching_reg { + return Some(defs); + } + idx += 1; + } + None + } } #[cfg(test)] diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index d9d3f5074f..d6a1fc1b0c 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -12,6 +12,7 @@ pub mod cpu_profile_generation; pub mod cpuid_definitions; pub mod interrupts; pub mod layout; +pub mod msr_definitions; pub mod regs; #[cfg(feature = "tdx")] @@ -19,18 +20,21 @@ pub mod tdx; mod mpspec; mod mptable; +mod msr_filter; mod smbios; use std::arch::x86_64; +use std::collections::{HashMap, HashSet}; use std::mem; -use hypervisor::arch::x86::{CPUID_FLAG_VALID_INDEX, CpuIdEntry}; -use hypervisor::{CpuVendor, HypervisorCpuError, HypervisorError}; +use hypervisor::arch::x86::{CPUID_FLAG_VALID_INDEX, CpuIdEntry, MsrEntry}; +use hypervisor::{CpuVendor, HypervisorCpuError, HypervisorError, HypervisorVmError}; use linux_loader::loader::bootparam::{boot_params, setup_header}; use linux_loader::loader::elf::start_info::{ hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info, }; -use log::{debug, error, info}; +use log::{debug, error, info, trace}; +pub use msr_filter::filter_denied_msrs; use serde::{Deserialize, Serialize}; pub use smbios::{SmbiosChassisConfig, SmbiosConfig, SmbiosSystem}; use thiserror::Error; @@ -39,7 +43,10 @@ use vm_memory::{ GuestMemoryRegion, }; -use crate::x86_64::cpu_profile::CpuidOutputRegisterAdjustments; +use crate::x86_64::cpu_profile::{ + CpuidOutputRegisterAdjustments, FeatureMsrAdjustment, RequiredMsrUpdates, +}; +use crate::x86_64::msr_definitions::RegisterAddress; use crate::{CpuProfile, GuestMemoryMmap, InitramfsConfig, RegionType}; // While modern architectures support more than 255 CPUs via x2APIC, @@ -139,11 +146,29 @@ pub enum Error { /// Error getting supported CPUID through the hypervisor (kvm/mshv) API #[error("Error getting supported CPUID through the hypervisor API")] CpuidGetSupported(#[source] HypervisorError), + /// Error getting the MSR-based features through the hypervisor (kvm) API + #[error("Error getting the MSR-based features through the hypervisor API")] + MsrBasedFeaturesGetSupported(#[source] HypervisorError), + + #[error("Error getting the MSRs supported by the hypervisor")] + MsrIndexList(#[source] HypervisorError), #[error( "The selected CPU profile cannot be utilized because the host's CPUID entries are not compatible with the profile" )] CpuProfileCpuidIncompatibility, + + #[error( + "The selected CPU profile cannot be utilized because the host's MSR-based features are not compatible with the profile" + )] + CpuProfileMsrIncompatibility, + + #[error("Unable to apply MSR filter: Bitmaps exceed maximum permitted memory usage")] + MsrFilterTooLarge, + + #[error("The hypervisor failed to set the given MSR filter")] + MsrFilter(#[source] HypervisorVmError), + /// 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, @@ -738,7 +763,7 @@ pub fn generate_common_cpuid( let (host_adjusted_to_profile, profile_cpu_vendor) = { config .profile - .data(config.amx) + .cpuid_data(config.amx) .map_or((Ok(None), None), |profile_data| { ( CpuidOutputRegisterAdjustments::adjust_cpuid_entries( @@ -960,12 +985,136 @@ pub fn generate_common_cpuid( } } +/// This function computes the [`RequiredMsrUpdates`] according to the +/// given `cpu_profile`, and `kvm_hyperv` parameters. +/// +/// If `[CpuProfile::Host]` is used then this function immediately returns `Ok(None)`, +/// regardless of the other parameters. +/// +/// ## Consistency with CPUID +/// +/// Some MSRs are only present when certain related bits in CPUID leaves are. +/// The CPU profile definition ensures consistency between the MSRs it permits and the +/// CPUID adjustments it prescribes. +/// +/// There are however certain CPUID values that can be modified by the VMM independently of the +/// CPUID profile and there may be corresponding MSRs that should then not be accessible. +/// At this point in time this only concerns the KVM and Hyper-V specific CPUID leaves and we +/// assume that the end user checks CPUID before accessing any of the related MSRs for now. +// TODO: Add `cpuid: &[CpuidEntry]` as a parameter and patch the permitted MSRs accordingly +// before upstreaming. +pub fn compute_required_msr_updates( + hypervisor: &dyn hypervisor::Hypervisor, + cpu_profile: CpuProfile, + kvm_hyperv: bool, +) -> super::Result> { + let Some(data) = cpu_profile.msr_data() else { + return Ok(None); + }; + + let cpu_vendor_host = hypervisor.get_cpu_vendor(); + let cpu_vendor_profile = data.cpu_vendor; + if cpu_vendor_host != cpu_vendor_profile { + return Err(Error::CpuProfileVendorIncompatibility { + cpu_vendor_profile, + cpu_vendor_host, + } + .into()); + } + + let msr_based_features = hypervisor + .get_msr_based_features() + .map_err(Error::MsrBasedFeaturesGetSupported)?; + + let msr_index_list = hypervisor + .get_msr_index_list() + .map_err(Error::MsrIndexList)?; + + let all_host_msrs: HashSet = msr_based_features + .iter() + .map(|entry| entry.index) + .chain(msr_index_list.iter().copied()) + .collect(); + + let mut permitted_msrs: HashSet = data.permitted_msrs.iter().map(|msr| msr.0).collect(); + + if kvm_hyperv { + // Log the Hyper-V MSRs that are not in the list of permitted MSRs. + // Some of these MSRs not being permitted by the profile might be benign or even intentional, + // but it might also indicate a BUG, or misconceptions that lead to bad CPU profiles. We thus + // log this at the info level for now. + for msr in msr_definitions::hyperv::HYPERV_MSRS { + if !permitted_msrs.contains(&msr) { + info!( + "NOTE: Hyper-V MSR: {msr:#x} is not in the list of MSRs supported by the CPU profile" + ); + } + } + } else { + // Remove all HYPER-V MSRs from the list of permitted MSRs + for msr in msr_definitions::hyperv::HYPERV_MSRS { + if permitted_msrs.remove(&msr) { + trace!("Removed {msr:#x} from the set of supported MSRs"); + } + } + } + + let forbidden_msrs: Vec = all_host_msrs + .difference(&permitted_msrs) + .map(|msr| RegisterAddress(*msr)) + .collect(); + + if (all_host_msrs.len() - forbidden_msrs.len()) != permitted_msrs.len() { + error!("Host does not have all the permitted MSRS"); + for msr in permitted_msrs.iter() { + if !all_host_msrs.contains(msr) { + error!("Host is missing the required MSR:={msr:#x}"); + } + } + Err(Error::CpuProfileMsrIncompatibility)?; + } + + // NOTE: It is fine to ignore the inner error because the called function logs any missing MSRs. + let adjusted_msr_based_features = + FeatureMsrAdjustment::adjust_to(&data.adjustments, &msr_based_features) + .map_err(|_| Error::CpuProfileMsrIncompatibility)?; + + // TODO: CPU profiles are only available for Intel CPUs at the moment. We need to branch on the vendor + // once we also have CPU profiles for AMD. + assert!(matches!(cpu_vendor_host, CpuVendor::Intel)); + crate::x86_64::msr_definitions::intel::check_feature_msr_compatibility( + &HashMap::from_iter( + adjusted_msr_based_features + .iter() + .map(|entry| (entry.index, entry.data)), + ), + &HashMap::from_iter( + msr_based_features + .iter() + .map(|entry| (entry.index, entry.data)), + ), + "CPU Profile", + "Host", + ) + .map_err(|_| { + error!("feature-based MSR compatibility check failed"); + Error::CpuProfileMsrIncompatibility + })?; + + let update = RequiredMsrUpdates { + msr_based_features: adjusted_msr_based_features, + denied_msrs: forbidden_msrs, + }; + Ok(Some(update)) +} + #[allow(clippy::too_many_arguments)] pub fn configure_vcpu( vcpu: &dyn hypervisor::Vcpu, id: u32, boot_setup: Option<(EntryPoint, &GuestMemoryAtomic)>, cpuid: Vec, + feature_msrs: &[MsrEntry], kvm_hyperv: bool, cpu_vendor: CpuVendor, topology: (u16, u16, u16, u16), @@ -1039,7 +1188,7 @@ pub fn configure_vcpu( vcpu.enable_hyperv_synic().unwrap(); } - regs::setup_msrs(vcpu).map_err(Error::MsrsConfiguration)?; + regs::setup_msrs(vcpu, feature_msrs).map_err(Error::MsrsConfiguration)?; if let Some((kernel_entry_point, guest_memory)) = boot_setup { regs::setup_regs(vcpu, kernel_entry_point).map_err(Error::RegsConfiguration)?; regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?; diff --git a/arch/src/x86_64/msr_definitions/hyperv.rs b/arch/src/x86_64/msr_definitions/hyperv.rs new file mode 100644 index 0000000000..71803e4964 --- /dev/null +++ b/arch/src/x86_64/msr_definitions/hyperv.rs @@ -0,0 +1,169 @@ +// Copyright © 2025 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! This module exports a list of all known Hyper-V MSRs that we found in Appendix F in +//! the Microsoft Hypervisor Top Level Functional Specification document from Feburary 2017. + +const HV_X64_MSR_GUEST_OS_ID: u32 = 0x40000000; +const HV_X64_MSR_HYPERCALL: u32 = 0x40000001; +const HV_X64_MSR_VP_INDEX: u32 = 0x40000002; +const HV_X64_MSR_RESET: u32 = 0x40000003; +const HV_X64_MSR_VP_RUNTIME: u32 = 0x40000010; +const HV_X64_MSR_TIME_REF_COUNT: u32 = 0x40000020; +const HV_X64_MSR_REFERENCE_TSC: u32 = 0x40000021; +const HV_X64_MSR_TSC_FREQUENCY: u32 = 0x40000022; +const HV_X64_MSR_APIC_FREQUENCY: u32 = 0x40000023; +const HV_X64_MSR_EOI: u32 = 0x40000070; +const HV_X64_MSR_ICR: u32 = 0x40000071; +const HV_X64_MSR_TPR: u32 = 0x40000072; +const HV_X64_MSR_VP_ASSIST_PAGE: u32 = 0x40000073; +const HV_X64_MSR_SCONTROL: u32 = 0x40000080; +const HV_X64_MSR_SVERSION: u32 = 0x40000081; +const HV_X64_MSR_SIEFP: u32 = 0x40000082; +const HV_X64_MSR_SIMP: u32 = 0x40000083; +const HV_X64_MSR_EOM: u32 = 0x40000084; +const HV_X64_MSR_SINT0: u32 = 0x40000090; +const HV_X64_MSR_SINT1: u32 = 0x40000091; +const HV_X64_MSR_SINT2: u32 = 0x40000092; +const HV_X64_MSR_SINT3: u32 = 0x40000093; +const HV_X64_MSR_SINT4: u32 = 0x40000094; +const HV_X64_MSR_SINT5: u32 = 0x40000095; +const HV_X64_MSR_SINT6: u32 = 0x40000096; +const HV_X64_MSR_SINT7: u32 = 0x40000097; +const HV_X64_MSR_SINT8: u32 = 0x40000098; +const HV_X64_MSR_SINT9: u32 = 0x40000099; +const HV_X64_MSR_SINT10: u32 = 0x4000009A; +const HV_X64_MSR_SINT11: u32 = 0x4000009B; +const HV_X64_MSR_SINT12: u32 = 0x4000009C; +const HV_X64_MSR_SINT13: u32 = 0x4000009D; +const HV_X64_MSR_SINT14: u32 = 0x4000009E; +const HV_X64_MSR_SINT15: u32 = 0x4000009F; +const HV_X64_MSR_STIMER0_CONFIG: u32 = 0x400000B0; +const HV_X64_MSR_STIMER0_COUNT: u32 = 0x400000B1; +const HV_X64_MSR_STIMER1_CONFIG: u32 = 0x400000B2; +const HV_X64_MSR_STIMER1_COUNT: u32 = 0x400000B3; +const HV_X64_MSR_STIMER2_CONFIG: u32 = 0x400000B4; +const HV_X64_MSR_STIMER2_COUNT: u32 = 0x400000B5; +const HV_X64_MSR_STIMER3_CONFIG: u32 = 0x400000B6; +const HV_X64_MSR_STIMER3_COUNT: u32 = 0x400000B7; +const HV_X64_MSR_POWER_STATE_TRIGGER_C1: u32 = 0x400000C1; +const HV_X64_MSR_POWER_STATE_TRIGGER_C2: u32 = 0x400000C2; +const HV_X64_MSR_POWER_STATE_TRIGGER_C3: u32 = 0x400000C3; +const HV_X64_MSR_POWER_STATE_CONFIG_C1: u32 = 0x400000D1; +const HV_X64_MSR_POWER_STATE_CONFIG_C2: u32 = 0x400000D2; +const HV_X64_MSR_POWER_STATE_CONFIG_C3: u32 = 0x400000D3; +const HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE: u32 = 0x400000E0; +const HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE: u32 = 0x400000E1; +const HV_X64_MSR_STATS_VP_RETAIL_PAGE: u32 = 0x400000E2; +const HV_X64_MSR_STATS_VP_INTERNAL_PAGE: u32 = 0x400000E3; +const HV_X64_MSR_GUEST_IDLE: u32 = 0x400000F0; +const HV_X64_MSR_SYNTH_DEBUG_CONTRO: u32 = 0x400000F1; +const HV_X64_MSR_SYNTH_DEBUG_STATU: u32 = 0x400000F2; +const HV_X64_MSR_SYNTH_DEBUG_SEND_BUFFER: u32 = 0x400000F3; +const HV_X64_MSR_SYNTH_DEBUG_RECEIVE_BUFFER: u32 = 0x400000F4; +const HV_X64_MSR_SYNTH_DEBUG_PENDING_BUFFER: u32 = 0x400000F5; +const HV_X64_MSR_CRASH_P0: u32 = 0x40000100; +const HV_X64_MSR_CRASH_P1: u32 = 0x40000101; +const HV_X64_MSR_CRASH_P2: u32 = 0x40000102; +const HV_X64_MSR_CRASH_P3: u32 = 0x40000103; +const HV_X64_MSR_CRASH_P4: u32 = 0x40000104; +const HV_X64_MSR_CRASH_CTL: u32 = 0x40000105; + +/// This is a list of all Hyper-V MSRs that we found in Appendix F in the Microsoft +/// Hypervisor Top Level Functional Specification document from Feburary 2017 +pub(in crate::x86_64) const HYPERV_MSRS: [u32; 64] = [ + HV_X64_MSR_GUEST_OS_ID, + HV_X64_MSR_HYPERCALL, + HV_X64_MSR_VP_INDEX, + HV_X64_MSR_RESET, + HV_X64_MSR_VP_RUNTIME, + HV_X64_MSR_TIME_REF_COUNT, + HV_X64_MSR_REFERENCE_TSC, + HV_X64_MSR_TSC_FREQUENCY, + HV_X64_MSR_APIC_FREQUENCY, + HV_X64_MSR_EOI, + HV_X64_MSR_ICR, + HV_X64_MSR_TPR, + HV_X64_MSR_VP_ASSIST_PAGE, + HV_X64_MSR_SCONTROL, + HV_X64_MSR_SVERSION, + HV_X64_MSR_SIEFP, + HV_X64_MSR_SIMP, + HV_X64_MSR_EOM, + HV_X64_MSR_SINT0, + HV_X64_MSR_SINT1, + HV_X64_MSR_SINT2, + HV_X64_MSR_SINT3, + HV_X64_MSR_SINT4, + HV_X64_MSR_SINT5, + HV_X64_MSR_SINT6, + HV_X64_MSR_SINT7, + HV_X64_MSR_SINT8, + HV_X64_MSR_SINT9, + HV_X64_MSR_SINT10, + HV_X64_MSR_SINT11, + HV_X64_MSR_SINT12, + HV_X64_MSR_SINT13, + HV_X64_MSR_SINT14, + HV_X64_MSR_SINT15, + HV_X64_MSR_STIMER0_CONFIG, + HV_X64_MSR_STIMER0_COUNT, + HV_X64_MSR_STIMER1_CONFIG, + HV_X64_MSR_STIMER1_COUNT, + HV_X64_MSR_STIMER2_CONFIG, + HV_X64_MSR_STIMER2_COUNT, + HV_X64_MSR_STIMER3_CONFIG, + HV_X64_MSR_STIMER3_COUNT, + HV_X64_MSR_POWER_STATE_TRIGGER_C1, + HV_X64_MSR_POWER_STATE_TRIGGER_C2, + HV_X64_MSR_POWER_STATE_TRIGGER_C3, + HV_X64_MSR_POWER_STATE_CONFIG_C1, + HV_X64_MSR_POWER_STATE_CONFIG_C2, + HV_X64_MSR_POWER_STATE_CONFIG_C3, + HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE, + HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE, + HV_X64_MSR_STATS_VP_RETAIL_PAGE, + HV_X64_MSR_STATS_VP_INTERNAL_PAGE, + HV_X64_MSR_GUEST_IDLE, + HV_X64_MSR_SYNTH_DEBUG_CONTRO, + HV_X64_MSR_SYNTH_DEBUG_STATU, + HV_X64_MSR_SYNTH_DEBUG_SEND_BUFFER, + HV_X64_MSR_SYNTH_DEBUG_RECEIVE_BUFFER, + HV_X64_MSR_SYNTH_DEBUG_PENDING_BUFFER, + HV_X64_MSR_CRASH_P0, + HV_X64_MSR_CRASH_P1, + HV_X64_MSR_CRASH_P2, + HV_X64_MSR_CRASH_P3, + HV_X64_MSR_CRASH_P4, + HV_X64_MSR_CRASH_CTL, +]; + +#[cfg(all(test, feature = "kvm"))] +mod tests { + use super::*; + use crate::x86_64::msr_definitions::intel::{ + INTEL_MSR_FEATURE_DEFINITIONS, PERMITTED_IA32_MSRS, + }; + use crate::x86_64::msr_definitions::kvm::PROFILE_PERMITTED_KVM_MSRS; + + // If this can be assumed than that simplifies some things. + // + // NOTE: It is perfectly possible to make this a compile time check instead, + // but that is more cumbersome hence we leave that for later. + #[test] + fn does_not_intersect_other_permitted_msr_sets() { + for msr in HYPERV_MSRS { + assert!( + !INTEL_MSR_FEATURE_DEFINITIONS + .as_slice() + .iter() + .map(|r| r.0.0) + .chain(PERMITTED_IA32_MSRS) + .chain(PROFILE_PERMITTED_KVM_MSRS) + .any(|other_permitted_msr| other_permitted_msr == msr) + ); + } + } +} diff --git a/arch/src/x86_64/msr_definitions/intel/architectural_msrs.rs b/arch/src/x86_64/msr_definitions/intel/architectural_msrs.rs new file mode 100644 index 0000000000..4c2eb77608 --- /dev/null +++ b/arch/src/x86_64/msr_definitions/intel/architectural_msrs.rs @@ -0,0 +1,1703 @@ +// Copyright © 2025 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// +//! This module contains lists of architectural MSRs (or more accurately MSR register addresses) that +//! are permitted and forbidden for use with CPU profiles. +//! +//! The CPU profile generation tool obtains all MSRS supported by both KVM and the hardware +//! when it runs and uses the permitted list to only record those that are permitted. +//! +//! The list of forbidden architectural MSRs is only used to rule out "false" new MSRs that otherwise +//! would require updating the CPU profile generation tool. + +// We occasionally write doc comments for constants that are defined in private modules. This +// is still helpful for developers as the LSP can then provide information about the constants +// directly at the site(s) where they are being used. +#![allow(unused_doc_comments)] + +#[cfg(feature = "cpu_profile_generation")] +pub(in crate::x86_64) use forbidden_architectural_msrs::FORBIDDEN_IA32_MSR_RANGES; +pub(in crate::x86_64) use permitted_architectural_msrs::PERMITTED_IA32_MSRS; + +use crate::x86_64::CpuidReg; +use crate::x86_64::cpuid_definitions::Parameters; +use crate::x86_64::cpuid_definitions::intel::assert_not_denied_cpuid_feature; + +mod permitted_architectural_msrs { + use read_only::READ_ONLY_IA32_MSRS; + use read_write::READ_WRITE_IA32_MSRS; + use write_only::WRITE_ONLY_IA32_MSRS; + + use super::{CpuidReg, Parameters}; + use crate::x86_64::msr_definitions::intel::architectural_msrs::assert_not_denied_cpuid_feature; + + mod read_only { + use super::{CpuidReg, Parameters, assert_not_denied_cpuid_feature}; + /// (R/O) + const IA32_BARRIER: u32 = 0x2f; + const _IA32_BARRIER_CPUID_CHECK: () = const { + assert_not_denied_cpuid_feature::<27>(&Parameters { + leaf: 0x7, + sub_leaf: 0..=0, + register: CpuidReg::EAX, + }); + }; + /// Overclocking Status (R/O) + const IA32_OVERCLOCKING_STATUS: u32 = 0x195; + // TODO: Also check consistency with IA32_ARCH_CAPABILITIES[23] + + /// xAPIC Disable Status (R/O) + const IA32_XAPIC_DISABLE_STATUS: u32 = 0xbd; + const _IA32_XAPIC_DISABLE_STATUS_CPUID_CHECK: () = + assert_not_denied_cpuid_feature::<29>(&Parameters { + leaf: 0x7, + sub_leaf: 0..=0, + register: CpuidReg::EDX, + }); + + // TODO: Also assert that IA32_ARCH_CAPABILITIES[21] is also not hard-coded to prevent + // this MSR from being accessed + + /// MTRR Capability (R/O) + const IA32_MTRRCAP: u32 = 0xfe; + + // TODO: Not sure whether the IA32_FZM_* msrs should be permitted + const IA32_FZM_DOMAIN_CONFIG: u32 = 0x83; + const IA32_FZM_RANGE_STARTADDR: u32 = 0x84; + const IA32_FZM_RANGE_ENDADDR: u32 = 0x85; + const IA32_FZM_RANGE_WRITESTATUS: u32 = 0x86; + + /// DCA Capability (R) + const IA32_PLATFORM_DCA_CAP: u32 = 0x1f8; + /// If set, CPU supports Prefetch-Hint type + const IA32_CPU_DCA_CAP: u32 = 0x1f9; + + const _IA32_DCA_CAP_CPUID_CHECK: () = assert_not_denied_cpuid_feature::<18>(&Parameters { + leaf: 0x1, + sub_leaf: 0..=0, + register: CpuidReg::ECX, + }); + + // TODO: Can we rather place this MSR in the deny list? + const IA32_MCU_STAGING_MBOX_ADDR: u32 = 0x7a5; + + // NOTE: THE X2APIC related MSRs cannot be filtered by KVM, but we include them here anyway for completeness sake. + const IA32_X2APIC_APICID: u32 = 0x802; + const IA32_X2APIC_VERSION: u32 = 0x803; + const IA32_X2APIC_PPR: u32 = 0x80a; + const IA32_X2APIC_LDR: u32 = 0x80d; + const IA32_X2APIC_ISR0: u32 = 0x810; + const IA32_X2APIC_ISR1: u32 = 0x811; + const IA32_X2APIC_ISR2: u32 = 0x812; + + const IA32_X2APIC_ISR3: u32 = 0x813; + const IA32_X2APIC_ISR4: u32 = 0x814; + const IA32_X2APIC_ISR5: u32 = 0x815; + const IA32_X2APIC_ISR6: u32 = 0x816; + const IA32_X2APIC_ISR7: u32 = 0x817; + const IA32_X2APIC_TMR0: u32 = 0x818; + const IA32_X2APIC_TMR1: u32 = 0x819; + const IA32_X2APIC_TMR2: u32 = 0x81a; + const IA32_X2APIC_TMR3: u32 = 0x81b; + const IA32_X2APIC_TMR4: u32 = 0x81c; + const IA32_X2APIC_TMR5: u32 = 0x81d; + const IA32_X2APIC_TMR6: u32 = 0x81e; + const IA32_X2APIC_TMR7: u32 = 0x81f; + const IA32_X2APIC_IRR0: u32 = 0x820; + const IA32_X2APIC_IRR1: u32 = 0x821; + const IA32_X2APIC_IRR2: u32 = 0x822; + const IA32_X2APIC_IRR3: u32 = 0x823; + const IA32_X2APIC_IRR4: u32 = 0x824; + const IA32_X2APIC_IRR5: u32 = 0x825; + const IA32_X2APIC_IRR6: u32 = 0x826; + const IA32_X2APIC_IRR7: u32 = 0x827; + const IA32_X2APIC_CUR_COUNT: u32 = 0x839; + + pub(super) const READ_ONLY_IA32_MSRS: [u32; 40] = [ + IA32_BARRIER, + IA32_MTRRCAP, + IA32_OVERCLOCKING_STATUS, + IA32_XAPIC_DISABLE_STATUS, + IA32_FZM_DOMAIN_CONFIG, + IA32_FZM_RANGE_STARTADDR, + IA32_FZM_RANGE_ENDADDR, + IA32_FZM_RANGE_WRITESTATUS, + IA32_PLATFORM_DCA_CAP, + IA32_CPU_DCA_CAP, + IA32_MCU_STAGING_MBOX_ADDR, + IA32_X2APIC_APICID, + IA32_X2APIC_VERSION, + IA32_X2APIC_PPR, + IA32_X2APIC_LDR, + IA32_X2APIC_ISR0, + IA32_X2APIC_ISR1, + IA32_X2APIC_ISR2, + IA32_X2APIC_ISR3, + IA32_X2APIC_ISR4, + IA32_X2APIC_ISR5, + IA32_X2APIC_ISR6, + IA32_X2APIC_ISR7, + IA32_X2APIC_TMR0, + IA32_X2APIC_TMR1, + IA32_X2APIC_TMR2, + IA32_X2APIC_TMR3, + IA32_X2APIC_TMR4, + IA32_X2APIC_TMR5, + IA32_X2APIC_TMR6, + IA32_X2APIC_TMR7, + IA32_X2APIC_IRR0, + IA32_X2APIC_IRR1, + IA32_X2APIC_IRR2, + IA32_X2APIC_IRR3, + IA32_X2APIC_IRR4, + IA32_X2APIC_IRR5, + IA32_X2APIC_IRR6, + IA32_X2APIC_IRR7, + IA32_X2APIC_CUR_COUNT, + ]; + } + + mod read_write { + use super::{CpuidReg, Parameters, assert_not_denied_cpuid_feature}; + // TODO: Not sure if we need to permit this + const IA32_P5_MC_ADDR: u32 = 0x0; + // TODO: Not sure if we need to permit this + const IA32_P5_MC_TYPE: u32 = 0x1; + + // TODO: Is this also write? + const IA32_TIME_STAMP_COUNTER: u32 = 0x10; + + const IA32_APIC_BASE: u32 = 0x1b; + + const IA32_FEATURE_CONTROL: u32 = 0x3a; + + /// Per Logical Processor TSC Adjust (R/Write to clear) + const IA32_TSC_ADJUST: u32 = 0x3b; + const _IA32_TSC_ADJUST_CPUID_CHECK: () = + assert_not_denied_cpuid_feature::<1>(&Parameters { + leaf: 0x7, + sub_leaf: 0..=0, + register: CpuidReg::EBX, + }); + + const IA32_SPEC_CTRL: u32 = 0x48; + const _IA32_SPECT_CTRL_CPUID_CHECK: () = + assert_not_denied_cpuid_feature::<26>(&Parameters { + leaf: 0x7, + sub_leaf: 0..=0, + register: CpuidReg::EDX, + }); + + /// SYSENTER_CS_MSR + const IA32_SYSENTER_CS: u32 = 0x174; + + /// SYSENTER_ESP_MSR + const IA32_SYSENTER_ESP: u32 = 0x175; + + /// SYSENTER_ESP_MSR + const IA32_SYSENTER_EIP: u32 = 0x176; + + // TODO: Does it really make sense to permit this MSR? + const IA32_SMM_MONITOR_CTL: u32 = 0x9b; + const _IA32_SMM_MONITOR_CTL_CPUID_CHECK: () = + assert_not_denied_cpuid_feature::<5>(&Parameters { + leaf: 0x1, + sub_leaf: 0..=0, + register: CpuidReg::ECX, + }); + + /// Enable Misc. Processr Features + const IA32_MISC_ENABLE: u32 = 0x1a0; + + // TODO: Not sure what this does and whether it should be enabled + const IA32_FZM_RANGE_INDEX: u32 = 0x82; + + const IA32_XFD: u32 = 0x1c4; + const IA32_XFD_ERR: u32 = 0x1c5; + + // TODO: Not sure about SMRR_* (note that they are writable only in SMM) + const IA32_SMRR_PHYSBASE: u32 = 0x1f2; + const IA32_SMRR_PHYSMASK: u32 = 0x1f3; + + const IA32_DCA_0_CAP: u32 = 0x1fa; + + const _IA32_DCA_0_CAP_CPUID_CHECK: () = + assert_not_denied_cpuid_feature::<18>(&Parameters { + leaf: 0x1, + sub_leaf: 0..=0, + register: CpuidReg::ECX, + }); + + const IA32_MTRR_PHYSBASE0: u32 = 0x200; + const IA32_MTRR_PHYSMASK0: u32 = 0x201; + const IA32_MTRR_PHYSBASE1: u32 = 0x202; + const IA32_MTRR_PHYSMASK1: u32 = 0x203; + const IA32_MTRR_PHYSBASE2: u32 = 0x204; + const IA32_MTRR_PHYSMASK2: u32 = 0x205; + const IA32_MTRR_PHYSBASE3: u32 = 0x206; + const IA32_MTRR_PHYSMASK3: u32 = 0x207; + const IA32_MTRR_PHYSBASE4: u32 = 0x208; + const IA32_MTRR_PHYSMASK4: u32 = 0x209; + const IA32_MTRR_PHYSBASE5: u32 = 0x20a; + const IA32_MTRR_PHYSMASK5: u32 = 0x20b; + const IA32_MTRR_PHYSBASE6: u32 = 0x20c; + const IA32_MTRR_PHYSMASK6: u32 = 0x20d; + const IA32_MTRR_PHYSBASE7: u32 = 0x20e; + const IA32_MTRR_PHYSMASK7: u32 = 0x20f; + const IA32_MTRR_PHYSBASE8: u32 = 0x210; + const IA32_MTRR_PHYSMASK8: u32 = 0x211; + const IA32_MTRR_PHYSBASE9: u32 = 0x212; + const IA32_MTRR_PHYSMASK9: u32 = 0x213; + + // TODO: Are these actually READ + Write? + const IA32_MTRR_FIX64K_00000: u32 = 0x250; + const IA32_MTRR_FIX16K_80000: u32 = 0x258; + const IA32_MTRR_FIX16K_A0000: u32 = 0x259; + const IA32_MTRR_FIX4K_C0000: u32 = 0x268; + const IA32_MTRR_FIX4K_C8000: u32 = 0x269; + const IA32_MTRR_FIX4K_D0000: u32 = 0x26a; + const IA32_MTRR_FIX4K_D8000: u32 = 0x26b; + const IA32_MTRR_FIX4K_E0000: u32 = 0x26c; + const IA32_MTRR_FIX4K_E8000: u32 = 0x26d; + const IA32_MTRR_FIX4K_F0000: u32 = 0x26e; + const IA32_MTRR_FIX4K_F8000: u32 = 0x26f; + + const _IA32_MTRR_FIX_I_X_CPUID_CHECK: () = + assert_not_denied_cpuid_feature::<12>(&Parameters { + leaf: 0x1, + sub_leaf: 0..=0, + register: CpuidReg::EDX, + }); + + const IA32_PAT: u32 = 0x277; + const _IA32_PAT_CPUID_CHECK: () = assert_not_denied_cpuid_feature::<16>(&Parameters { + leaf: 0x1, + sub_leaf: 0..=0, + register: CpuidReg::EDX, + }); + + const IA32_MTRR_DEF_TYPE: u32 = 0x2ff; + + const IA32_U_CET: u32 = 0x6a0; + const IA32_S_CET: u32 = 0x6a2; + + const IA32_TSC_DEADLINE: u32 = 0x6e0; + const _IA32_TSC_DEADLINE_CPUID_CHECK: () = + assert_not_denied_cpuid_feature::<24>(&Parameters { + leaf: 0x1, + sub_leaf: 0..=0, + register: CpuidReg::ECX, + }); + + // TODO: We should probably not permit this if possible + const IA32_PECI_HWP_REQUEST_INFO: u32 = 0x775; + + // NOTE: THE X2APIC related MSRs cannot be filtered by KVM, but we include them here anyway for completeness sake. + const IA32_X2APIC_TPR: u32 = 0x808; + const IA32_X2APIC_SIVR: u32 = 0x80f; + + const IA32_X2APIC_ESR: u32 = 0x828; + const IA32_X2APIC_LVT_CMCI: u32 = 0x82f; + const IA32_X2APIC_ICR: u32 = 0x830; + const IA32_X2APIC_LVT_TIMER: u32 = 0x832; + const IA32_X2APIC_LVT_THERMAL: u32 = 0x833; + const IA32_X2APIC_LVT_PMI: u32 = 0x834; + const IA32_X2APIC_LVT_LINT0: u32 = 0x835; + + const IA32_X2APIC_LVT_LINT1: u32 = 0x836; + const IA32_X2APIC_LVT_ERROR: u32 = 0x837; + const IA32_X2APIC_INIT_COUNT: u32 = 0x838; + const IA32_X2APIC_DIV_CONF: u32 = 0x83e; + + // TODO: Not sure about this MSR + const IA32_RESOURCE_PRIORITY: u32 = 0xc88; + // TODO: Not sure about this MSR + const IA32_RESOURCE_PRIORITY_PKG: u32 = 0xc89; + + const IA32_PASID: u32 = 0xd93; + + const IA32_XSS: u32 = 0xda0; + const _IA32_XSS_CPUID_CHECK: () = assert_not_denied_cpuid_feature::<3>(&Parameters { + leaf: 0xd, + sub_leaf: 1..=1, + register: CpuidReg::EAX, + }); + + /// Extended Feature Enable + const IA32_EFER: u32 = 0xc0000080; + + const IA32_STAR: u32 = 0xc000_0081; + const IA32_LSTAR: u32 = 0xc000_0082; + const IA32_CSTAR: u32 = 0xc000_0083; + const IA32_FMASK: u32 = 0xc000_0084; + const IA32_FS_BASE: u32 = 0xc000_0100; + const IA32_GS_BASE: u32 = 0xc000_0101; + const IA32_KERNEL_GS_BASE: u32 = 0xc000_0102; + const _IA32_EFER_UPTO_IA32_KERNEL_GS_BASE_CPUID_CHECK: () = + assert_not_denied_cpuid_feature::<29>(&Parameters { + leaf: 0x80000001, + sub_leaf: 0..=0, + register: CpuidReg::EDX, + }); + + const IA32_TSC_AUX: u32 = 0xc000_0103; + // NOTE That either the following has to pass, or the same test with 0x80000001.EDX[27] + const _IA32_TSC_AUX_CPUID_CHECK: () = assert_not_denied_cpuid_feature::<22>(&Parameters { + leaf: 0x7, + sub_leaf: 0..=0, + register: CpuidReg::ECX, + }); + + const IA32_UARCH_MISC_CTL: u32 = 0x1b01; + // TODO: Check against IA32_ARCH_CAPABILITIES[12] + pub(super) const READ_WRITE_IA32_MSRS: [u32; 82] = [ + IA32_P5_MC_ADDR, + IA32_P5_MC_TYPE, + IA32_TIME_STAMP_COUNTER, + IA32_APIC_BASE, + IA32_FEATURE_CONTROL, + IA32_TSC_ADJUST, + IA32_SPEC_CTRL, + IA32_SYSENTER_CS, + IA32_SYSENTER_ESP, + IA32_SYSENTER_EIP, + // IA32_UMWAIT_CONTROL, Should perhaps not be permitted + IA32_SMM_MONITOR_CTL, + IA32_MISC_ENABLE, + IA32_FZM_RANGE_INDEX, + IA32_XFD, + IA32_XFD_ERR, + IA32_SMRR_PHYSBASE, + IA32_SMRR_PHYSMASK, + IA32_DCA_0_CAP, + IA32_MTRR_PHYSBASE0, + IA32_MTRR_PHYSMASK0, + IA32_MTRR_PHYSBASE1, + IA32_MTRR_PHYSMASK1, + IA32_MTRR_PHYSBASE2, + IA32_MTRR_PHYSMASK2, + IA32_MTRR_PHYSBASE3, + IA32_MTRR_PHYSMASK3, + IA32_MTRR_PHYSBASE4, + IA32_MTRR_PHYSMASK4, + IA32_MTRR_PHYSBASE5, + IA32_MTRR_PHYSMASK5, + IA32_MTRR_PHYSBASE6, + IA32_MTRR_PHYSMASK6, + IA32_MTRR_PHYSBASE7, + IA32_MTRR_PHYSMASK7, + IA32_MTRR_PHYSBASE8, + IA32_MTRR_PHYSMASK8, + IA32_MTRR_PHYSBASE9, + IA32_MTRR_PHYSMASK9, + IA32_MTRR_FIX64K_00000, + IA32_MTRR_FIX16K_80000, + IA32_MTRR_FIX16K_A0000, + IA32_MTRR_FIX4K_C0000, + IA32_MTRR_FIX4K_C8000, + IA32_MTRR_FIX4K_D0000, + IA32_MTRR_FIX4K_D8000, + IA32_MTRR_FIX4K_E0000, + IA32_MTRR_FIX4K_E8000, + IA32_MTRR_FIX4K_F0000, + IA32_MTRR_FIX4K_F8000, + IA32_PAT, + IA32_MTRR_DEF_TYPE, + IA32_U_CET, + IA32_S_CET, + IA32_TSC_DEADLINE, + IA32_PECI_HWP_REQUEST_INFO, + IA32_X2APIC_TPR, + IA32_X2APIC_SIVR, + IA32_X2APIC_ESR, + IA32_X2APIC_LVT_CMCI, + IA32_X2APIC_ICR, + IA32_X2APIC_LVT_TIMER, + IA32_X2APIC_LVT_THERMAL, + IA32_X2APIC_LVT_PMI, + IA32_X2APIC_LVT_LINT0, + IA32_X2APIC_LVT_LINT1, + IA32_X2APIC_LVT_ERROR, + IA32_X2APIC_INIT_COUNT, + IA32_X2APIC_DIV_CONF, + IA32_RESOURCE_PRIORITY, + IA32_RESOURCE_PRIORITY_PKG, + IA32_PASID, + IA32_XSS, + IA32_EFER, + IA32_STAR, + IA32_LSTAR, + IA32_CSTAR, + IA32_FMASK, + IA32_FS_BASE, + IA32_GS_BASE, + IA32_KERNEL_GS_BASE, + IA32_TSC_AUX, + IA32_UARCH_MISC_CTL, + ]; + } + + mod write_only { + use super::{CpuidReg, Parameters, assert_not_denied_cpuid_feature}; + + /// Prediction Command (WO) + const IA32_PRED_CMD: u32 = 0x49; + const _IA32_PRED_CMD_CPUID_CHECK: () = assert_not_denied_cpuid_feature::<26>(&Parameters { + leaf: 0x7, + sub_leaf: 0..=0, + register: CpuidReg::EDX, + }); + + /// Flush Command (WO) + const IA32_FLUSH_CMD: u32 = 0x10b; + + // TODO: Should probably use inherit policy here + const _IA32_FLUSH_CMD_CPUID_CHECK: () = + assert_not_denied_cpuid_feature::<28>(&Parameters { + leaf: 0x7, + sub_leaf: 0..=0, + register: CpuidReg::EDX, + }); + + // X2apic related MSRS cannot be filtered by KVM, but we include it here anyway for completeness sake + const IA32_X2APIC_EOI: u32 = 0x80b; + + const IA32_X2APIC_SELF_IPI: u32 = 0x83f; + + pub(super) const WRITE_ONLY_IA32_MSRS: [u32; 4] = [ + IA32_PRED_CMD, + IA32_FLUSH_CMD, + IA32_X2APIC_EOI, + IA32_X2APIC_SELF_IPI, + ]; + } + + /// A list of permitted Intel IA32 MSRs that are not considered MSR-based feature indices + /// by KVM. + /// + /// The MSRs listed here can be studied further in Table 2.2 in Section 2.1 of the Intel SDM + /// Vol. 4 from October 2025 + pub(in crate::x86_64) const PERMITTED_IA32_MSRS: [u32; 126] = const { + let mut permitted = [0u32; 126]; + let read_only_len = READ_ONLY_IA32_MSRS.len(); + let write_only_len = WRITE_ONLY_IA32_MSRS.len(); + let read_write_len = READ_WRITE_IA32_MSRS.len(); + assert!(permitted.len() == (read_only_len + write_only_len + read_write_len)); + let mut idx = 0; + // Insert read only msrs + { + let mut i = 0; + while i < read_only_len { + permitted[idx + i] = READ_ONLY_IA32_MSRS[i]; + i += 1; + } + idx += read_only_len; + } + // Insert write only msrs + { + let mut i = 0; + while i < write_only_len { + permitted[idx + i] = WRITE_ONLY_IA32_MSRS[i]; + i += 1; + } + idx += write_only_len; + } + // Insert read & write msrs + { + let mut i = 0; + while i < read_write_len { + permitted[idx + i] = READ_WRITE_IA32_MSRS[i]; + i += 1; + } + } + permitted + }; +} + +#[cfg(feature = "cpu_profile_generation")] +mod forbidden_architectural_msrs { + // TODO: Not sure about IA32_P5_MC_ADDR & IA32_P5_MC_TYPE + + // TODO: IA32_MCU_OPT_CTRL should be not permitted (ensure this via IA32_ARCH_CAPABILITIES) + + const IA32_MONITOR_FILTER_SIZE: (u32, u32) = (0x6, 0x6); + // TODO: Not sure about this one + const IA32_PLATFORM_ID: (u32, u32) = (0x17, 0x17); + + /// Only available is CPUID 0x7.0x1.EBX[0] = 1, but this is always 0 for non-host CPU profiles + const IA32_PPIN_CTL: (u32, u32) = (0x4e, 0x4e); + + /// Only available is CPUID 0x7.0x1.EBX[0] = 1, but this is always 0 for non-host CPU profiles + const IA32_PPIN: (u32, u32) = (0x4f, 0x4f); + + /// Used for microcode updates. Should not be available for guests. + const IA32_BIOS_UPDT_TRIG: (u32, u32) = (0x79, 0x79); + + /// Currently only related to Secure enclaves/Keylocker which is not available for non-host CPU profiles + const IA32_FEATURE_ACTIVATION: (u32, u32) = (0x7a, 0x7a); + + /// Related to microcode updates + const IA32_MCU_ENUMERATION: (u32, u32) = (0x7b, 0x7b); + + const IA32_MCU_STATUS: (u32, u32) = (0x7c, 0x7c); + + /// Related to total memory encryption + const IA32_MKTME_KEYID_PARTITIONING: (u32, u32) = (0x87, 0x87); + + // TODO: Not sure what to do about IA32_BIOS_SIGN_ID (note that it is also a MSR-based feature according to KVM) + + const IA32_SGXLEPUBKEYHASH0: (u32, u32) = (0x8c, 0x8c); + + const IA32_SGXLEPUBKEYHASH1: (u32, u32) = (0x8d, 0x8d); + + const IA32_SGXLEPUBKEYHASH2: (u32, u32) = (0x8e, 0x8e); + + const IA32_SGXLEPUBKEYHASH3: (u32, u32) = (0x8f, 0x8f); + + const IA32_SGXLEPUBKEYHASH4: (u32, u32) = (0x90, 0x90); + + const IA32_SGXLEPUBKEYHASH5: (u32, u32) = (0x91, 0x91); + + // TODO: Check this + const IA32_SMBASE: (u32, u32) = (0x9e, 0x9e); + + const IA32_MISC_PACKAGE_CTLS: (u32, u32) = (0xbc, 0xbc); + + /// Clock Modulation Control + /// This is disabled via CPUID for non-host CPU profiles + const IA32_CLOCK_MODULATION: (u32, u32) = (0x19a, 0x19a); + + // TODO: IA32_X2APIC_DISABLE_STATUS + + // IA32_PLI_SSP is disabled via CPUID for non-host profiles + const IA32_PLI_SSP: (u32, u32) = (0x6a4, 0x6a7); + + // This is disabled via CPUID for non-host profiles + const IA32_INTERRUPT_SSP_TABLE_ADDR: (u32, u32) = (0x6a8, 0x6a8); + + const IA32_PMC0: (u32, u32) = (0xc1, 0xc1); + const IA32_PMC1: (u32, u32) = (0xc2, 0xc2); + const IA32_PMC2: (u32, u32) = (0xc3, 0xc3); + const IA32_PMC3: (u32, u32) = (0xc4, 0xc4); + const IA32_PMC4: (u32, u32) = (0xc5, 0xc5); + const IA32_PMC5: (u32, u32) = (0xc6, 0xc6); + const IA32_PMC6: (u32, u32) = (0xc7, 0xc7); + const IA32_PMC7: (u32, u32) = (0xc8, 0xc8); + const IA32_PMC8: (u32, u32) = (0xc9, 0xc9); + const IA32_PMC9: (u32, u32) = (0xca, 0xca); + + const IA32_CORE_CAPABILITIES: (u32, u32) = (0xcf, 0xcf); + + // TODO: Do we really want to forbid this MSR? + const IA32_UMWAIT_CONTROL: (u32, u32) = (0xe1, 0xe1); + + // Disabled by CPUID for non-host CPU profiles + const IA32_MPERF: (u32, u32) = (0xe7, 0xe7); + + const IA32_APERF: (u32, u32) = (0xe8, 0xe8); + + const IA32_TSX_FORCE_ABORT: (u32, u32) = (0x10f, 0x10f); + + // Disabled via static IA32_ARCH_CAPABILITIES bit for non-host CPU profiles + const IA32_TSX_CTRL: (u32, u32) = (0x122, 0x122); + + // NOTE: IA32_MCU_OPT_CTRL must necessarily be available, due to + // what we set in CPUID for some CPU profiles (inherit policy) + + // TODO: Don't know about IA32_SYSENTER_CS, IA32_SYSENTER_ESP, + // IA32_SYSENTER_EIP + // + + // TODO: Not sure if we can/should deny this MSR, but + // it doesn't really make sense to have it available in + // a virtualized environment + // + // If we keep it denied we should document that + // even for 06_01H one cannot rely on the existence of this MSR + const IA32_MCG_CAP: (u32, u32) = (0x179, 0x179); + + // TODO: Also not sure if we may deny this MSR + const IA32_MCG_STATUS: (u32, u32) = (0x17a, 0x17a); + + // TODO: Can we deny this? + const IA32_MCG_CTL: (u32, u32) = (0x17b, 0x17b); + + // TODO: 0x180- 0x185 is reserved, we should not list these MSRS at all + + /// Disabled via CPUID for all non-host CPU profiles + const IA32_PERFEVTSEL0: (u32, u32) = (0x186, 0x186); + const IA32_PERFEVTSEL1: (u32, u32) = (0x187, 0x187); + const IA32_PERFEVTSEL2: (u32, u32) = (0x188, 0x188); + const IA32_PERFEVTSEL3: (u32, u32) = (0x189, 0x189); + const IA32_PERFEVTSEL4: (u32, u32) = (0x18a, 0x18a); + const IA32_PERFEVTSEL5: (u32, u32) = (0x18b, 0x18b); + const IA32_PERFEVTSEL6: (u32, u32) = (0x18c, 0x18c); + const IA32_PERFEVTSEL7: (u32, u32) = (0x18d, 0x18d); + const IA32_PERFEVTSEL8: (u32, u32) = (0x18e, 0x18e); + const IA32_PERFEVTSEL9: (u32, u32) = (0x18f, 0x18f); + + // TODO: 0x18a - 0x194 is reserved and should not be included in any list + + // TODO: 0x196, 197 is reserved and should not be included in any list + // + + const IA32_PERF_STATUS: (u32, u32) = (0x198, 0x198); + + const IA32_PERF_CTL: (u32, u32) = (0x199, 0x199); + + // Disabled via CPUID for non-host profiles + const IA32_THERM_INTERRUPT: (u32, u32) = (0x19b, 0x19b); + + // Disabled via CPUID for non-host profiles + const IA32_THERM_STATUS: (u32, u32) = (0x19c, 0x19c); + + // TODO: Consider disabling IA32_MISC_ENABLE + + // Disabled via CPUID for non-host profiles + const IA32_ENERGY_PERF_BIAS: (u32, u32) = (0x1b0, 0x1b0); + + // Disabled via CPUID for non-host profiles + const IA32_PACKAGE_THERM_STATUS: (u32, u32) = (0x1b1, 0x1b1); + + // Disabled via CPUID for non-host profiles + const IA32_PACKAGE_THERM_INTERRUPT: (u32, u32) = (0x1b2, 0x1b2); + + const IA32_DEBUGCTL: (u32, u32) = (0x1d9, 0x1d9); + + const IA32_LER_FROM_IP: (u32, u32) = (0x1dd, 0x1dd); + + const IA32_LER_TO_IP: (u32, u32) = (0x1de, 0x1de); + + const IA32_LER_INFO: (u32, u32) = (0x1e0, 0x1e0); + + // TODO: Not sure about IA32_SMRR_PHYSBASE & IA32_SMRR_PHYSMASK + + const IA32_MC_I_CTL2: (u32, u32) = (0x280, 0x29f); + + // Disabled via CPUID for non-host profiles + const IA32_INTEGRITY_STATUS: (u32, u32) = (0x2dc, 0x2dc); + + const IA32_FIXED_CTRI: (u32, u32) = (0x309, 0x30f); + + // IA32_PERF_CAPABILITIES is an MSR-based feature thus not listed here + + // Disabled via CPUID for non-host profiles + const IA32_FIXED_CTR_CTRL: (u32, u32) = (0x38d, 0x38d); + + // Disabled via CPUID for non-host profiles + const IA32_PERF_GLOBAL_STATUS: (u32, u32) = (0x38e, 0x38e); + + // Disabled via CPUID for non-host profiles + const IA32_PERF_GLOBAL_CTRL: (u32, u32) = (0x38f, 0x38f); + + // Disabled via CPUID for non-host profiles + const IA32_PERF_GLOBAL_STATUS_RESET: (u32, u32) = (0x390, 0x390); + + // Disabled via CPUID for non-host profiles + const IA32_PERF_GLOBAL_STATUS_SET: (u32, u32) = (0x391, 0x391); + + // Disabled via CPUID for non-host profiles + const IA32_PERF_GLOBAL_INUSE: (u32, u32) = (0x392, 0x392); + + // TODO: Not sure about this one, but seems to be related to performance monitoring which + // should be disabled for non-host CPU profiles. + const IA32_PEBS_ENABLE: (u32, u32) = (0x3f1, 0x3f1); + + const IA32_MC0_CTL: (u32, u32) = (0x400, 0x400); + const IA32_MC0_STATUS: (u32, u32) = (0x401, 0x401); + const IA32_MC0_ADDR: (u32, u32) = (0x402, 0x402); + const IA32_MC0_MISC: (u32, u32) = (0x403, 0x403); + const IA32_MC1_CTL: (u32, u32) = (0x404, 0x404); + const IA32_MC1_STATUS: (u32, u32) = (0x405, 0x405); + const IA32_MC1_ADDR: (u32, u32) = (0x406, 0x406); + + const IA32_MC1_MISC: (u32, u32) = (0x407, 0x407); + const IA32_MC2_CTL: (u32, u32) = (0x408, 0x408); + const IA32_MC2_STATUS: (u32, u32) = (0x409, 0x409); + const IA32_MC2_ADDR: (u32, u32) = (0x40a, 0x40a); + const IA32_MC2_MISC: (u32, u32) = (0x40b, 0x40b); + const IA32_MC3_CTL: (u32, u32) = (0x40c, 0x40c); + const IA32_MC3_STATUS: (u32, u32) = (0x40d, 0x40d); + const IA32_MC3_ADDR1: (u32, u32) = (0x40e, 0x40e); + const IA32_MC3_MISC: (u32, u32) = (0x40f, 0x40f); + const IA32_MC4_CTL: (u32, u32) = (0x410, 0x410); + const IA32_MC4_STATUS: (u32, u32) = (0x411, 0x411); + const IA32_MC4_ADDR: (u32, u32) = (0x412, 0x412); + const IA32_MC4_MISC: (u32, u32) = (0x413, 0x413); + const IA32_MC5_CTL: (u32, u32) = (0x414, 0x414); + const IA32_MC5_STATUS: (u32, u32) = (0x415, 0x415); + const IA32_MC5_ADDR: (u32, u32) = (0x416, 0x416); + const IA32_MC5_MISC: (u32, u32) = (0x417, 0x417); + const IA32_MC6_CTL: (u32, u32) = (0x418, 0x418); + + const IA32_MC6_STATUS: (u32, u32) = (0x419, 0x419); + const IA32_MC6_ADDR1: (u32, u32) = (0x41a, 0x41a); + const IA32_MC6_MISC: (u32, u32) = (0x41b, 0x41b); + const IA32_MC7_CTL: (u32, u32) = (0x41c, 0x41c); + const IA32_MC7_STATUS: (u32, u32) = (0x41d, 0x41d); + const IA32_MC7_ADDR: (u32, u32) = (0x41e, 0x41e); + const IA32_MC7_MISC: (u32, u32) = (0x41f, 0x41f); + const IA32_MC8_CTL: (u32, u32) = (0x420, 0x420); + const IA32_MC8_STATUS: (u32, u32) = (0x421, 0x421); + const IA32_MC8_ADDR: (u32, u32) = (0x422, 0x422); + const IA32_MC8_MISC: (u32, u32) = (0x423, 0x423); + const IA32_MC9_CTL: (u32, u32) = (0x424, 0x424); + const IA32_MC9_STATUS: (u32, u32) = (0x425, 0x425); + const IA32_MC9_ADDR: (u32, u32) = (0x426, 0x426); + const IA32_MC9_MISC: (u32, u32) = (0x427, 0x427); + const IA32_MC10_CTL: (u32, u32) = (0x428, 0x428); + const IA32_MC10_STATUS: (u32, u32) = (0x429, 0x429); + const IA32_MC10_ADDR: (u32, u32) = (0x42a, 0x42a); + const IA32_MC10_MISC: (u32, u32) = (0x42b, 0x42b); + + const IA32_MC11_CTL: (u32, u32) = (0x42c, 0x42c); + const IA32_MC11_STATUS: (u32, u32) = (0x42d, 0x42d); + const IA32_MC11_ADDR: (u32, u32) = (0x42e, 0x42e); + const IA32_MC11_MISC: (u32, u32) = (0x42f, 0x42f); + const IA32_MC12_CTL: (u32, u32) = (0x430, 0x430); + const IA32_MC12_STATUS: (u32, u32) = (0x431, 0x431); + const IA32_MC12_ADDR: (u32, u32) = (0x432, 0x432); + const IA32_MC12_MISC: (u32, u32) = (0x433, 0x433); + const IA32_MC13_CTL: (u32, u32) = (0x434, 0x434); + const IA32_MC13_STATUS: (u32, u32) = (0x435, 0x435); + const IA32_MC13_ADDR: (u32, u32) = (0x436, 0x436); + const IA32_MC13_MISC: (u32, u32) = (0x437, 0x437); + const IA32_MC14_CTL: (u32, u32) = (0x438, 0x438); + const IA32_MC14_STATUS: (u32, u32) = (0x439, 0x439); + const IA32_MC14_ADDR: (u32, u32) = (0x43a, 0x43a); + const IA32_MC14_MISC: (u32, u32) = (0x43b, 0x43b); + const IA32_MC15_CTL: (u32, u32) = (0x43c, 0x43c); + const IA32_MC15_STATUS: (u32, u32) = (0x43d, 0x43d); + + const IA32_MC15_ADDR: (u32, u32) = (0x43e, 0x43e); + const IA32_MC15_MISC: (u32, u32) = (0x43f, 0x43f); + const IA32_MC16_CTL: (u32, u32) = (0x440, 0x440); + const IA32_MC16_STATUS: (u32, u32) = (0x441, 0x441); + const IA32_MC16_ADDR: (u32, u32) = (0x442, 0x442); + const IA32_MC16_MISC: (u32, u32) = (0x443, 0x443); + const IA32_MC17_CTL: (u32, u32) = (0x444, 0x444); + const IA32_MC17_STATUS: (u32, u32) = (0x445, 0x445); + const IA32_MC17_ADDR: (u32, u32) = (0x446, 0x446); + const IA32_MC17_MISC: (u32, u32) = (0x447, 0x447); + const IA32_MC18_CTL: (u32, u32) = (0x448, 0x448); + const IA32_MC18_STATUS: (u32, u32) = (0x449, 0x449); + const IA32_MC18_ADDR: (u32, u32) = (0x44a, 0x44a); + const IA32_MC18_MISC: (u32, u32) = (0x44b, 0x44b); + const IA32_MC19_CTL: (u32, u32) = (0x44c, 0x44c); + const IA32_MC19_STATUS: (u32, u32) = (0x44d, 0x44d); + const IA32_MC19_ADDR: (u32, u32) = (0x44e, 0x44e); + const IA32_MC19_MISC: (u32, u32) = (0x44f, 0x44f); + const IA32_MC20_CTL: (u32, u32) = (0x450, 0x450); + + const IA32_MC20_STATUS: (u32, u32) = (0x451, 0x451); + const IA32_MC20_ADDR: (u32, u32) = (0x452, 0x452); + const IA32_MC20_MISC: (u32, u32) = (0x453, 0x453); + const IA32_MC21_CTL: (u32, u32) = (0x454, 0x454); + const IA32_MC21_STATUS: (u32, u32) = (0x455, 0x455); + const IA32_MC21_ADDR: (u32, u32) = (0x456, 0x456); + const IA32_MC21_MISC: (u32, u32) = (0x457, 0x457); + const IA32_MC22_CTL: (u32, u32) = (0x458, 0x458); + const IA32_MC22_STATUS: (u32, u32) = (0x459, 0x459); + const IA32_MC22_ADDR: (u32, u32) = (0x45a, 0x45a); + const IA32_MC22_MISC: (u32, u32) = (0x45b, 0x45b); + const IA32_MC23_CTL: (u32, u32) = (0x45c, 0x45c); + const IA32_MC23_STATUS: (u32, u32) = (0x45d, 0x45d); + const IA32_MC23_ADDR: (u32, u32) = (0x45e, 0x45e); + const IA32_MC23_MISC: (u32, u32) = (0x45f, 0x45f); + const IA32_MC24_CTL: (u32, u32) = (0x460, 0x460); + const IA32_MC24_STATUS: (u32, u32) = (0x461, 0x461); + const IA32_MC24_ADDR: (u32, u32) = (0x462, 0x462); + + const IA32_MC24_MISC: (u32, u32) = (0x463, 0x463); + const IA32_MC25_CTL: (u32, u32) = (0x464, 0x464); + const IA32_MC25_STATUS: (u32, u32) = (0x465, 0x465); + const IA32_MC25_ADDR: (u32, u32) = (0x466, 0x466); + const IA32_MC25_MISC: (u32, u32) = (0x467, 0x467); + const IA32_MC26_CTL: (u32, u32) = (0x468, 0x468); + const IA32_MC26_STATUS: (u32, u32) = (0x469, 0x469); + const IA32_MC26_ADDR: (u32, u32) = (0x46a, 0x46a); + const IA32_MC26_MISC: (u32, u32) = (0x46b, 0x46b); + const IA32_MC27_CTL: (u32, u32) = (0x46c, 0x46c); + const IA32_MC27_STATUS: (u32, u32) = (0x46d, 0x46d); + const IA32_MC27_ADDR: (u32, u32) = (0x46e, 0x46e); + const IA32_MC27_MISC: (u32, u32) = (0x46f, 0x46f); + const IA32_MC28_CTL: (u32, u32) = (0x470, 0x470); + const IA32_MC28_STATUS: (u32, u32) = (0x471, 0x471); + const IA32_MC28_ADDR: (u32, u32) = (0x472, 0x472); + const IA32_MC28_MISC: (u32, u32) = (0x473, 0x473); + const IA32_MC29_CTL: (u32, u32) = (0x474, 0x474); + const IA32_MC29_STATUS: (u32, u32) = (0x475, 0x475); + + const IA32_MC29_ADDR: (u32, u32) = (0x476, 0x476); + const IA32_MC29_MISC: (u32, u32) = (0x477, 0x477); + const IA32_MC30_CTL: (u32, u32) = (0x478, 0x478); + const IA32_MC30_STATUS: (u32, u32) = (0x479, 0x479); + const IA32_MC30_ADDR: (u32, u32) = (0x47a, 0x47a); + const IA32_MC30_MISC: (u32, u32) = (0x47b, 0x47b); + const IA32_MC31_CTL: (u32, u32) = (0x47c, 0x47c); + const IA32_MC31_STATUS: (u32, u32) = (0x47d, 0x47d); + const IA32_MC31_ADDR: (u32, u32) = (0x47e, 0x47e); + const IA32_MC31_MISC: (u32, u32) = (0x47f, 0x47f); + + const IA32_A_PMC0: (u32, u32) = (0x4c1, 0x4c1); + const IA32_A_PMC1: (u32, u32) = (0x4c2, 0x4c2); + const IA32_A_PMC2: (u32, u32) = (0x4c3, 0x4c3); + const IA32_A_PMC3: (u32, u32) = (0x4c4, 0x4c4); + const IA32_A_PMC4: (u32, u32) = (0x4c5, 0x4c5); + const IA32_A_PMC5: (u32, u32) = (0x4c6, 0x4c6); + const IA32_A_PMC6: (u32, u32) = (0x4c7, 0x4c7); + const IA32_A_PMC7: (u32, u32) = (0x4c8, 0x4c8); + const IA32_A_PMC8: (u32, u32) = (0x4c9, 0x4c9); + const IA32_A_PMC9: (u32, u32) = (0x4ca, 0x4ca); + + const IA32_MCG_EXT_CTL: (u32, u32) = (0x4d0, 0x4d0); + + // SGX is disabled via CPUID for non-host CPU profiles + const IA32_SGX_SVN_STATUS: (u32, u32) = (0x500, 0x500); + + // Disabled via CPUID for non-host CPU profiles + const IA32_RTIT_OUTPUT_BASE: (u32, u32) = (0x560, 0x560); + + // Disabled via CPUID for non-host CPU profiles + const IA32_RTIT_OUTPUT_MASK_PTRS: (u32, u32) = (0x561, 0x561); + + // Disabled via CPUID for non-host CPU profiles + const IA32_RTIT_CTL: (u32, u32) = (0x570, 0x570); + + // Disabled via CPUID for non-host CPU profiles + const IA32_RTIT_STATUS: (u32, u32) = (0x571, 0x571); + + // Disabled via CPU profiles + const IA32_RTIT_CR3_MATCH: (u32, u32) = (0x572, 0x572); + + const IA32_RTIT_ADDR0_A: (u32, u32) = (0x580, 0x580); + const IA32_RTIT_ADDR0_B: (u32, u32) = (0x581, 0x581); + const IA32_RTIT_ADDR1_A: (u32, u32) = (0x582, 0x582); + const IA32_RTIT_ADDR1_B: (u32, u32) = (0x583, 0x583); + const IA32_RTIT_ADDR2_A: (u32, u32) = (0x584, 0x584); + const IA32_RTIT_ADDR2_B: (u32, u32) = (0x585, 0x585); + const IA32_RTIT_ADDR3_A: (u32, u32) = (0x586, 0x586); + const IA32_RTIT_ADDR3_B: (u32, u32) = (0x587, 0x587); + + // Disabled via CPUID for non-host CPU profiles + const IA32_DS_AREA: (u32, u32) = (0x600, 0x600); + + // TODO: IA32_TSC_DEADLINE should be available because the TSC_DEADLINE CPUID bit + // is set by CHV unconditionally. The availability of this MSR probably needs to be + // handled by CHV itself and not the CPU profiles + + // Disabled via CPUID for non-host CPU profiles + const IA32_PKRS: (u32, u32) = (0x6e1, 0x6e1); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PM_ENABLE: (u32, u32) = (0x770, 0x770); + + // Disabled via CPUID for non-host CPU profiles + const IA32_HWP_CAPABILITIES: (u32, u32) = (0x771, 0x771); + + // Disabled via CPUID for non-host CPU profiles + const IA32_HWP_REQUEST_PKG: (u32, u32) = (0x772, 0x772); + + // Disabled via CPUID for non-host CPU profiles + const IA32_HWP_INTERRUPT: (u32, u32) = (0x773, 0x773); + + // Disabled via CPUID for non-host CPU profiles + const IA32_HWP_REQUEST: (u32, u32) = (0x774, 0x774); + + // TODO: Can we also deny IA32_PECI_HWP_REQUEST_INFO? + + // Disabled via CPUID for non-host CPU profiles + const IA32_HWP_CTL: (u32, u32) = (0x776, 0x776); + + // Disabled via CPUID for non-host CPU profiles + const IA32_HWP_STATUS: (u32, u32) = (0x777, 0x777); + + // TODO: Currently permitted via IA32_ARCH_CAPABILITIES (bit 22), + // but that bit should probably have policy Static(0) ? + const IA32_MCU_EXT_SERVICE: (u32, u32) = (0x7a3, 0x7a3); + + const IA32_MCU_ROLLBACK_MIN_ID: (u32, u32) = (0x7a4, 0x7a4); + + // TODO: Not sure about IA32_MCU_STAGING_MBOX_ADDR + + const IA32_ROLLBACK_SIGN_ID_0: (u32, u32) = (0x7b0, 0x7b0); + const IA32_ROLLBACK_SIGN_ID_1: (u32, u32) = (0x7b1, 0x7b1); + const IA32_ROLLBACK_SIGN_ID_2: (u32, u32) = (0x7b2, 0x7b2); + const IA32_ROLLBACK_SIGN_ID_3: (u32, u32) = (0x7b3, 0x7b3); + const IA32_ROLLBACK_SIGN_ID_4: (u32, u32) = (0x7b4, 0x7b4); + const IA32_ROLLBACK_SIGN_ID_5: (u32, u32) = (0x7b5, 0x7b5); + const IA32_ROLLBACK_SIGN_ID_6: (u32, u32) = (0x7b6, 0x7b6); + const IA32_ROLLBACK_SIGN_ID_7: (u32, u32) = (0x7b7, 0x7b7); + const IA32_ROLLBACK_SIGN_ID_8: (u32, u32) = (0x7b8, 0x7b8); + const IA32_ROLLBACK_SIGN_ID_9: (u32, u32) = (0x7b9, 0x7b9); + const IA32_ROLLBACK_SIGN_ID_10: (u32, u32) = (0x7ba, 0x7ba); + const IA32_ROLLBACK_SIGN_ID_11: (u32, u32) = (0x7bb, 0x7bb); + const IA32_ROLLBACK_SIGN_ID_12: (u32, u32) = (0x7bc, 0x7bc); + const IA32_ROLLBACK_SIGN_ID_13: (u32, u32) = (0x7bd, 0x7bd); + const IA32_ROLLBACK_SIGN_ID_14: (u32, u32) = (0x7be, 0x7be); + const IA32_ROLLBACK_SIGN_ID_15: (u32, u32) = (0x7bf, 0x7bf); + + // Disabled via CPUID for non-host CPU profiles + const IA32_TME_CAPABILITY: (u32, u32) = (0x981, 0x981); + + // Disabled via CPUID for non-host CPU profiles + const IA32_TME_ACTIVATE: (u32, u32) = (0x982, 0x982); + + // Disabled via CPUID for non-host CPU profiles + const IA32_TME_EXCLUDE_MASK: (u32, u32) = (0x983, 0x983); + + // Disabled via CPUID for non-host CPU profiles + const IA32_TME_EXCLUDE_BASE: (u32, u32) = (0x984, 0x984); + + // Disabled via CPUID for non-host CPU profiles + const IA32_UINTR_RR: (u32, u32) = (0x985, 0x985); + + // Disabled via CPUID for non-host CPU profiles + const IA32_UINTR_HANDLER: (u32, u32) = (0x986, 0x986); + + // Disabled via CPUID for non-host CPU profiles + const IA32_UINTR_STACKADJUST: (u32, u32) = (0x987, 0x987); + + // Disabled via CPUID for non-host CPU profiles + const IA32_UINTR_MISC: (u32, u32) = (0x988, 0x988); + + // Disabled via CPUID for non-host CPU profiles + const IA32_UINTR_PD: (u32, u32) = (0x989, 0x989); + + // Disabled via CPUID for non-host CPU profiles + const IA32_UINTR_TT: (u32, u32) = (0x98a, 0x98a); + + // Disabled via CPUID for non-host CPU profiles + const IA32_COPY_STATUS: (u32, u32) = (0x990, 0x990); + + // Disabled via CPUID for non-host CPU profiles + const IA32_IWKEYBACKUP_STATUS: (u32, u32) = (0x991, 0x991); + + const IA32_TME_CLEAR_SAVED_KEY: (u32, u32) = (0x9fb, 0x9fb); + + // Disabled via CPUID for non-host CPU profiles + const IA32_DEBUG_INTERFACE: (u32, u32) = (0xc80, 0xc80); + + // Disabled via CPUID for non-host CPU profiles + const IA32_L3_QOS_CFG: (u32, u32) = (0xc81, 0xc81); + + // Disabled via CPUID + const IA32_L2_QOS_CFG: (u32, u32) = (0xc82, 0xc82); + + // Disabled via CPUID + const IA32_L3_IO_QOS_CFG: (u32, u32) = (0xc83, 0xc83); + + // TODO: Not sure about IA32_RESOURCE_PRIORITY and IA32_RESOURCE_PRIORITY_PKG + + // Disabled via CPUID for non-host CPU profiles + const IA32_QM_EVTSEL: (u32, u32) = (0xc8d, 0xc8d); + + // Disabled via CPUID for non-host CPU profiles + const IA32_QM_CTR: (u32, u32) = (0xc8e, 0xc8e); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PQR_ASSOC: (u32, u32) = (0xc8f, 0xc8f); + + // Disabled via CPUID for non-host CPU profiles + const IA32_L3_MASK_0: (u32, u32) = (0xc90, 0xc90); + + const IA32_L3_MASK_N: (u32, u32) = (0xc91, 0xd8f); + + // Disabled via CPUID for non-host CPU profiles + const IA32_L2_MASK_0: (u32, u32) = (0xd10, 0xd10); + + // Disabled via CPUID for non-host CPU profiles + const IA32_L2_MASK_N: (u32, u32) = (0xd11, 0xd4f); + + // Disabled via CPUID for non-host CPU profiles + const IA32_L2_QOS_EXT_BW_THRTL_I: (u32, u32) = (0xd50, 0xd5e); + + // Disabled via CPUID for non-host CPU profiles + const IA32_BNDCFGS: (u32, u32) = (0xd90, 0xd90); + + // Disabled via CPUID for non-host CPU profiles + const IA32_COPY_LOCAL_TO_PLATFORM: (u32, u32) = (0xd91, 0xd91); + + // Disabled via CPUID for non-host CPU profiles + const IA32_COPY_PLATFORM_TO_LOCAL: (u32, u32) = (0xd92, 0xd92); + + // TODO: Not sure about IA32_PASID + + // Disabled via CPUID for non-host CPU profiles + const IA32_PKG_HDC_CTL: (u32, u32) = (0xdb0, 0xdb0); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PM_CTL1: (u32, u32) = (0xdb1, 0xdb1); + + // Disabled via CPUID for non-host CPU profiles + const IA32_THREAD_STALL: (u32, u32) = (0xdb2, 0xdb2); + + // Disabled via CPUID for non-host CPU profiles + const IA32_QOS_CORE_BW_THRTL_0: (u32, u32) = (0xe00, 0xe00); + + // Disabled via CPUID for non-host CPU profiles + const IA32_QOS_CORE_BW_THRTL_1: (u32, u32) = (0xe01, 0xe01); + + // TODO: Is it OK to disable this for CPU profiles? + // Note that we have CPUID 0x7.EDX.[19] = 0 (ARCH_LBR) + const IA32_LBR_X_INFO: (u32, u32) = (0x1200, 0x121f); + + // TDX related. + const IA32_SEAMRR_BASE: (u32, u32) = (0x1400, 0x1400); + + // TDX related. + const IA32_SEAMRR_MASK: (u32, u32) = (0x1401, 0x1401); + + // Disabled via ARCH_CAPABILITIES for non-host CPU profiles + // TODO: Check that deny policy is compatible with + // the policy for IA32_ARCH_COMPATIBILITY[9] + const IA32_MCU_CONTROL: (u32, u32) = (0x1406, 1406); + + const IA32_LBR_CTL: (u32, u32) = (0x14ce, 0x14ce); + + const IA32_LBR_DEPTH: (u32, u32) = (0x14cf, 0x14cf); + + const IA32_LBR_X_FROM_IP: (u32, u32) = (0x1500, 0x151f); + + const IA32_LBR_X_TO_IP: (u32, u32) = (0x1600, 0x161f); + + // Disabled via CPUID for non-host CPU profiles + const IA32_HW_FEEDBACK_PTR: (u32, u32) = (0x17d0, 0x17d0); + + // Disabled via CPUID for non-host CPU profiles + const IA32_HW_FEEDBACK_CONFIG: (u32, u32) = (0x17d1, 0x17d1); + + // Disabled via CPUID for non-host CPU profiles + const IA32_HW_FEEDBACK_THREAD_CHAR: (u32, u32) = (0x17d2, 0x17d2); + + const IA32_HW_FEEDBACK_THREAD_CONFIG: (u32, u32) = (0x17d4, 0x17d4); + + const IA32_HRESET_ENABLE: (u32, u32) = (0x17da, 0x17da); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP0_CTR: (u32, u32) = (0x1900, 0x1900); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP0_CFG_A: (u32, u32) = (0x1901, 0x1901); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP0_CFG_C: (u32, u32) = (0x1903, 0x1903); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP1_CTR: (u32, u32) = (0x1904, 0x1904); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP1_CFG_A: (u32, u32) = (0x1905, 0x1905); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP1_CFG_C: (u32, u32) = (0x1907, 0x1907); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP2_CTR: (u32, u32) = (0x1908, 0x1908); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP2_CFG_A: (u32, u32) = (0x1909, 0x1909); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP2_CFG_B: (u32, u32) = (0x190a, 0x190a); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP2_CFG_C: (u32, u32) = (0x190b, 0x190b); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP3_CTR: (u32, u32) = (0x190c, 0x190c); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP3_CFG_A: (u32, u32) = (0x190d, 0x190d); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP3_CFG_B: (u32, u32) = (0x190e, 0x190e); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP3_CFG_C: (u32, u32) = (0x190f, 0x190f); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP4_CTR: (u32, u32) = (0x1910, 0x1910); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP4_CFG_A: (u32, u32) = (0x1911, 0x1911); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP4_CFG_B: (u32, u32) = (0x1912, 0x1912); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP4_CFG_C: (u32, u32) = (0x1913, 0x1913); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP5_CTR: (u32, u32) = (0x1914, 0x1914); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP5_CFG_A: (u32, u32) = (0x1915, 0x1915); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP5_CFG_B: (u32, u32) = (0x1916, 0x1916); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP5_CFG_C: (u32, u32) = (0x1917, 0x1917); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP6_CTR: (u32, u32) = (0x1918, 0x1918); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP6_CFG_A: (u32, u32) = (0x1919, 0x1919); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP6_CFG_B: (u32, u32) = (0x191a, 0x191a); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP6_CFG_C: (u32, u32) = (0x191b, 0x191b); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP7_CTR: (u32, u32) = (0x191c, 0x191c); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP7_CFG_A: (u32, u32) = (0x191d, 0x191d); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP7_CFG_B: (u32, u32) = (0x191e, 0x191e); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP7_CFG_C: (u32, u32) = (0x191f, 0x191f); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP8_CTR: (u32, u32) = (0x1920, 0x1920); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP8_CFG_A: (u32, u32) = (0x1921, 0x1921); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP9_CTR: (u32, u32) = (0x1924, 0x1924); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_GP9_CFG_A: (u32, u32) = (0x1925, 0x1925); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_FX0_CTR: (u32, u32) = (0x1980, 0x1980); + + const IA32_PMC_FX0_CFG_B: (u32, u32) = (0x1982, 0x1982); + const IA32_PMC_FX0_CFG_C: (u32, u32) = (0x1983, 0x1983); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_FX1_CTR: (u32, u32) = (0x1984, 0x1984); + const IA32_PMC_FX1_CFG_B: (u32, u32) = (0x1986, 0x1986); + const IA32_PMC_FX1_CFG_C: (u32, u32) = (0x1987, 0x1987); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_FX2_CTR: (u32, u32) = (0x1988, 0x1988); + + const IA32_PMC_FX2_CFG_C: (u32, u32) = (0x198b, 0x198b); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_FX3_CTR: (u32, u32) = (0x198c, 0x198c); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_FX4_CTR: (u32, u32) = (0x1990, 0x1990); + const IA32_PMC_FX4_CFG_C: (u32, u32) = (0x1993, 0x1993); + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_FX5_CTR: (u32, u32) = (0x1994, 0x1994); + const IA32_PMC_FX5_CFG_C: (u32, u32) = (0x1997, 0x1997); + + // Disabled via CPUID for non-host CPU profiles + const IA32_PMC_FX6_CTR: (u32, u32) = (0x1998, 0x1998); + const IA32_PMC_FX6_CFG_C: (u32, u32) = (0x199b, 0x199b); + + // TODO: Check IA32_UARCH_MISC_CTL + // + + /// A list of ARCHITECTURAL MSR register addresses that are forbidden for all non-host CPU profiles and also not + /// considered MSR-based FEATURE indices by KVM. + pub(in crate::x86_64) const FORBIDDEN_IA32_MSR_RANGES: [(u32, u32); 345] = [ + // TODO: Not sure about IA32_P5_MC_ADDR & IA32_P5_MC_TYPE + IA32_MONITOR_FILTER_SIZE, + // TODO: Not sure about this one + IA32_PLATFORM_ID, + /// Only available is CPUID 0x7.0x1.EBX[0] = 1, but this is always 0 for non-host CPU profiles + IA32_PPIN_CTL, + /// Only available is CPUID 0x7.0x1.EBX[0] = 1, but this is always 0 for non-host CPU profiles + IA32_PPIN, + /// Used for microcode updates. Should not be available for guests. + IA32_BIOS_UPDT_TRIG, + /// Currently only related to Secure enclaves/Keylocker which is not available for non-host CPU profiles + IA32_FEATURE_ACTIVATION, + /// Related to microcode updates + IA32_MCU_ENUMERATION, + IA32_MCU_STATUS, + /// Related to total memory encryption + IA32_MKTME_KEYID_PARTITIONING, + // TODO: Not sure what to do about IA32_BIOS_SIGN_ID (note that it is also a MSR-based feature according to KVM) + IA32_SGXLEPUBKEYHASH0, + IA32_SGXLEPUBKEYHASH1, + IA32_SGXLEPUBKEYHASH2, + IA32_SGXLEPUBKEYHASH3, + IA32_SGXLEPUBKEYHASH4, + IA32_SGXLEPUBKEYHASH5, + // TODO: Check this + IA32_SMBASE, + IA32_MISC_PACKAGE_CTLS, + // TODO: IA32_X2APIC_DISABLE_STATUS + IA32_PMC0, + IA32_PMC1, + IA32_PMC2, + IA32_PMC3, + IA32_PMC4, + IA32_PMC5, + IA32_PMC6, + IA32_PMC7, + IA32_PMC8, + IA32_PMC9, + IA32_CORE_CAPABILITIES, + IA32_UMWAIT_CONTROL, + IA32_CLOCK_MODULATION, + IA32_PLI_SSP, + IA32_INTERRUPT_SSP_TABLE_ADDR, + // Disabled by CPUID for non-host CPU profiles + IA32_MPERF, + IA32_APERF, + IA32_TSX_FORCE_ABORT, + // Disabled via static IA32_ARCH_CAPABILITIES bit for non-host CPU profiles + IA32_TSX_CTRL, + // NOTE: IA32_MCU_OPT_CTRL must necessarily be available, due to + // what we set in CPUID for some CPU profiles (inherit policy) + + // TODO: Don't know about IA32_SYSENTER_CS, IA32_SYSENTER_ESP, + // IA32_SYSENTER_EIP + // + + // TODO: Not sure if we can/should deny this MSR, but + // it doesn't really make sense to have it available in + // a virtualized environment + // + // If we keep it denied we should document that + // even for 06_01H one cannot rely on the existence of this MSR + IA32_MCG_CAP, + // TODO: Also not sure if we may deny this MSR + IA32_MCG_STATUS, + // TODO: Can we deny this? + IA32_MCG_CTL, + // TODO: 0x180- 0x185 is reserved, we should not list these MSRS at all + /// Disabled via CPUID for all non-host CPU profiles + IA32_PERFEVTSEL0, + IA32_PERFEVTSEL1, + IA32_PERFEVTSEL2, + IA32_PERFEVTSEL3, + IA32_PERFEVTSEL4, + IA32_PERFEVTSEL5, + IA32_PERFEVTSEL6, + IA32_PERFEVTSEL7, + IA32_PERFEVTSEL8, + IA32_PERFEVTSEL9, + // TODO: 0x18a - 0x194 is reserved and should not be included in any list + + // TODO: 0x196, 197 is reserved and should not be included in any list + // + IA32_PERF_STATUS, + IA32_PERF_CTL, + // Disabled via CPUID for non-host profiles + IA32_THERM_INTERRUPT, + // Disabled via CPUID for non-host profiles + IA32_THERM_STATUS, + // TODO: Consider disabling IA32_MISC_ENABLE + + // Disabled via CPUID for non-host profiles + IA32_ENERGY_PERF_BIAS, + // Disabled via CPUID for non-host profiles + IA32_PACKAGE_THERM_STATUS, + // Disabled via CPUID for non-host profiles + IA32_PACKAGE_THERM_INTERRUPT, + IA32_DEBUGCTL, + IA32_LER_FROM_IP, + IA32_LER_TO_IP, + IA32_LER_INFO, + // TODO: Not sure about IA32_SMRR_PHYSBASE & IA32_SMRR_PHYSMASK + IA32_MC_I_CTL2, + // Disabled via CPUID for non-host profiles + IA32_INTEGRITY_STATUS, + IA32_FIXED_CTRI, + // IA32_PERF_CAPABILITIES is an MSR-based feature thus not listed here + + // Disabled via CPUID for non-host profiles + IA32_FIXED_CTR_CTRL, + // Disabled via CPUID for non-host profiles + IA32_PERF_GLOBAL_STATUS, + // Disabled via CPUID for non-host profiles + IA32_PERF_GLOBAL_CTRL, + // Disabled via CPUID for non-host profiles + IA32_PERF_GLOBAL_STATUS_RESET, + // Disabled via CPUID for non-host profiles + IA32_PERF_GLOBAL_STATUS_SET, + // Disabled via CPUID for non-host profiles + IA32_PERF_GLOBAL_INUSE, + // TODO: Not sure about this one, but seems to be related to performance monitoring which + // should be disabled for non-host CPU profiles. + IA32_PEBS_ENABLE, + IA32_MC0_CTL, + IA32_MC0_STATUS, + IA32_MC0_ADDR, + IA32_MC0_MISC, + IA32_MC1_CTL, + IA32_MC1_STATUS, + IA32_MC1_ADDR, + IA32_MC1_MISC, + IA32_MC2_CTL, + IA32_MC2_STATUS, + IA32_MC2_ADDR, + IA32_MC2_MISC, + IA32_MC3_CTL, + IA32_MC3_STATUS, + IA32_MC3_ADDR1, + IA32_MC3_MISC, + IA32_MC4_CTL, + IA32_MC4_STATUS, + IA32_MC4_ADDR, + IA32_MC4_MISC, + IA32_MC5_CTL, + IA32_MC5_STATUS, + IA32_MC5_ADDR, + IA32_MC5_MISC, + IA32_MC6_CTL, + IA32_MC6_STATUS, + IA32_MC6_ADDR1, + IA32_MC6_MISC, + IA32_MC7_CTL, + IA32_MC7_STATUS, + IA32_MC7_ADDR, + IA32_MC7_MISC, + IA32_MC8_CTL, + IA32_MC8_STATUS, + IA32_MC8_ADDR, + IA32_MC8_MISC, + IA32_MC9_CTL, + IA32_MC9_STATUS, + IA32_MC9_ADDR, + IA32_MC9_MISC, + IA32_MC10_CTL, + IA32_MC10_STATUS, + IA32_MC10_ADDR, + IA32_MC10_MISC, + IA32_MC11_CTL, + IA32_MC11_STATUS, + IA32_MC11_ADDR, + IA32_MC11_MISC, + IA32_MC12_CTL, + IA32_MC12_STATUS, + IA32_MC12_ADDR, + IA32_MC12_MISC, + IA32_MC13_CTL, + IA32_MC13_STATUS, + IA32_MC13_ADDR, + IA32_MC13_MISC, + IA32_MC14_CTL, + IA32_MC14_STATUS, + IA32_MC14_ADDR, + IA32_MC14_MISC, + IA32_MC15_CTL, + IA32_MC15_STATUS, + IA32_MC15_ADDR, + IA32_MC15_MISC, + IA32_MC16_CTL, + IA32_MC16_STATUS, + IA32_MC16_ADDR, + IA32_MC16_MISC, + IA32_MC17_CTL, + IA32_MC17_STATUS, + IA32_MC17_ADDR, + IA32_MC17_MISC, + IA32_MC18_CTL, + IA32_MC18_STATUS, + IA32_MC18_ADDR, + IA32_MC18_MISC, + IA32_MC19_CTL, + IA32_MC19_STATUS, + IA32_MC19_ADDR, + IA32_MC19_MISC, + IA32_MC20_CTL, + IA32_MC20_STATUS, + IA32_MC20_ADDR, + IA32_MC20_MISC, + IA32_MC21_CTL, + IA32_MC21_STATUS, + IA32_MC21_ADDR, + IA32_MC21_MISC, + IA32_MC22_CTL, + IA32_MC22_STATUS, + IA32_MC22_ADDR, + IA32_MC22_MISC, + IA32_MC23_CTL, + IA32_MC23_STATUS, + IA32_MC23_ADDR, + IA32_MC23_MISC, + IA32_MC24_CTL, + IA32_MC24_STATUS, + IA32_MC24_ADDR, + IA32_MC24_MISC, + IA32_MC25_CTL, + IA32_MC25_STATUS, + IA32_MC25_ADDR, + IA32_MC25_MISC, + IA32_MC26_CTL, + IA32_MC26_STATUS, + IA32_MC26_ADDR, + IA32_MC26_MISC, + IA32_MC27_CTL, + IA32_MC27_STATUS, + IA32_MC27_ADDR, + IA32_MC27_MISC, + IA32_MC28_CTL, + IA32_MC28_STATUS, + IA32_MC28_ADDR, + IA32_MC28_MISC, + IA32_MC29_CTL, + IA32_MC29_STATUS, + IA32_MC29_ADDR, + IA32_MC29_MISC, + IA32_MC30_CTL, + IA32_MC30_STATUS, + IA32_MC30_ADDR, + IA32_MC30_MISC, + IA32_MC31_CTL, + IA32_MC31_STATUS, + IA32_MC31_ADDR, + IA32_MC31_MISC, + IA32_A_PMC0, + IA32_A_PMC1, + IA32_A_PMC2, + IA32_A_PMC3, + IA32_A_PMC4, + IA32_A_PMC5, + IA32_A_PMC6, + IA32_A_PMC7, + IA32_A_PMC8, + IA32_A_PMC9, + IA32_MCG_EXT_CTL, + // SGX is disabled via CPUID for non-host CPU profiles + IA32_SGX_SVN_STATUS, + // Disabled via CPUID for non-host CPU profiles + IA32_RTIT_OUTPUT_BASE, + // Disabled via CPUID for non-host CPU profiles + IA32_RTIT_OUTPUT_MASK_PTRS, + // Disabled via CPUID for non-host CPU profiles + IA32_RTIT_CTL, + // Disabled via CPUID for non-host CPU profiles + IA32_RTIT_STATUS, + // Disabled via CPU profiles + IA32_RTIT_CR3_MATCH, + IA32_RTIT_ADDR0_A, + IA32_RTIT_ADDR0_B, + IA32_RTIT_ADDR1_A, + IA32_RTIT_ADDR1_B, + IA32_RTIT_ADDR2_A, + IA32_RTIT_ADDR2_B, + IA32_RTIT_ADDR3_A, + IA32_RTIT_ADDR3_B, + // Disabled via CPUID for non-host CPU profiles + IA32_DS_AREA, + // TODO: IA32_TSC_DEADLINE should be available because the TSC_DEADLINE CPUID bit + // is set by CHV unconditionally. The availability of this MSR probably needs to be + // handled by CHV itself and not the CPU profiles + + // Disabled via CPUID for non-host CPU profiles + IA32_PKRS, + // Disabled via CPUID for non-host CPU profiles + IA32_PM_ENABLE, + // Disabled via CPUID for non-host CPU profiles + IA32_HWP_CAPABILITIES, + // Disabled via CPUID for non-host CPU profiles + IA32_HWP_REQUEST_PKG, + // Disabled via CPUID for non-host CPU profiles + IA32_HWP_INTERRUPT, + // Disabled via CPUID for non-host CPU profiles + IA32_HWP_REQUEST, + // TODO: Can we also deny IA32_PECI_HWP_REQUEST_INFO? + + // Disabled via CPUID for non-host CPU profiles + IA32_HWP_CTL, + // Disabled via CPUID for non-host CPU profiles + IA32_HWP_STATUS, + // TODO: Currently permitted via IA32_ARCH_CAPABILITIES (bit 22), + // but that bit should probably have policy Static(0) ? + IA32_MCU_EXT_SERVICE, + IA32_MCU_ROLLBACK_MIN_ID, + // TODO: Not sure about IA32_MCU_STAGING_MBOX_ADDR + IA32_ROLLBACK_SIGN_ID_0, + IA32_ROLLBACK_SIGN_ID_1, + IA32_ROLLBACK_SIGN_ID_2, + IA32_ROLLBACK_SIGN_ID_3, + IA32_ROLLBACK_SIGN_ID_4, + IA32_ROLLBACK_SIGN_ID_5, + IA32_ROLLBACK_SIGN_ID_6, + IA32_ROLLBACK_SIGN_ID_7, + IA32_ROLLBACK_SIGN_ID_8, + IA32_ROLLBACK_SIGN_ID_9, + IA32_ROLLBACK_SIGN_ID_10, + IA32_ROLLBACK_SIGN_ID_11, + IA32_ROLLBACK_SIGN_ID_12, + IA32_ROLLBACK_SIGN_ID_13, + IA32_ROLLBACK_SIGN_ID_14, + IA32_ROLLBACK_SIGN_ID_15, + // Disabled via CPUID for non-host CPU profiles + IA32_TME_CAPABILITY, + // Disabled via CPUID for non-host CPU profiles + IA32_TME_ACTIVATE, + // Disabled via CPUID for non-host CPU profiles + IA32_TME_EXCLUDE_MASK, + // Disabled via CPUID for non-host CPU profiles + IA32_TME_EXCLUDE_BASE, + // Disabled via CPUID for non-host CPU profiles + IA32_UINTR_RR, + // Disabled via CPUID for non-host CPU profiles + IA32_UINTR_HANDLER, + // Disabled via CPUID for non-host CPU profiles + IA32_UINTR_STACKADJUST, + // Disabled via CPUID for non-host CPU profiles + IA32_UINTR_MISC, + // Disabled via CPUID for non-host CPU profiles + IA32_UINTR_PD, + // Disabled via CPUID for non-host CPU profiles + IA32_UINTR_TT, + // Disabled via CPUID for non-host CPU profiles + IA32_COPY_STATUS, + // Disabled via CPUID for non-host CPU profiles + IA32_IWKEYBACKUP_STATUS, + IA32_TME_CLEAR_SAVED_KEY, + // Disabled via CPUID for non-host CPU profiles + IA32_DEBUG_INTERFACE, + // Disabled via CPUID for non-host CPU profiles + IA32_L3_QOS_CFG, + // Disabled via CPUID + IA32_L2_QOS_CFG, + // Disabled via CPUID + IA32_L3_IO_QOS_CFG, + // TODO: Not sure about IA32_RESOURCE_PRIORITY and IA32_RESOURCE_PRIORITY_PKG + + // Disabled via CPUID for non-host CPU profiles + IA32_QM_EVTSEL, + // Disabled via CPUID for non-host CPU profiles + IA32_QM_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PQR_ASSOC, + // Disabled via CPUID for non-host CPU profiles + IA32_L3_MASK_0, + IA32_L3_MASK_N, + // Disabled via CPUID for non-host CPU profiles + IA32_L2_MASK_0, + // Disabled via CPUID for non-host CPU profiles + IA32_L2_MASK_N, + // Disabled via CPUID for non-host CPU profiles + IA32_L2_QOS_EXT_BW_THRTL_I, + // Disabled via CPUID for non-host CPU profiles + IA32_BNDCFGS, + // Disabled via CPUID for non-host CPU profiles + IA32_COPY_LOCAL_TO_PLATFORM, + // Disabled via CPUID for non-host CPU profiles + IA32_COPY_PLATFORM_TO_LOCAL, + // TODO: Not sure about IA32_PASID + + // Disabled via CPUID for non-host CPU profiles + IA32_PKG_HDC_CTL, + // Disabled via CPUID for non-host CPU profiles + IA32_PM_CTL1, + // Disabled via CPUID for non-host CPU profiles + IA32_THREAD_STALL, + // Disabled via CPUID for non-host CPU profiles + IA32_QOS_CORE_BW_THRTL_0, + // Disabled via CPUID for non-host CPU profiles + IA32_QOS_CORE_BW_THRTL_1, + // TODO: Is it OK to disable this for CPU profiles? + // Note that we have CPUID 0x7.EDX.[19] = 0 (ARCH_LBR) + IA32_LBR_X_INFO, + // TDX related. + IA32_SEAMRR_BASE, + // TDX related. + IA32_SEAMRR_MASK, + // Disabled via ARCH_CAPABILITIES for non-host CPU profiles + IA32_MCU_CONTROL, + IA32_LBR_CTL, + IA32_LBR_DEPTH, + IA32_LBR_X_FROM_IP, + IA32_LBR_X_TO_IP, + // Disabled via CPUID for non-host CPU profiles + IA32_HW_FEEDBACK_PTR, + // Disabled via CPUID for non-host CPU profiles + IA32_HW_FEEDBACK_CONFIG, + // Disabled via CPUID for non-host CPU profiles + IA32_HW_FEEDBACK_THREAD_CHAR, + IA32_HW_FEEDBACK_THREAD_CONFIG, + IA32_HRESET_ENABLE, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP0_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP0_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP0_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP1_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP1_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP1_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP2_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP2_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP2_CFG_B, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP2_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP3_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP3_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP3_CFG_B, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP3_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP4_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP4_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP4_CFG_B, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP4_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP5_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP5_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP5_CFG_B, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP5_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP6_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP6_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP6_CFG_B, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP6_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP7_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP7_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP7_CFG_B, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP7_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP8_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP8_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP9_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_GP9_CFG_A, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_FX0_CTR, + IA32_PMC_FX0_CFG_B, + IA32_PMC_FX0_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_FX1_CTR, + IA32_PMC_FX1_CFG_B, + IA32_PMC_FX1_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_FX2_CTR, + IA32_PMC_FX2_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_FX3_CTR, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_FX4_CTR, + IA32_PMC_FX4_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_FX5_CTR, + IA32_PMC_FX5_CFG_C, + // Disabled via CPUID for non-host CPU profiles + IA32_PMC_FX6_CTR, + IA32_PMC_FX6_CFG_C, + // TODO: Check IA32_UARCH_MISC_CTL + ]; +} diff --git a/arch/src/x86_64/msr_definitions/intel/mod.rs b/arch/src/x86_64/msr_definitions/intel/mod.rs new file mode 100644 index 0000000000..0ee0435a62 --- /dev/null +++ b/arch/src/x86_64/msr_definitions/intel/mod.rs @@ -0,0 +1,21 @@ +// Copyright © 2025 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// + +#[cfg(any(test, feature = "cpu_profile_generation"))] +mod architectural_msrs; + +#[cfg(feature = "cpu_profile_generation")] +mod non_architectural_msrs; + +mod msr_based_features; + +#[cfg(feature = "cpu_profile_generation")] +pub(in crate::x86_64) use architectural_msrs::FORBIDDEN_IA32_MSR_RANGES; +#[cfg(any(test, feature = "cpu_profile_generation"))] +pub(in crate::x86_64) use architectural_msrs::PERMITTED_IA32_MSRS; +pub use msr_based_features::INTEL_MSR_FEATURE_DEFINITIONS; +pub(in crate::x86_64) use msr_based_features::check_feature_msr_compatibility; +#[cfg(feature = "cpu_profile_generation")] +pub(in crate::x86_64) use non_architectural_msrs::NON_ARCHITECTURAL_INTEL_MSRS; diff --git a/arch/src/x86_64/msr_definitions/intel/msr_based_features.rs b/arch/src/x86_64/msr_definitions/intel/msr_based_features.rs new file mode 100644 index 0000000000..edc71fe746 --- /dev/null +++ b/arch/src/x86_64/msr_definitions/intel/msr_based_features.rs @@ -0,0 +1,4421 @@ +// Copyright © 2025 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::collections::HashMap; + +use log::{debug, error, warn}; + +use crate::x86_64::msr_definitions::{ + MsrDefinitions, ProfilePolicy, RegisterAddress, ValueDefinition, ValueDefinitions, +}; + +impl RegisterAddress { + pub const IA32_BIOS_SIGN_ID: Self = Self(0x8b); + pub const IA32_ARCH_CAPABILITIES: Self = Self(0x10a); + pub const IA32_PERF_CAPABILITIES: Self = Self(0x345); + pub const IA32_VMX_BASIC: Self = Self(0x480); + pub const IA32_VMX_PINBASED_CTLS: Self = Self(0x481); + pub const IA32_VMX_PROCBASED_CTLS: Self = Self(0x482); + pub const IA32_VMX_EXIT_CTLS: Self = Self(0x483); + pub const IA32_VMX_ENTRY_CTLS: Self = Self(0x484); + pub const IA32_VMX_MISC: Self = Self(0x485); + pub const IA32_VMX_CR0_FIXED0: Self = Self(0x486); + pub const IA32_VMX_CR0_FIXED1: Self = Self(0x487); + pub const IA32_VMX_CR4_FIXED0: Self = Self(0x488); + pub const IA32_VMX_CR4_FIXED1: Self = Self(0x489); + pub const IA32_VMX_VMCS_ENUM: Self = Self(0x48a); + pub const IA32_VMX_PROCBASED_CTLS2: Self = Self(0x48b); + pub const IA32_VMX_EPT_VPID_CAP: Self = Self(0x48c); + pub const IA32_VMX_TRUE_PINBASED_CTLS: Self = Self(0x48d); + pub const IA32_VMX_TRUE_PROCBASED_CTLS: Self = Self(0x48e); + pub const IA32_VMX_TRUE_EXIT_CTLS: Self = Self(0x48f); + pub const IA32_VMX_TRUE_ENTRY_CTLS: Self = Self(0x490); + pub const IA32_VMX_VMFUNC: Self = Self(0x491); + pub const IA32_VMX_PROCBASED_CTLS3: Self = Self(0x492); + pub const IA32_VMX_EXIT_CTLS2: Self = Self(0x493); + + // =============== Non-architectural MSRs ======== + + // KVM + Intel Skylake reports this as an MSR-based feature + pub const MSR_PLATFORM_INFO: Self = Self(0xce); +} + +/// This table contains descriptions of all the MSRs whose register addresses can be contained in +/// the list returned by `KVM_GET_MSR_FEATURE_INDEX_LIST` when executed on an Intel CPU. +/// +/// The values described here are based on the Intel 64 and IA-32 Architectures Software Developer's +/// Manual Combined Volumes: 1,2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4 from October 2025. +/// +/// We try to use the same short descriptions as Intel, but in the cases where we could not find an +/// official name for the bit field(s) we invented our own based on the description. +/// +/// The descriptions written here are based on those found in the aforementioned manual, but often less +/// detailed. We recommend consulting the official Intel documentation whenever more information +/// is required. +/// +/// +/// ## Future-proofing +/// +/// Future processors and/or KVM versions may of course introduce more MSR-based features than those listed here at this time of writing. +/// In order to make sure that this is taken into account, the CPU profile generation tool will error when this is detected. The person +/// attempting to create a new CPU profile should then update this table accordingly and try again. +pub static INTEL_MSR_FEATURE_DEFINITIONS: MsrDefinitions<24> = const { + MsrDefinitions([ + ( + RegisterAddress::IA32_BIOS_SIGN_ID, + ValueDefinitions::new(&[ + ValueDefinition { + short: "PATCH_SIGN_ID", + description: "Any non-zero value is the microcode update signature patch signature ID", + bits_range: (32, 63), + policy: ProfilePolicy::Passthrough, + } + ]) + ), + + ( + RegisterAddress::IA32_ARCH_CAPABILITIES, + ValueDefinitions::new(&[ + ValueDefinition { + short: "RDCL_NO", + description: "The processor is not susceptible to Rogue Data Cache Load (RDCL)", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "IBRS_ALL", + description: "The processor supports enhanced IBRS", + bits_range: (1, 1), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "RSBA", + description: "The processor supports RSB Alternate", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "SKIP_L1DFL_VMENTRY", + description: "A value of 1 indicates the hypervisor need not flush the L1D on VM entry", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "SSB_NO", + description: "Processor is not susceptible to Speculation Store Bypass", + bits_range: (4, 4), + policy: ProfilePolicy::Passthrough, + }, + ValueDefinition { + short: "MDS_NO", + description: "Processor is not susceptible to Microarchitectural Data Sampling (MDS)", + bits_range: (5, 5), + policy: ProfilePolicy::Passthrough, + }, + ValueDefinition { + short: "IF_PSCHANGE_MC_NO", + description: "The processor is not susceptible to a machine check error due to modifying the size of a code page without TLB invalidation", + bits_range: (6, 6), + policy: ProfilePolicy::Passthrough, + }, + ValueDefinition { + short: "TSX_CTRL", + description: "If 1, indicates presence of IA32_TSX_CTRL MSR", + bits_range: (7, 7), + // TSX is riddled with CVEs + // TODO: Check that this is indeed the right policy + policy: ProfilePolicy::Static(0), + }, + ValueDefinition { + short: "TAA_NO", + description: "If 1, processor is not affected by TAA", + bits_range: (8, 8), + policy: ProfilePolicy::Passthrough, + }, + ValueDefinition { + short: "MCU_CONTROL", + description: "If 1, the processor supports the IA32_MCU_CONTROL MSR", + bits_range: (9, 9), + // TODO: Check what the IA32_MCU_CONTROL MSR is + policy: ProfilePolicy::Static(0), + }, + ValueDefinition { + short: "MISC_PACKAGE_CTLS", + description: "The processor supports IA32_MISC_PACKAGE_CTLS MSR", + bits_range: (10, 10), + policy: ProfilePolicy::Static(0), + }, + ValueDefinition { + short: "ENERGY_FILTERING_CTL", + description: "The processor supports setting and reading the IA32_MISC_PACKAGE_CTLS[0] (ENERGY_FILTERING_ENABLE) bit", + bits_range: (11, 11), + policy: ProfilePolicy::Static(0), + }, + ValueDefinition { + short: "DOITM:", + description: "If 1, the processor supports Data Operand Independent Timing Mode", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "SBDR_SSDP_NO", + description: "The processor is not affected by either the Shared Buffers Data Read (SBDR) vulnerability or the Sideband Stale Data Propagator (SSDP)", + bits_range: (13, 13), + policy: ProfilePolicy::Passthrough, + }, + ValueDefinition { + short: "FBSDP_NO", + description: "The processor is not affected by the Fill Buffer Stale Data Propagator (DBSDP)", + bits_range: (14, 14), + policy: ProfilePolicy::Passthrough, + }, + ValueDefinition { + short: "PSDP_NO", + description: "The processor is not affected by vulnerabilities involving the Primary Stale Data Propagator (PSDP)", + bits_range: (15, 15), + policy: ProfilePolicy::Passthrough, + }, + ValueDefinition { + short: "MCU_ENUMERATION", + description: "If 1, the processor supportss the IA32_MCU_ENUMERATION and IA32_MCU_STATUS MSRs", + bits_range: (16, 16), + policy: ProfilePolicy::Static(0), + }, + ValueDefinition { + short: "FB_CLEAR", + description: "If 1, the processor supports overwrite of fill buffer values as part of MD_CLEAR operations with the VERW instruction. + On these processors L1D_FLUSH does not overwrite fill buffer values", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit, + }, + + ValueDefinition { + short: "FB_CLEAR_CTRL", + description: "If 1, the processor supports the IA32_MCU_OPT_CTRL MSR and allows software to set bit 3 of that MSR (FB_CLEAR_DIS)", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit, + }, + + ValueDefinition { + short: "RRSBA", + description: "A value of 1 indicates the processor may have the RRSBA alternate prediction behavior, if not disabled by RRSBA_DIS_U or RRSBA_DIS_S", + bits_range: (19, 19), + policy: ProfilePolicy::Inherit, + }, + + ValueDefinition { + short: "BHI_NO", + description: "A value of 1 indicates BHI_NO branch prediction behavior, regardless of the value of IA32_SPEC_CTRL[BHI_DIS_S] MSR bit", + bits_range: (20, 20), + policy: ProfilePolicy::Passthrough, + }, + + ValueDefinition { + short: "XAPIC_DISABLE_STATUS", + description: "Enumerates that the IA32_XAPIC_DISABLE_STATUS MSR exists, and that bit 0 specifies whether the legacy xAPIC is disabled and APIC state is locked to x2APIC", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit, + }, + + ValueDefinition { + short: "MCU_EXTENDED_SERVICE", + description: "If 1, the processor supports MCU extended servicing - IA32_MCU_EXT_SERVICE MSR", + bits_range: (22, 22), + // TODO: Check + policy: ProfilePolicy::Static(0), + }, + + ValueDefinition { + short: "OVERCLOCKING_STATUS", + description: "If set, the IA32_OVERCLOCKING_STATUS MSR exists", + bits_range: (23, 23), + // TODO: Check + policy: ProfilePolicy::Inherit, + }, + + ValueDefinition { + short: "PBRSB_NO", + description: "If 1, the processor is not affected by issues related to Post-Barrier Return Stack Buffer Predictions", + bits_range: (24, 24), + policy: ProfilePolicy::Passthrough, + }, + ValueDefinition { + short: "GDS_CTRL", + description: "If 1, the processor supports the GDS_MITG_DIS and GDS_MITG_LOCK bits of the IA32_MCU_OPT_CTRL MSR", + bits_range: (25, 25), + // TODO: Check + policy: ProfilePolicy::Inherit, + }, + + ValueDefinition { + short: "GDS_NO", + description: "If 1, the processor is not affected by Gather Data Sampling", + bits_range: (26, 26), + policy: ProfilePolicy::Passthrough, + }, + + ValueDefinition { + short: "RFDS_NO", + description: "If 1, processor is not affected by Register File Data Sampling", + bits_range: (27, 27), + policy: ProfilePolicy::Passthrough, + }, + + ValueDefinition { + short: "RFDS_CLEAR", + description: "If 1, when VERW is executed the processor will clear stale data from register files affected by Register File Data Sampling", + bits_range: (28, 28), + policy: ProfilePolicy::Passthrough, + }, + + ValueDefinition { + short: "IGN_UMONITOR_SUPPORT", + description: "If 0, IA32_MCU_OPT_CTRL bit 6 (IGN_UMONITOR) is not supported. If 1, it indicates support of IA32_MCU_OPT_CTRL bit 6 (IGN_UMONITOR)", + bits_range: (29, 29), + // TODO: Check + policy: ProfilePolicy::Inherit, + }, + + ValueDefinition { + short: "MON_UMON_MITG_SUPPORT", + description: "If 1, indicates support for IA32_MCU_OPT_CTRL bit 7 (MON_UMON_MITG), otherwise it is not supported", + bits_range: (30, 30), + policy: ProfilePolicy::Inherit, + }, + + + ValueDefinition { + short: "PBOPT_SUPPORT", + description: "If 1, IA32_PBOPT_CTRL bit 0 (Prediction Barrier Option (PBOPT)) is supported, otherwise it is not", + bits_range: (32, 32), + policy: ProfilePolicy::Inherit, + }, + + ValueDefinition { + short: "ITS_NO", + description: "If 0, the hypervisor indicates that the system is not affected by indirect Target Selection. If 1, then the hypervisor + indicates that the system may be affected by indirect Target Selection", + bits_range: (62, 62), + policy: ProfilePolicy::Passthrough, + + }, + + ]), + ), + + ( + RegisterAddress::IA32_PERF_CAPABILITIES, + ValueDefinitions::new(&[ + ValueDefinition { + short: "IA32_PERF_CAPABILITIES", + description: "Read Only MSR that enumerates the existence of performance monitoring features", + bits_range: (0, 63), + // This MSR is only valid if CPUID 0x1.ECX[15] is set, but that bit is always zeroed out for CPU profiles different from host + policy: ProfilePolicy::Deny + } + ]) + ), + + ( + RegisterAddress::IA32_VMX_BASIC, + ValueDefinitions::new(&[ + ValueDefinition { + short: "VMCS_REV_ID", + description: "31-bit VMCS revision identifier. Processors that use the same VMCS revision identifier + use the same size for VMCS regions", + bits_range: (0,31), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "REGION_SIZE", + description: "Number of bytes that software should allocate for the VMXON region and any VMCS region. It is a value greater than + 0 and at most 4096", + bits_range: (32, 44), + policy: ProfilePolicy::Inherit, + }, + + ValueDefinition { + short: "DUAL_MON", + description: " If 1, the logical processor supports the dual-monitor treatment of system-management + interrupts and system-management mode. See Section 33.15 for details of this treatment", + bits_range: (49, 49), + // TODO: Should we have Static(0)? here (I think that might be equivalent to what QEMU does) + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "MEM_TYPE", + description: "The memory type that should be used for the VMCS, for data structures referenced by pointers + in the VMCS (I/O bitmaps, virtual-APIC page, MSR areas for VMX transitions), and for the MSEG header", + bits_range: (50, 53), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "VM_EXIT_INFO_INS_OUTS", + description: " If 1, the processor reports information in the VM-exit instruction-information field on VM exits + due to execution of the INS and OUTS instructions. + ", + bits_range: (54, 54), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "VMX_CTRLS_DEFAULT_MUT", + description: "Any VMX controls that default to 1 may be cleared to 0", + bits_range: (55,55), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "VM_ENTRY_HARDWARE_EXCEPTIONS", + description: "If 1, then software can use VM entry to deliver a hardware exception", + bits_range: (56, 56), + policy: ProfilePolicy::Inherit + } + ]) + ), + + ( + RegisterAddress::IA32_VMX_PINBASED_CTLS, + ValueDefinitions::new(&[ + ValueDefinition { + short:"ALLOWED_ZERO_EXTERNAL_INTERRUPT_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_1_2", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (1, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_NMI_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_4", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (4, 4), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_VIRTUAL_NMIS", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (5, 5), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACTIVATE_VMX_PREEMPTION_TIMER", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (6, 6), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_PROCESS_POSTED_INTERRUPTS", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (7, 7), + policy: ProfilePolicy::Inherit + }, + + + ValueDefinition { + short: "ALLOWED_ZERO", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (8, 31), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short:"ALLOWED_ONE_EXTERNAL_INTERRUPT_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (32, 32), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_1_2", + description: "VM entry allows control X to be 1 if bit X in this MSR is 1", + bits_range: (33, 34), + policy: ProfilePolicy::Inherit + }, + ValueDefinition{ + short:"ALLOWED_ONE_NMI_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (35, 35), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_4", + description: "VM entry allows control X to be 1 if bit X in this MSR is 1", + bits_range: (36, 36), + policy: ProfilePolicy::Inherit + }, + ValueDefinition{ + short:"ALLOWED_ONE_VIRTUAL_NMIS", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (37, 37), + policy: ProfilePolicy::Inherit + }, + ValueDefinition{ + short:"ALLOWED_ONE_ACTIVATE_VMX__PREEMPTION_TIMER", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (38, 38), + policy: ProfilePolicy::Inherit + }, + ValueDefinition{ + short:"ALLOWED_ONE_PROCESS_POSTED_INTERRUPTS", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (39, 39), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (40, 63), + policy: ProfilePolicy::Inherit + } + ]) + ), + + ( + RegisterAddress::IA32_VMX_PROCBASED_CTLS, + ValueDefinitions::new(&[ + ValueDefinition { + short: "ALLOWED_ZERO_0_1", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (0, 1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_INTERRUPT_WINDOW_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_USE_TSC_OFFSETTING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_4_6", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (4, 6), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_HLT_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (7, 7), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_8", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (8, 8), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_INVLPG_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (9, 9), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_MWAIT_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (10, 10), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_RDPMC_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (11, 11), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_RDTSC_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_13_14", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (13, 14), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CR3_LOAD_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (15, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CR3_STORE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACTIVATE_TERTIARY_CONTROLS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_18", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CR8_LOAD_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (19, 19), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CR8_STORE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (20, 20), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_USE_TPR_SHADOW", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_NMI_WINDOW_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_MOV_DR_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (23, 23), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_UNCONDITIONAL_I/O_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (24, 24), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_USE_I/O_BITMAPS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (25, 25), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_26", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (26, 26), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_MONITOR_TRAP_FLAG", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (27, 27), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_USE_MSR_BITMAPS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (28, 28), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_MONITOR_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (29, 29), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_PAUSE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (30, 30), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACTIVATE_SECONDARY_CONTROLS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (31, 31), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_0_1", + description: "Control X is allowed to be 1 if bit 32 + X of this MSR is 1", + bits_range: (32, 33), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_INTERRUPT_WINDOW_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (34, 34), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_USE_TSC_OFFSETTING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (35, 35), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_4_6", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (36, 38), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_HLT_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (39, 39), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_8", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (40, 40), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_INVLPG_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (41, 41), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_MWAIT_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (42, 42), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_RDPMC_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (43, 43), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_RDTSC_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (44, 44), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "ALLOWED_ONE_13_14", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (45, 46), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CR3_LOAD_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (47, 47), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CR3_STORE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (48, 48), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ACTIVATE_TERTIARY_CONTROLS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (49, 49), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_18", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (50, 50), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short:"ALLOWED_ONE_CR8_LOAD_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (51, 51), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CR8_STORE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (52, 52), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_USE_TPR_SHADOW", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (53, 53), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_NMI_WINDOW_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (54, 54), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_MOV_DR_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (55, 55), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_UNCONDITIONAL_I/O_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (56, 56), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_USE_I/O_BITMAPS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (57, 57), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_26", + description: "Control X is allowed to be 1 if bit X of this MSR is 1", + bits_range: (58, 58), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short:"ALLOWED_ONE_MONITOR_TRAP_FLAG", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (59, 59), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_USE_MSR_BITMAPS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (60, 60), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_MONITOR_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (61, 61), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_PAUSE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (62, 62), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ACTIVATE_SECONDARY_CONTROLS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (63, 63), + policy: ProfilePolicy::Inherit + }, + + ]) + ), + + ( + RegisterAddress::IA32_VMX_EXIT_CTLS, + ValueDefinitions::new(&[ + ValueDefinition { + short: "ALLOWED_ZERO_0_1", + description: "Control X is allowed to be 0 if bit X in this MSR is 0", + bits_range: (0, 1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_DEBUG_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_3_8", + description: "Control X is allowed to be 0 if bit X in this MSR is 0", + bits_range: (3, 8), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_HOST_ADDRESS_SPACE_SIZE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (9, 9), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_10_11", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (10, 11), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_PERF_GLOBAL_CTRL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_13_14", + description: "Control X is allowed to be 0 if bit X in this MSR is 0", + bits_range: (13, 14), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACKNOWLEDGE_INTERRUPT_O_EXIT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (15, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_16_17", + description: "Control X is allowed to be 0 if bit X in this MSR is 0", + bits_range: (16, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (19, 19), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (20, 20), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_VMX_PREEMPTION_TIMER_VALUE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CLEAR_IA32_BNDCFGS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (23, 23), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (24, 24), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CLEAR_IA32_RTIT_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (25, 25), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CLEAR_IA32_LBR_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (26, 26), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CLEAR_UINV", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (27, 27), + policy: ProfilePolicy::Inherit + }, + // TODO: Also determines whether SSP is loaded on VM exit (do we need that?) + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_CET_STATE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (28, 28), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_PKRS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (29, 29), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_IA32_PERF_GLOBAL_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (30, 30), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACTIVATE_SECONDARY_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (31, 31), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_0_1", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (32, 33), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short:"ALLOWED_ONE_SAVE_DEBUG_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (34, 34), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "ALLOWED_ONE_3_8", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (35, 40), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_HOST_ADDRESS_SPACE_SIZE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (41, 41), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_10_11", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (42, 43), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_PERF_GLOBAL_CTRL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (44, 44), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "ALLOWED_ONE_13_14", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (45, 46), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ACKNOWLEDGE_INTERRUPT_O_EXIT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (47, 47), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_16_17", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (48, 49), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_SAVE_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (50, 50), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (51, 51), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_SAVE_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (52, 52), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (53, 53), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_SAVE_VMX_PREEMPTION_TIMER_VALUE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (54, 54), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CLEAR_IA32_BNDCFGS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (55, 55), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (56, 56), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CLEAR_IA32_RTIT_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (57, 57), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CLEAR_IA32_LBR_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (58, 58), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CLEAR_UINV", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (59, 59), + policy: ProfilePolicy::Inherit + }, + // TODO: Also determines whether SSP is loaded on VM exit (do we need that?) + ValueDefinition { + short:"ALLOWED_ONE_LOAD_CET_STATE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (60, 60), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_PKRS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (61, 61), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_SAVE_IA32_PERF_GLOBAL_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (62, 62), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_ACTIVATE_SECONDARY_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (63, 63), + policy: ProfilePolicy::Inherit + }, + ]) + ), + ( + RegisterAddress::IA32_VMX_ENTRY_CTLS, + ValueDefinitions::new(&[ + ValueDefinition { + short: "ALLOWED_ZERO_0_1", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (0, 1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_DEBUG_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_3_8", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (3, 8), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_IA_32E_MODE_GUES", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (9, 9), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENTRY_TO_SMM", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (10, 10), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_DEACTIVATE_DUAL__MONITOR_TREATMENT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (11, 11), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_12", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_PERF_GLOBAL_CTRL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (13, 13), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (14, 14), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (15, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_BNDCFGS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_RTIT_CTL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_UINV", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (19, 19), + policy: ProfilePolicy::Inherit + }, + // TODO: Also determines whether SSP is loaded on VM exit (do we need that?) + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_CET_STATE", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (20, 20), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_GUEST_IA32_LBR_CTL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_PKRS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_23_24", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (23, 24), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ALLOW_SEAM_GUEST_TELEMETRY", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (25, 25), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_26_31", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (26, 31), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_0_1", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (32, 33), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_DEBUG_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (34, 34), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_3_8", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (35, 40), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_IA_32E_MODE_GUES", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (41, 41), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENTRY_TO_SMM", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (42, 42), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_DEACTIVATE_DUAL__MONITOR_TREATMENT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (43, 43), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_12", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (44, 44), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_PERF_GLOBAL_CTRL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (45, 45), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (46, 46), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (47, 47), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_BNDCFGS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (48, 48), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (49, 49), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_RTIT_CTL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (50, 50), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_UINV", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (51, 51), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_CET_STATE", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (52, 52), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_GUEST_IA32_LBR_CTL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (53, 53), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_PKRS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (54, 54), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_23_24", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (55, 56), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ALLOW_SEAM_GUEST_TELEMETRY", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (57, 57), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_26_31", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (58, 63), + policy: ProfilePolicy::Inherit + }, + ]) + ), + + ( + RegisterAddress::IA32_VMX_MISC, + ValueDefinitions::new(&[ + ValueDefinition { + short: "VMX_PREEMPTION_TSC_REL", + description: "specifies the relationship between the rate of the VMX-preemption timer and that of the timestamp counter (TSC)", + bits_range: (0, 4), + policy: ProfilePolicy::Passthrough + }, + ValueDefinition { + short: "IA32_EFER.LMA_STORE", + description: "If 1, then VM exits store the value of IA32_EFER.LMA into the IA32-e mode guest VM-entry control", + bits_range: (5,5), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "HLT_STATE", + description: "Activity state 1 (HLT) is supported", + bits_range: (6,6), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "SHUTDOWN_STATE", + description: "Activity state 2 (shutdown) is supported", + bits_range: (7,7), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "WAIT_FOR_SIPI__STATE", + description: "Activity state 3 (wait-for-SIPI) is supported", + bits_range: (8,8), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "VMX_INTEL_PT", + description: "If 1 then Intel Processor Trace can be used in VMX operation", + bits_range: (14,14), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "RDMSR_SMM", + description: "If 1 then the RDMSR instruction can be used in system management mode (SMM) to read the IA32_SMBASE MSR", + bits_range: (15,15), + // TODO: Is this a reasonable policy? + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "VMX_NUM_CR3", + description: "The number of CR3-target values supported by the processor", + bits_range: (16,24), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "MAX_MSR_STORE_LISTS", + description: "If N then 512*(N +1) is the recommended maximum number of MSRs to be included each of the VM-exit MSR-store list, VM-exit-MSR-load-list, VM-entry MSR-load list", + bits_range: (25, 27), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "SMM_MONITOR_CTL_BIT2", + description: "If set then bit 2 of the IA32_SMM_MONITOR_CTL can be set to 1", + // TODO: Check policy. Perhaps this should rather be Static(0) ? + bits_range: (28, 28), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "VM_WRITE_EXIT_FIELDS", + description: "If 1 then software can use VMWRITE to write to any supported field in the VMCS", + bits_range: (29,29), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "VM_ENTRY_INJECTION", + description: "If 1 then VM entry permits injection of the following: software interrupt, software exception, or privileged software exception with an instruction length of 0", + bits_range: (30,30), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "MSEG_REV_ID", + description: "MSEG revision identifier used by the processor", + bits_range: (32,63), + // TODO: Should this be Passthrough? + policy: ProfilePolicy::Inherit + }, + ]) + ), + + ( + RegisterAddress::IA32_VMX_CR0_FIXED0, + // NOTE 1: If any entry in IA32_VMX_CR0_FIXED1 has ProfilePolicy::Stattic(0) then the corresponding entry here must also have ProfilePolicy::Static(0) + // + // NOTE 2: We use the inherit policy for reserved fields. + ValueDefinitions::new(&[ + ValueDefinition { + short: "CR0.PE", + description: "If 0, then bit 0 (Protection Enable) of CR0 is allowed to be 0. bit 0 of CR0 enables real-address mode when clear.", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.MP", + description: "If 0, then bit 1 (Monitor Coprocessor) of CR0 is allowed to be 0. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (1, 1), + policy: ProfilePolicy::Inherit + }, + // We expect this to be 0 for all modern processors, but Inherit is fine. + ValueDefinition { + short: "CR0.EM", + description: "If 0, then bit 2 (Emulation) of CR0 is allowed to be 0. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.TS", + description: "If 0, then bit 3 (Task Switched) of CR0 is allowed to be 0. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.ET", + description: "If 0, then bit 4 (Extension Type) of CR0 is allowed to be 0. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (4, 4), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.NE", + description: "If 0, then bit 5 (Numeric Error) of CR0 is allowed to be 0. Enables the PC-style x87 FPU error reporting mechanism when clear in CR0.", + bits_range: (5, 5), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "IA32_VMX_CR0_FIXED1_RESERVED_6_15", + description: "Reports bits allowed to be 0 in CR0", + bits_range: (6, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.WP", + description: "If 0, then bit 16 (Write protect) of CR0 is allowed to be 0. If this bit is clear in CR0 then supervisor-level procedures are + allowed to write into read-only pages", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "IA32_VMX_CR0_FIXED1_RESERVED_17_17", + description: "Reports bits allowed to be 0 in CR0", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.AM", + description: "If 0, then bit 18 (Alignment Mask) of CR0 is allowed to be 0. If this bit is clear in CR0 then alignment checking is disabled.", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "IA32_VMX_CR0_FIXED1_RESERVED_19_28", + description: "Reports bits allowed to be 0 in CR0", + bits_range: (19, 28), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.NW", + description: "If 0, then bit 29 (Not Write-through) of CR0 is allowed to be 0. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (29, 29), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.CD", + description: "If 0, then bit 30 (Cache disable) of CR0 is allowed to be 0. If CR0 bits 30 and 29 are 0 then caching of memory locations + for the whole of physical memory in the processor's internal (and external) cache is enabled.", + bits_range: (30, 30), + policy: ProfilePolicy::Inherit + }, + // TOD0: Disabling paging sounds bad, should we force this to 1? + ValueDefinition { + short: "CR0.PG", + description: "If 0, then bit 31 (Paging) of CR0 is allowed to be 0. If bit 31 of CR0 is cleared then paging is disabled (all linear addresses get treated as physical addresses).", + bits_range: (31, 31), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "IA32_VMX_CR0_FIXED1_RESERVED_32_63", + description: "Reports bits allowed to be 0 in CR0", + bits_range: (32, 63), + policy: ProfilePolicy::Inherit + }, + ]) + ), + + // NOTE: CR0_FIXED1 cannot be set by KVM, but this is OK, because its value is determined by CPUID anyway + ( + RegisterAddress::IA32_VMX_CR0_FIXED1, + ValueDefinitions::new(&[ + + ValueDefinition { + short: "CR0.PE", + description: "If 1, then bit 0 (Protection Enable) of CR0 is allowed to be 1. bit 0 of CR0 enables protected mode when set", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "CR0.MP", + description: "If 1, then bit 1 (Monitor Coprocessor) of CR0 is allowed to be 1. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (1, 1), + policy: ProfilePolicy::Inherit + }, + // We expect this to be 0 for all modern processors, but Inherit is fine. + ValueDefinition { + short: "CR0.EM", + description: "If 1, then bit 2 (Emulation) of CR0 is allowed to be 1. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.TS", + description: "If 1, then bit 3 (Task Switched) of CR0 is allowed to be 1. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.ET", + description: "If 1, then bit 4 (Extension Type) of CR0 is allowed to be 1. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (4, 4), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.NE", + description: "If 1, then bit 5 (Numeric Error) of CR0 is allowed to be 1. This bit enables the native (internal) mechanism for reporting x87 FPU errors when set in CR0.", + bits_range: (5, 5), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "IA32_VMX_CR0_FIXED1_RESERVED_6_15", + description: "Reports bits allowed to be 1 in CR0", + bits_range: (6, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.WP", + description: "If 1, then bit 16 (Write protect) of CR0 is allowed to be 1. If this bit is set in CR0 then supervisor-level procedures are + inhibited from writing into read-only pages", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "IA32_VMX_CR0_FIXED1_RESERVED_17_17", + description: "Reports bits allowed to be 1 in CR0", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.AM", + description: "If 1, then bit 18 (Alignment Mask) of CR0 is allowed to be 1. If bit 18 of CR0 is set then automatic alignment checking is possible.", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "IA32_VMX_CR0_FIXED1_RESERVED_19_28", + description: "Reports bits allowed to be 1 in CR0", + bits_range: (19, 28), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.NW", + description: "If 1, then bit 29 (Not Write-through) of CR0 is allowed to be 1. See Intel SDM Vol. 3A Section 2.5 for more information", + bits_range: (29, 29), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.CD", + description: "If 1, then bit 30 (Cache disable) of CR0 is allowed to be 1. If CR0 bit 30 is 1 then caching is restricted", + bits_range: (30, 30), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR0.PG", + description: "If 1, then bit 31 (Paging) of CR0 is allowed to be 1 which enables paging", + bits_range: (31, 31), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "IA32_VMX_CR0_FIXED1_RESERVED_32_63", + description: "Reports bits allowed to be 1 in CR0", + bits_range: (32, 63), + policy: ProfilePolicy::Inherit + }, + ]) + ), + + ( + RegisterAddress::IA32_VMX_CR4_FIXED0, + ValueDefinitions::new(&[ + ValueDefinition { + short: "CR4.VME", + description: "If 0, then bit 0 (Virtual-8086 Mode Extension) of CR4 is allowed to be 0. Bit 0 of CR4 disables the interrupt and exception-handling extensions in virtual-8086 mode when clear.", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PVI", + description: "If 0, then bit 1 (Protected-Mode Virtual Interrupts) of CR4 is allowed to be 0. Bit 1 of CR4 disables the virtual interrupt flag in protected mode when clear.", + bits_range: (1, 1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.TSD", + description: "If 0, then bit 2 (Time Stamp Disable) of CR4 is allowed to be 0. Bit 2 of CR4 allows RDTSC instruction to be executed at any privilege level when clear.", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.DE", + description: "If 0, then bit 3 (Debugging extensions) of CR4 is allowed to be 0. When Bit 3 of CR4 is clear the processor aliases references to registers DR4 and DR5 for compatibility with legacy software", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PSE", + description: "If 0, then bit 4 (Page Size Extensions) of CR4 is allowed to be 0. Bit 4 of CR4 restricts 32-bit paging to pages of 4 KBytes when clear.", + bits_range: (4, 4), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PAE", + description: "If 0, then bit 5 (Physical Address Extension) of CR4 is allowed to be 0. Bit 5 of CR4 restricts physical addresses to 32 bits when clear", + bits_range: (5, 5), + policy: ProfilePolicy::Inherit + }, + // TODO: Perhaps we should force this to 0? + ValueDefinition { + short: "CR4.MCE", + description: "If 0, then bit 6 (Machine-Check Enable) of CR4 is allowed to be 0. Bit 6 of CR4 disables the machine-check exception when clear", + bits_range: (6, 6), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PGE", + description: "If 0, then bit 7 (Page Global Enable) of CR4 is allowed to be 0. Bit 7 of CR4 disables the global page feature when clear", + bits_range: (7, 7), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PCE", + description: "If 0, then bit 8 (Performance-Monitoring Counter Enable) of CR4 is allowed to be 0. The RDPMC instruction can only be executed at protection level 0 when bit 8 of CR4 is clear", + bits_range: (8, 8), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.OSFXSR", + description: "If 0, then bit 9 (OS Support for FXSAVE and FXRSTOR) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (9, 9), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.OSXMMEXCPT", + description: "If 0, then bit 10 (OS Support for Unmaksed SIMD Floating-Point Exceptions) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (10, 10), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.UMIP", + description: "If 0, then bit 11 (User-Mode instruction Prevention) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (11, 11), + policy: ProfilePolicy::Inherit + }, + // Maybe this could even be passthrogh? CHV is 64-bit only. + ValueDefinition { + short: "CR4.LA57", + description: "If 0, then bit 12 (57-bit linear addresses) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.VMXE", + description: "If 0, then bit 13 (VMX-Enable) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (13, 13), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.SMXE", + description: "If 0, then bit 14 (SMX-Enable) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (14, 14), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.RESERVED_15", + description: "If 0, then bit 15 (RESERVED) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (15, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.FSGSBASE", + description: "If 0, then bit 16 (FSGSBASE-Enable) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + // Probably irrelevant? + ValueDefinition { + short: "CR4.PCIDE", + description: "If 0, then bit 17 (PCID-Enable) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.OSXSAVE", + description: "If 0, then bit 18 (XSAVE and Processor Extended States-Enable) of CR4 is allowed to be 0. See Intel SDM Vol.3A Section 2.5 for more information", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + // CPU Profiles do not support Key locker features for now + ValueDefinition { + short: "CR4.KL", + description: "If 0, then bit 19 (Key-Locker-Enable) of CR4 is allowed to be 0. When bit 19 of CR4 is set, the LOADIWKEY instruction is enabled and CPUID.0x19.EBX[0] is set if support for AES key locker instructions has been activated by system firmware", + bits_range: (19, 19), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.SMEP", + description: "If 0, then bit 20 (SMEP-Enable) of CR4 is allowed to be 0. See Intel SDM Vol 3.A Section 2.5 for more information", + bits_range: (20, 20), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.SMAP", + description: "If 0, then bit 21 (SMAP-Enable) of CR4 is allowed to be 0. See Intel SDM Vol 3.A Section 2.5 for more information", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PKE", + description: "If 0, then bit 22 (Enable protection keys for user-mode pages) of CR4 is allowed to be 0. See Intel SDM Vol. 3.A Section 2.5 for more information.", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit, + }, + ValueDefinition { + short: "CR4.CET", + description: "If 0, then bit 23 (Control-flow Enforcement Technology) of CR4 is allowed to be 0. See Intel SDM Vol. 3.A Section 2.5 for more information.", + bits_range: (23, 23), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.PKS", + description: "If 0, then bit 24 (Enable protection keys for supervisor-mode pages) of CR4 is allowed to be 0. See Intel SDM Vol. 3.A Section 2.5 for more information.", + bits_range: (24, 24), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.UINTR", + description: "If 0, then bit 25 (User Interrupts Enable) of CR4 is allowed to be 0. See Intel SDM Vol. 3.A Section 2.5 for more information.", + bits_range: (25, 25), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.RESERVED_26", + description: "If 0, then bit 26 (RESERVED) of CR4 is allowed to be 0. See Intel SDM Vol.3.A Section 2.5 for more information.", + bits_range: (26, 26), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.LASS", + description: "If 0, then bit 27 (User Interrupts Enable) of CR4 is allowed to be 0. See Intel SDM Vol. 3.A Section 2.5 for more information.", + bits_range: (27, 27), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.LAM_SUP", + description: "If 0, then bit 28 (Supervisor LAM-enable) of CR4 is allowed to be 0. See Intel SDM Vol. 3.A Section 25 for more information.", + bits_range: (28, 28), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "IA32_VMX_CR4_FIXED0", + description: "Reports bits allowed to be 0 in CR4", + bits_range: (29, 63), + policy: ProfilePolicy::Inherit + } + ]) + ), + + // NOTE: CR4_FIXED1 cannot be set by KVM, but this is OK, because its value is determined by CPUID anyway + ( + RegisterAddress::IA32_VMX_CR4_FIXED1, + ValueDefinitions::new(&[ + ValueDefinition { + short: "CR4.VME", + description: "If 1, then bit 1 (Virtual-8086 Mode Extension) of CR4 is allowed to be 1. Bit 0 of CR4 enables the interrupt and exception-handling extensions in virtual-8086 mode when set.", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PVI", + description: "If 1, then bit 1 (Protected-Mode Virtual Interrupts) of CR4 is allowed to be 1. Bit 1 of CR4 enables hardware support for a virtual interrupt flag in protected mode when set.", + bits_range: (1, 1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.TSD", + description: "If 1, then bit 2 (Time Stamp Disable) of CR4 is allowed to be 1. Bit 2 of CR4 restricts the execution of the RDTS instruction to procedures running at privilege level 0 when set.", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.DE", + description: "If 1, then bit 3 (Debugging extensions) of CR4 is allowed to be 1. Bit 3 of CR4 make references to debug registers DR4 and DR5 cause an undefined opcode exception when set", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PSE", + description: "If 1, then bit 4 (Page Size Extensions) of CR4 is allowed to be 1. Bit 4 of CR4 enables 4-MByte pages with 32-bit paging when set", + bits_range: (4, 4), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PAE", + description: "If 1, then bit 5 (Physical Address Extension) of CR4 is allowed to be 1. Bit 5 of CR4 enables paging to produce physical addresses of more than 32 bits when set", + bits_range: (5, 5), + policy: ProfilePolicy::Inherit + }, + // TODO: Perhaps we should force this to 0? + ValueDefinition { + short: "CR4.MCE", + description: "If 1, then bit 6 (Machine-Check Enable) of CR4 is allowed to be 1. Bit 6 of CR4 enables the machine-check exception when set", + bits_range: (6, 6), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PGE", + description: "If 1, then bit 7 (Page Global Enable) of CR4 is allowed to be 1. Bit 7 of CR4 enables the global page feature when set", + bits_range: (7, 7), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PCE", + description: "If 1, then bit 8 (Performance-Monitoring Counter Enable) of CR4 is allowed to be 1. The RDPMC instruction can be executed at any protection level when bit 8 of CR4 is set.", + bits_range: (8, 8), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.OSFXSR", + description: "If 1, then bit 9 (OS Support for FXSAVE and FXRSTOR) of CR4 is allowed to be 1. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (9, 9), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.OSXMMEXCPT", + description: "If 1, then bit 10 (OS Support for Unmaksed SIMD Floating-Point Exceptions) of CR4 is allowed to be 1. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (10, 10), + policy: ProfilePolicy::Inherit + }, + // TODO: Is this always 0 for QEMU? + ValueDefinition { + short: "CR4.UMIP", + description: "If 1, then bit 11 (User-Mode instruction Prevention) of CR4 is allowed to be 1. If bit 11 of CR4 is set and CPL > 0 then the SGDT,SIDT,SLDT,SMSW and STR instructions cannot be executed.", + bits_range: (11, 11), + policy: ProfilePolicy::Inherit + }, + // Maybe this could even be passthrogh? CHV is 64-bit only. + ValueDefinition { + short: "CR4.LA57", + description: "If 1, then bit 12 (57-bit linear addresses) of CR4 is allowed to be 1. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.VMXE", + description: "If 1, then bit 13 (VMX-Enable) of CR4 is allowed to be 1. Bit 13 of CR4 enables VMX operation when set.", + bits_range: (13, 13), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.SMXE", + description: "If 1, then bit 14 (SMX-Enable) of CR4 is allowed to be 1. Bit 14 of CR4 enables SMX operation when set.", + bits_range: (14, 14), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.RESERVED_15", + description: "If 1, then bit 15 (RESERVED) of CR4 is allowed to be 1. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (15, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.FSGSBASE", + description: "If 1, then bit 16 (FSGSBASE-Enable) of CR4 is allowed to be 1. See Intel SDM Vol.3A Section 2.5 for more information", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + // Probably irrelevant? + ValueDefinition { + short: "CR4.PCIDE", + description: "If 1, then bit 17 (PCID-Enable) of CR4 is allowed to be 1. Enables process-context identifiers (PCIDs) when bit 17 of CR4 is set. Applies only in IA-32e mode", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.OSXSAVE", + description: "If 1, then bit 18 (XSAVE and Processor Extended States-Enable) of CR4 is allowed to be 1. See Intel SDM Vol.3A Section 2.5 for more information", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + // CPU Profiles do not support Key locker features for now + ValueDefinition { + short: "CR4.KL", + description: "If 1, then bit 19 (Key-Locker-Enable) of CR4 is allowed to be 1. When bit 19 of CR4 is set, the LOADIWKEY instruction is enabled and CPUID.0x19.EBX[0] is set if support for AES key locker instructions has been activated by system firmware", + bits_range: (19, 19), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.SMEP", + description: "If 1, then bit 20 (SMEP-Enable) of CR4 is allowed to be 1. Bit 20 of CR4 enables supervisor-mode execution prevention when set", + bits_range: (20, 20), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.SMAP", + description: "If 1, then bit 21 (SMAP-Enable) of CR4 is allowed to be 1. Bit 21 of CR4 enables supervisor-mode access prevention when set", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.PKE", + description: "If 1, then bit 22 (Enable protection keys for user-mode pages) of CR4 is allowed to be 1. When bit 22 of CR4 is set, CPUID.0x7.ECX[4] is displayed as 1. See Intel SDM Vol. 3.A Section 2.5 for more information.", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.CET", + description: "If 1, then bit 23 (Control-flow Enforcement Technology) of CR4 is allowed to be 1. See Intel SDM Vol. 3.A Section 2.5 for more information.", + bits_range: (23, 23), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.PKS", + description: "If 1, then bit 24 (Enable protection keys for supervisor-mode pages) of CR4 is allowed to be 1. See Intel SDM Vol. 3.A Section 2.5 for more information.", + bits_range: (24, 24), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.UINTR", + description: "If 1, then bit 25 (User Interrupts Enable) of CR4 is allowed to be 1. Bit 25 of CR4 enables user interrupts when set.", + bits_range: (25, 25), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "CR4.RESERVED_26", + description: "If 1, then bit 26 (RESERVED) of CR4 is allowed to be 1. See Intel SDM Vol.3A Section 2.5 for more information.", + bits_range: (26, 26), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.LASS", + description: "If 1, then bit 27 (User Interrupts Enable) of CR4 is allowed to be 1. Bit 27 of CR4 enables LASS (Linear-Address-Space Separation) when set.", + bits_range: (27, 27), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.LAM_SUP", + description: "If 1, then bit 28 (Supervisor LAM-enable) of CR4 is allowed to be 1. Bit 28 of CR4 enables LAM (linear-address masking) for supervisor pointers when set.", + bits_range: (28, 28), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "CR4.RESERVED_29_63", + description: "Reports bits allowed to be 1 in CR4", + bits_range: (29, 63), + policy: ProfilePolicy::Inherit + } + ]) + ), + + ( + RegisterAddress::IA32_VMX_VMCS_ENUM, + ValueDefinitions::new(&[ + ValueDefinition{ + short: "MAX_INDEX", + description: "highest index value used for any VCMS encoding", + bits_range: (1, 9), + policy: ProfilePolicy::Inherit + } + ]) + + ), + + ( + RegisterAddress::IA32_VMX_PROCBASED_CTLS2, + ValueDefinitions::new(&[ + // Intel SDM Vol.3D A.3.3 documents that the ALLOWED_ZERO bits are actually always 0 for this MSR. + ValueDefinition { + short:"ALLOWED_ZERO_VIRTUALIZE_APIC_ACCESSES", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_EPT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (1, 1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_DESCRIPTOR_TABLE_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_RDTSCP", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_VIRTUALIZE_X2APIC_MODE", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (4, 4), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_VPID", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (5, 5), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_WBINVD_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (6, 6), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_UNRESTRICTED_GUEST", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (7, 7), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_APIC_REGISTER_VIRTUALIZATION", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (8, 8), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_VIRTUAL_INTERRUPT_DELIVERY", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (9, 9), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_PAUSE_LOOP_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (10, 10), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_RDRAND_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (11, 11), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_INVPCID", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_VM_FUNCTIONS", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (13, 13), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_VMCS_SHADOWING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (14, 14), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_ENCLS_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (15, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_RDSEED_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_PML", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_EPT_VIOLATION_#VE", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (19, 19), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_XSAVES/XRSTORS", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (20, 20), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_PASID_TRANSLATION", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_MODE_BASED_EXECUTE_CONTROL_FO_EPT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SUB_PAGE_WRITE_PERMISSIONS_FOR_EPT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (23, 23), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_INTEL_PT_USES_GUEST_PHYSICAL_ADDRESSES", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (24, 24), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_USE_TSC_SCALING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (25, 25), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_USER_WAIT_AND_PAUSE", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (26, 26), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENABLE_PCONFIG", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (27, 27), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_28_29", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (28, 29), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_VMM_BUS_LOCK_DETECTION", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (30, 30), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_INSTRUCTION_TIMEOU", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (31, 31), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_VIRTUALIZE_APIC_ACCESSES", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (32, 32), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_EPT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (33, 33), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_DESCRIPTOR_TABLE_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (34, 34), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_RDTSCP", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (35, 35), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_VIRTUALIZE_X2APIC_MODE", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (36, 36), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_VPID", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (37, 37), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_WBINVD_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (38, 38), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_UNRESTRICTED_GUEST", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (39, 39), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_APIC_REGISTER_VIRTUALIZATION", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (40, 40), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_VIRTUAL_INTERRUPT_DELIVERY", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (41, 41), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_PAUSE_LOOP_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (42, 42), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_RDRAND_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (43, 43), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_INVPCID", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (44, 44), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_VM_FUNCTIONS", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (45, 45), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_VMCS_SHADOWING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (46, 46), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_ENCLS_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (47, 47), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_RDSEED_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (48, 48), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_PML", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (49, 49), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_EPT_VIOLATION_#VE", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (50, 50), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (51, 51), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_XSAVES/XRSTORS", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (52, 52), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_PASID_TRANSLATION", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (53, 53), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_MODE_BASED_EXECUTE_CONTROL_FO_EPT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (54, 54), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_SUB_PAGE_WRITE_PERMISSIONS_FOR_EPT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (55, 55), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_INTEL_PT_USES_GUEST_PHYSICAL_ADDRESSES", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (56, 56), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_USE_TSC_SCALING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (57, 57), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_USER_WAIT_AND_PAUSE", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (58, 58), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENABLE_PCONFIG", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (59, 59), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_28_29", + description: "Control X is allowed to be 1 if bit X of this MSR is 1", + bits_range: (60, 61), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_VMM_BUS_LOCK_DETECTION", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (62, 62), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_INSTRUCTION_TIMEOUT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-7. (Definitions of Secondary Processor-Based VM-Execution Controls)", + bits_range: (63, 63), + policy: ProfilePolicy::Inherit + }, + ]) + ), + ( + RegisterAddress::IA32_VMX_EPT_VPID_CAP, + ValueDefinitions::new(&[ + ValueDefinition{ + short: "EPT_EXECUTE_ONLY", + description: "The processor supports execute-only translations by EPT", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "PAGE_WALK_LENGTH_4", + description: "Support for Page-walk length of 4", + bits_range: (6, 6), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "PAGE_WALK_LENGTH_5", + description: "Support for Page-walk length of 5", + bits_range: (7, 7), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "EPT_MEM_TYPE_UC", + description: "Software can configure the EPT paging structure to memory type to be unreachable (UC)", + bits_range: (8, 8), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "EPT_MEM_TYPE_WB", + description: "Software can configure the EPT paging structure to memory type to be write-back (WB)", + bits_range: (14, 14), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "EPT_PDE_2M", + description: "Software can configure the EPT PDE to map a 2-Mbyte page", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "EPT_PDPTE_1G", + description: "Software can configure the EPT PDPTE to map a 1-Gbyte page", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "INVEPT", + description: "INVEPT instruction is supported", + bits_range: (20, 20), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "FLAGS_EPT", + description: "Accessed and dirty flags for EPT are supported", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "VM_EXIT_VIOLATIONS_INFO", + description: "If set, the processors advanced VM-exit information for EPT violations", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "SHADOW_STACK_CTL", + description: "Supervisor shadow-stack control is supported", + bits_range: (23, 23), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "SINGLE_CONTEXT_INVEPT", + description: "The single-context INVEPT type is supported", + bits_range: (25, 25), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "ALL_CONTEXT_INVEPT", + description: "The all-context INVEPT type is supported", + bits_range: (26, 26), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "INVVPID", + description: "INVVPID instruction is supported", + bits_range: (32, 32), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "INDIVIDUAL_ADDRESS_INVVPID", + description: "The individual address INVVPID type is supported", + bits_range: (40, 40), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "SINGLE_CONTEXT_INVVPID", + description: "The single-context INVVPID type is supported", + bits_range: (41, 41), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "ALL_CONTEXT_INVVPID", + description: "The all-context INVEPT type is supported", + bits_range: (42, 42), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "SINGLE_CONTEXT_RETAINING_GLOBALS_INVVPID", + description: "The single-context-retaining-globals INVVPID type is supported", + bits_range: (43, 43), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short: "MAX_HLAT_PREFIX", + description: "Enumerates the maximum HLAT prefix size", + bits_range: (48, 53), + policy: ProfilePolicy::Inherit + }, + ]) + ), + + ( + + RegisterAddress::IA32_VMX_TRUE_PINBASED_CTLS, + ValueDefinitions::new(&[ + ValueDefinition { + short:"ALLOWED_ZERO_EXTERNAL_INTERRUPT_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_1_2", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (1, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_NMI_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_4", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (4, 4), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_VIRTUAL_NMIS", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (5, 5), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACTIVATE_VMX_PREEMPTION_TIMER", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (6, 6), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_PROCESS_POSTED_INTERRUPTS", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (7, 7), + policy: ProfilePolicy::Inherit + }, + + + ValueDefinition { + short: "ALLOWED_ZERO", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (8, 31), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition{ + short:"ALLOWED_ONE_EXTERNAL_INTERRUPT_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (32, 32), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_1_2", + description: "VM entry allows control X to be 1 if bit X in this MSR is 1", + bits_range: (33, 34), + policy: ProfilePolicy::Inherit + }, + ValueDefinition{ + short:"ALLOWED_ONE_NMI_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (35, 35), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_4", + description: "VM entry allows control X to be 1 if bit X in this MSR is 1", + bits_range: (36, 36), + policy: ProfilePolicy::Inherit + }, + ValueDefinition{ + short:"ALLOWED_ONE_VIRTUAL_NMIS", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (37, 37), + policy: ProfilePolicy::Inherit + }, + ValueDefinition{ + short:"ALLOWED_ONE_ACTIVATE_VMX__PREEMPTION_TIMER", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (38, 38), + policy: ProfilePolicy::Inherit + }, + ValueDefinition{ + short:"ALLOWED_ONE_PROCESS_POSTED_INTERRUPTS", + description: "See Intel SDM Vol.3C Section 26.6.1 Table 26-5 (Definitions of Pin-Based VM-Execution Controls)", + bits_range: (39, 39), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (40, 63), + policy: ProfilePolicy::Inherit + } + ]) + ), + + ( + RegisterAddress::IA32_VMX_TRUE_PROCBASED_CTLS, + ValueDefinitions::new(&[ + ValueDefinition { + short: "ALLOWED_ZERO_0_1", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (0, 1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_INTERRUPT_WINDOW_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_USE_TSC_OFFSETTING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_4_6", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (4, 6), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_HLT_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (7, 7), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_8", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (8, 8), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_INVLPG_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (9, 9), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_MWAIT_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (10, 10), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_RDPMC_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (11, 11), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_RDTSC_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_13_14", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (13, 14), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CR3_LOAD_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (15, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CR3_STORE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACTIVATE_TERTIARY_CONTROLS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_18", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CR8_LOAD_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (19, 19), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CR8_STORE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (20, 20), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_USE_TPR_SHADOW", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_NMI_WINDOW_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_MOV_DR_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (23, 23), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_UNCONDITIONAL_I/O_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (24, 24), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_USE_I/O_BITMAPS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (25, 25), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_26", + description: "Control X is allowed to be 0 if bit X of this MSR is 0", + bits_range: (26, 26), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_MONITOR_TRAP_FLAG", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (27, 27), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_USE_MSR_BITMAPS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (28, 28), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_MONITOR_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (29, 29), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_PAUSE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (30, 30), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACTIVATE_SECONDARY_CONTROLS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (31, 31), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_0_1", + description: "Control X is allowed to be 1 if bit 32 + X of this MSR is 1", + bits_range: (32, 33), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_INTERRUPT_WINDOW_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (34, 34), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_USE_TSC_OFFSETTING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (35, 35), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_4_6", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (36, 38), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_HLT_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (39, 39), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_8", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (40, 40), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_INVLPG_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (41, 41), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_MWAIT_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (42, 42), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_RDPMC_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (43, 43), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_RDTSC_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (44, 44), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "ALLOWED_ONE_13_14", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (45, 46), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CR3_LOAD_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (47, 47), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CR3_STORE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (48, 48), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ACTIVATE_TERTIARY_CONTROLS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (49, 49), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_18", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (50, 50), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short:"ALLOWED_ONE_CR8_LOAD_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (51, 51), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CR8_STORE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (52, 52), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_USE_TPR_SHADOW", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (53, 53), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_NMI_WINDOW_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (54, 54), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_MOV_DR_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (55, 55), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_UNCONDITIONAL_I/O_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (56, 56), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_USE_I/O_BITMAPS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (57, 57), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_26", + description: "Control X is allowed to be 1 if bit X of this MSR is 1", + bits_range: (58, 58), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short:"ALLOWED_ONE_MONITOR_TRAP_FLAG", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (59, 59), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_USE_MSR_BITMAPS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (60, 60), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_MONITOR_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (61, 61), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_PAUSE_EXITING", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (62, 62), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ACTIVATE_SECONDARY_CONTROLS", + description: "See Intel SDM. Vol.3C Section 26.6.2 Table 26-6 (Definitions of Primary Processor-Based VM-Execution Controls)", + bits_range: (63, 63), + policy: ProfilePolicy::Inherit + }, + + ]) + ), + + ( + RegisterAddress::IA32_VMX_TRUE_EXIT_CTLS, + ValueDefinitions::new(&[ + ValueDefinition { + short: "ALLOWED_ZERO_0_1", + description: "Control X is allowed to be 0 if bit X in this MSR is 0", + bits_range: (0, 1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_DEBUG_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_3_8", + description: "Control X is allowed to be 0 if bit X in this MSR is 0", + bits_range: (3, 8), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_HOST_ADDRESS_SPACE_SIZE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (9, 9), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_10_11", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (10, 11), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_PERF_GLOBAL_CTRL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_13_14", + description: "Control X is allowed to be 0 if bit X in this MSR is 0", + bits_range: (13, 14), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACKNOWLEDGE_INTERRUPT_O_EXIT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (15, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_16_17", + description: "Control X is allowed to be 0 if bit X in this MSR is 0", + bits_range: (16, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (19, 19), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (20, 20), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_VMX_PREEMPTION_TIMER_VALUE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CLEAR_IA32_BNDCFGS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (23, 23), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (24, 24), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CLEAR_IA32_RTIT_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (25, 25), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CLEAR_IA32_LBR_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (26, 26), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CLEAR_UINV", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (27, 27), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_CET_STATE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (28, 28), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_PKRS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (29, 29), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_SAVE_IA32_PERF_GLOBAL_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (30, 30), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ACTIVATE_SECONDARY_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (31, 31), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_0_1", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (32, 33), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short:"ALLOWED_ONE_SAVE_DEBUG_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (34, 34), + policy: ProfilePolicy::Inherit + }, + + ValueDefinition { + short: "ALLOWED_ONE_3_8", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (35, 40), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_HOST_ADDRESS_SPACE_SIZE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (41, 41), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_10_11", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (42, 43), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_PERF_GLOBAL_CTRL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (44, 44), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short: "ALLOWED_ONE_13_14", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (45, 46), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ACKNOWLEDGE_INTERRUPT_O_EXIT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (47, 47), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_16_17", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (48, 49), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_SAVE_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (50, 50), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (51, 51), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_SAVE_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (52, 52), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (53, 53), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_SAVE_VMX_PREEMPTION_TIMER_VALUE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (54, 54), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CLEAR_IA32_BNDCFGS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (55, 55), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (56, 56), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CLEAR_IA32_RTIT_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (57, 57), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CLEAR_IA32_LBR_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (58, 58), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_CLEAR_UINV", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (59, 59), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_CET_STATE", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (60, 60), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_PKRS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (61, 61), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_SAVE_IA32_PERF_GLOBAL_CTL", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (62, 62), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_ACTIVATE_SECONDARY_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.7.1 Table 26-14 (Definitions of Primary VM-Exit Controls)", + bits_range: (63, 63), + policy: ProfilePolicy::Inherit + }, + ]) + ), + + ( + RegisterAddress::IA32_VMX_TRUE_ENTRY_CTLS, + ValueDefinitions::new(&[ + ValueDefinition { + short: "ALLOWED_ZERO_0_1", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (0, 1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_DEBUG_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (2, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_3_8", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (3, 8), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_IA_32E_MODE_GUES", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (9, 9), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ENTRY_TO_SMM", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (10, 10), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_DEACTIVATE_DUAL__MONITOR_TREATMENT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (11, 11), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_12", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (12, 12), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_PERF_GLOBA_L_CTRL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (13, 13), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (14, 14), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (15, 15), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_BNDCFGS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (16, 16), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (17, 17), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_IA32_RTIT_CTL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (18, 18), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_UINV", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (19, 19), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_CET_STATE", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (20, 20), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_GUEST_IA32_LBR_CTL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (21, 21), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_LOAD_PKRS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (22, 22), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_23_24", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (23, 24), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ZERO_ALLOW_SEAM_GUEST_TELEMETRY", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (25, 25), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ZERO_26_31", + description: "VM entry allows control X to be 0 if bit X in this MSR is zero", + bits_range: (26, 31), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_0_1", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (32, 33), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_DEBUG_CONTROLS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (34, 34), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_3_8", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (35, 40), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_IA_32E_MODE_GUES", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (41, 41), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ENTRY_TO_SMM", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (42, 42), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_DEACTIVATE_DUAL__MONITOR_TREATMENT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (43, 43), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_12", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (44, 44), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_PERF_GLOBA_L_CTRL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (45, 45), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_PAT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (46, 46), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_EFER", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (47, 47), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_BNDCFGS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (48, 48), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_CONCEAL_VMX_FROM_PT", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (49, 49), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_IA32_RTIT_CTL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (50, 50), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_UINV", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (51, 51), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_CET_STATE", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (52, 52), + policy: ProfilePolicy::Static(0) + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_GUEST_IA32_LBR_CTL", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (53, 53), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_LOAD_PKRS", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (54, 54), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_23_24", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (55, 56), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_ALLOW_SEAM_GUEST_TELEMETRY", + description: "See Intel SDM Vol.3C Section 26.8.1 Table 26-17. (Definitions of VM-Entry Controls)", + bits_range: (57, 57), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_26_31", + description:"VM entry allows control X to be 1 if bit X + 32 in this MSR is 1", + bits_range: (58, 63), + policy: ProfilePolicy::Inherit + }, + ]) + ), + + ( + RegisterAddress::IA32_VMX_VMFUNC, + ValueDefinitions::new(&[ + ValueDefinition { + short:"ALLOWED_ONE_EPTP_SWITCHING", + description: "See Intel SDM Vol.3C Section 26.6.14 Table 26-10. (Definitions of VM-Function Controls)", + bits_range: (0, 0), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short:"ALLOWED_ONE_1_63", + description: "See Intel SDM Vol.3C Section 26.6.14 Table 26-10. (Definitions of VM-Function Controls)", + bits_range: (1, 63), + policy: ProfilePolicy::Inherit + }, + + ]) + ), + + // NOTE: This MSR is currently not supported by KVM. We keep the definition here regardless. (TODO: Maybe it would be better to remove it?) + ( + RegisterAddress::IA32_VMX_PROCBASED_CTLS3, + ValueDefinitions::new(&[ + ValueDefinition { + short: "ALLOWED_ONE_LOADIWKEY_EXITING", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-8 (Definitions of Tertiary Processor-Based VM-Execution Controls)", + bits_range: (0,0), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_ENABLE_HLAT", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-8 (Definitions of Tertiary Processor-Based VM-Execution Controls)", + bits_range: (1,1), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_EPT_PAGING_WRITE_CONTROL", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-8 (Definitions of Tertiary Processor-Based VM-Execution Controls)", + bits_range: (2,2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_GUEST_PAGING_VERIFICATION", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-8 (Definitions of Tertiary Processor-Based VM-Execution Controls)", + bits_range: (3,3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_IPI_VIRTUALIZATION", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-8 (Definitions of Tertiary Processor-Based VM-Execution Controls)", + bits_range: (4,4), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_SEAM_GUEST_PHYSICAL_ADDRESS_WIDTH", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-8 (Definitions of Tertiary Processor-Based VM-Execution Controls)", + bits_range: (5,5), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_ENABLE_MSR_LIST_INSTRUCTIONS", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-8 (Definitions of Tertiary Processor-Based VM-Execution Controls)", + bits_range: (6,6), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_VIRTUALIZE_IA32_SPEC_CTRL", + description: "See Intel SDM Vol.3C Section 26.6.2 Table 26-8 (Definitions of Tertiary Processor-Based VM-Execution Controls)", + bits_range: (7,7), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_8_63", + description: "Control X is allowed to be 1 if bit X in this MSR is 1", + bits_range: (8,63), + policy: ProfilePolicy::Inherit + }, + ]) + ), + + // NOTE: This MSR is currently not supported by KVM. We keep the definition here regardless. (TODO: Maybe it would be better to remove it?) + ( + RegisterAddress::IA32_VMX_EXIT_CTLS2, + ValueDefinitions::new(&[ + ValueDefinition { + short: "ALLOWED_ONE_0_2", + description:"VM entry allows control X to be 1 if bit X is 1", + bits_range: (0, 2), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_PREMATURELY_BUSY_SHADOW_STACK", + description:"See Intel SDM Vol.3C Section 26.7.1", + bits_range: (3, 3), + policy: ProfilePolicy::Inherit + }, + ValueDefinition { + short: "ALLOWED_ONE_4_63", + description:"VM entry allows control X to be 1 if bit X is 1", + bits_range: (4, 63), + policy: ProfilePolicy::Inherit + } + ]) + ), + ( + RegisterAddress::MSR_PLATFORM_INFO, + ValueDefinitions::new(&[ + ValueDefinition { + short: "PLATFORM_INFORMATION", + description: "Contains power management and other model specific features enumeration. In reality bits 15:8 describe the maximum frequency that does not require turbo. All other bits are reserved", + bits_range: (0, 63), + policy: ProfilePolicy::Deny + } + ]) + ) + ]) +}; + +/// Convenience function to lookup value definitions corresponding to the given MSR register address (as a const parameter). +#[cold] +#[inline(never)] +pub(in crate::x86_64) const fn msr_definitions() -> &'static [ValueDefinition] +{ + const { + let mut out = [].as_slice(); + let intel_definitions = INTEL_MSR_FEATURE_DEFINITIONS.as_slice(); + let mut i = 0; + let length = intel_definitions.len(); + while i < length { + let (addr, definitions) = intel_definitions[i]; + if addr.0 == REG_ADDR { + out = definitions.as_slice(); + break; + } + i += 1; + } + if out.is_empty() { + panic!("MSR definition not found"); + } + out + } +} + +/// Check that the `src_feature_msrs` are compatible with those given in `dest_feature_msrs`. +/// +/// If this check fails, then software that works under the `src_feature_msrs`, may no longer +/// behave correctly with `dest_feature_msrs`. +/// +/// The `src_id` and `dest_id` strings are only used for logging purposes to identify what +/// is being compared (e.g. CPU profile vs host where the profile should be applied, etc). +/// +/// NOTE: This function assumes CPUID compatibility. +/// +/// All register addresses/keys in [`INTEL_MSR_FEATURE_DEFINITIONS`] are checked, except for: +/// - IA32_BIOS_SIGN_ID, +/// - IA32_PERF_CAPABILITIES, +/// - MSR_PLATFORM_INFO +/// +/// IA32_PERF_CAPABILITIES are inherently incompatible between different VMs and we do not +/// think it makes much sense to compare IA32_BIOS_SIGN_ID or MSR_PLATFORM_INFO in this context. +/// +/// # Errors +/// +/// This function does not return early upon error, but rather attempts all MSR-based feature +/// checks while logging errors it encounters. If any of these checks fail an error is returned +/// at the end. +/// +/// We also just use the unit type as the error variant for now, as not much can be done to +/// recover from these errors at runtime and the logs should provide the user with enough +/// information to debug the problem. +/// +/// At this moment in time we prefer the aforementioned approach over designing a complex +/// error type capable of tracking everything that might fail. +pub(in crate::x86_64) fn check_feature_msr_compatibility( + src_feature_msrs: &HashMap, + dest_feature_msrs: &HashMap, + src_id: &str, + dest_id: &str, +) -> Result<(), ()> { + let mut is_err = false; + // First check IA32_ARCH_CAPABILITIES + // Since we are assuming CPUID to be compatible we + // may assume that either both src and dest have this + // MSR or none of them do + if let Some((src_val, dest_val)) = src_feature_msrs + .get(&RegisterAddress::IA32_ARCH_CAPABILITIES.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_ARCH_CAPABILITIES.0)) + { + is_err |= + check_arch_capabilities_compatibility(*src_val, *dest_val, src_id, dest_id).is_err(); + } + + // Next let us consider IA32_VMX_BASIC + let mut true_ctls_exist_src = false; + let mut true_ctls_exist_dest = false; + // Since we assume compatibility of CPUID we can again check that either both src and dest + // have the IA32_VMX_BASIC MSR or none of them do + if let Some((src_val, dest_val)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_BASIC.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_BASIC.0)) + { + true_ctls_exist_src = (*src_val & (1 << 55)) != 0; + true_ctls_exist_dest = (*dest_val & (1 << 55)) != 0; + is_err |= check_vmx_basic_compatibility(*src_val, *dest_val, src_id, dest_id).is_err(); + } + // The following closure saves us some boiler plate when checking the various VMX CTLS that have a default1 class + let check_vmx_ctls_with_default1_class = |vmx_ctrl_reg_address: RegisterAddress, + vmx_true_ctrl_reg_address: RegisterAddress, + check_id: &str, + src_id: &str, + dest_id: &str| + -> Result<(), ()> { + let mut is_err = false; + let src_reg_address = { + conditional_select( + vmx_ctrl_reg_address.0, + vmx_true_ctrl_reg_address.0, + true_ctls_exist_src, + ) + }; + + let dest_reg_address = { + conditional_select( + vmx_ctrl_reg_address.0, + vmx_true_ctrl_reg_address.0, + true_ctls_exist_dest, + ) + }; + + let src_val = src_feature_msrs.get(&src_reg_address); + let dest_val = dest_feature_msrs.get(&dest_reg_address); + if src_val.is_some() && dest_val.is_none() { + error!( + "{check_id} compatibility check failed: unable to compare value of MSR {src_reg_address:#x} of {src_id} with value of MSR {dest_reg_address:#x} of {dest_id}, because the latter value was not found" + ); + is_err = true; + } + if let Some((src_val, dest_val)) = src_val.zip(dest_val) + && let Err(CtlsCheck { + bitset_only_zero_src_lo, + bitset_only_one_src_hi, + }) = check_negative_subset_lo_and_subset_hi(*src_val, *dest_val) + { + is_err = true; + if let Some(bitset) = bitset_only_zero_src_lo { + for_each_bitpos(bitset, |bit_pos| { + debug!( + "{check_id} compatibility check failed: bit {bit_pos} is 0 in MSR:={src_reg_address:#x} of {src_id}, but 1 in MSR:={dest_reg_address:#x} of {dest_id}" + ); + }); + } + + if let Some(bitset) = bitset_only_one_src_hi { + for_each_bitpos(bitset, |bit_pos| { + debug!( + "{check_id} compatibility check failed: bit {bit_pos} is 1 in MSR:={src_reg_address:#x} of {src_id}, but 0 in MSR:={dest_reg_address:#x} of {dest_id}" + ); + }); + } + } + + if is_err { + if let Some(src_val) = src_val + && let Some(dest_val) = dest_val + { + error!( + "{check_id} compatibility check failed: {src_id} register address:={src_reg_address:#x}, {src_id} value:={:#x}, {dest_id} register address:={dest_reg_address:#x}, {dest_id} value:={:#x}", + *src_val, *dest_val + ); + } + Err(()) + } else { + Ok(()) + } + }; + + // Now we consider IA32_VMX_PINBASED_CTLS and/or IA32_VMX_TRUE_BINBASED_CTLS + // (Intel SDM Vol.3D A.3.1) + is_err |= check_vmx_ctls_with_default1_class( + RegisterAddress::IA32_VMX_PINBASED_CTLS, + RegisterAddress::IA32_VMX_TRUE_PINBASED_CTLS, + "IA32_VMX_PINBASED_CTLS", + src_id, + dest_id, + ) + .is_err(); + + // Next up is IA32_VMX_PROCBASED_CTLS and/or IA32_VMX_TRUE_PROCBASED_CTLS + // (Intel SDM Vol.3D A.3.2.) + is_err |= check_vmx_ctls_with_default1_class( + RegisterAddress::IA32_VMX_PROCBASED_CTLS, + RegisterAddress::IA32_VMX_TRUE_PROCBASED_CTLS, + "IA32_PROCBASED_CTLS", + src_id, + dest_id, + ) + .is_err(); + // Check IA32_VMX_EXIT_CTLS and/or IA32_VMX_TRUE_EXIT_CTLS + // (Intel SDM Vol.3D A.4) + is_err |= check_vmx_ctls_with_default1_class( + RegisterAddress::IA32_VMX_EXIT_CTLS, + RegisterAddress::IA32_VMX_TRUE_EXIT_CTLS, + "IA32_VMX_EXIT_CTLS", + src_id, + dest_id, + ) + .is_err(); + // Check IA32_VMX_ENTRY_CTLS and/or IA32_VMX_TRUE_ENTRY_CTLS + // (Intel SDM Vol.3D A.5) + is_err |= check_vmx_ctls_with_default1_class( + RegisterAddress::IA32_VMX_ENTRY_CTLS, + RegisterAddress::IA32_VMX_TRUE_ENTRY_CTLS, + "IA32_VMX_ENTRY_CTLS", + src_id, + dest_id, + ) + .is_err(); + // Check IA32_VMX_MISC + if let Some((src_val, dest_val)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_MISC.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_MISC.0)) + { + is_err |= check_vmx_misc_msr(*src_val, *dest_val, src_id, dest_id).is_err(); + } + // Check IA32_VMX_CR0_FIXED0 + if let Some((src_fixed0, dest_fixed0)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_CR0_FIXED0.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_CR0_FIXED0.0)) + { + is_err |= + check_cr_i_compatibility::<0>(*src_fixed0, *dest_fixed0, src_id, dest_id).is_err(); + } + + // Check IA32_VMX_CR4_FIXED0 + if let Some((src_fixed0, dest_fixed0)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_CR4_FIXED0.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_CR4_FIXED0.0)) + { + is_err |= + check_cr_i_compatibility::<4>(*src_fixed0, *dest_fixed0, src_id, dest_id).is_err(); + } + + // Check IA32_VMX_VMCS_ENUM + if let Some((src_val, dest_val)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_VMCS_ENUM.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_VMCS_ENUM.0)) + { + is_err |= check_vmx_vmcs_enum_compatibility(*src_val, *dest_val, src_id, dest_id).is_err(); + } + + // Check IA32_VMX_PROCBASED_CTLS2 + // This MSR exists only if bit 63 of IA32_VMX_PROCBASED_CTLS is set + // (note that if it is set on src then our IA32_VMX_PROCBASED_CTLS check + // ensures that it is also set on dest) + if let Some((src_val, dest_val)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_PROCBASED_CTLS2.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_PROCBASED_CTLS2.0)) + { + let src_val = *src_val; + let dest_val = *dest_val; + // First verify that the first 32 bits are indeed 0 as documented by Intel, otherwise we have misunderstood the documentation + // and we should not continue. + let lo_mask = u64::from(u32::MAX); + assert_eq!( + src_val & lo_mask, + 0, + "BUG: The 32-first bits of the IA32_VMX_PROCBASED_CTLS2 MSR were not zero for src" + ); + assert_eq!( + dest_val & lo_mask, + 0, + "BUG: The 32-first bits of the IA32_VMX_PROCBASED_CTLS2 MSR were not zero for dest" + ); + // Note that the 32-first bits are documented to always be 0 + if let Err(bits_only_in_src) = check_subset(src_val, dest_val) { + is_err = true; + error!( + "IA32_VMX_PROCBASED_CTLS2 compatibility check failed: {src_id} value:={src_val:#x}, {dest_id} value:={dest_val:#x}" + ); + for_each_bitpos(bits_only_in_src, |bit_pos| { + debug!( + "IA32_VMX_PROCBASED_CTLS2 check failed: VM entry allows control X:={bit_pos} to be 1 for {src_id}, but not for {dest_id}" + ); + }); + } + } + + // Check IA32_VMX_PROCBASED_CTLS3 + // This MSR exists only if bit 49 of IA32_VMX_PROCBASED_CTLS is set + // (note that if it is set on src then our IA32_VMX_PROCBASED_CTLS check + // ensures that it is also set on dest) + + if let Some((src_val, dest_val)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_PROCBASED_CTLS3.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_PROCBASED_CTLS3.0)) + && let Err(bits_only_in_src) = check_subset(*src_val, *dest_val) + { + is_err = true; + error!( + "IA32_VMX_PROCBASED_CTLS3 compatibility check failed: {src_id} value:= {:#x}, {dest_id} value:={:#x}", + *src_val, *dest_val + ); + + for_each_bitpos(bits_only_in_src, |bit_pos| { + debug!( + "IA32_VMX_PROCBASED_CTLS3 compatibility check failed: VM entry allows control X:={bit_pos} for {src_id}, but not for {dest_id}" + ); + }); + } + + // Check IA32_VMX_EXIT_CTLS2 + // This MSR exists only if bit 63 of the IA32_VMX_EXIT_CTLS is set + // (note that if it is set on src then our IA32_VMX_EXIT_CTLS check + // ensures that it is also set on dest) + if let Some((src_val, dest_val)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_EXIT_CTLS2.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_EXIT_CTLS2.0)) + && let Err(bits_only_in_src) = check_subset(*src_val, *dest_val) + { + is_err = true; + error!( + "IA32_VMX_EXIT_CTLS2 compatibility check failed: {src_id} value:={:#x}, {dest_id} value:={:#x}", + *src_val, *dest_val + ); + for_each_bitpos(bits_only_in_src, |bit_pos| { + debug!( + "IA32_VMX_EXIT_CTLS2 compatibility check failed: bit {bit_pos} is set for {src_id}, but not for {dest_id}" + ); + }); + } + + // Check IA32_VMX_EPT_VPID_CAP (Intel SDM Vol.3D A.10) + // + // This MSR is only available on processors where bit 63 of IA32_VMX_PROCBASED_CTLS is 1 and that either + // have bit 33 of IA32_VMX_PROCBASED_CTLS2 set, or bit 37 of IA32_VMX_PROC_BASED_CTLS2 set. Since we + // already check for compatibility of those bits, we may assume that if this MSR is available for src, then + // it is also available for dest. + if let Some((src_val, dest_val)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_EPT_VPID_CAP.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_EPT_VPID_CAP.0)) + { + is_err |= check_vpid_and_ept_capabilities(*src_val, *dest_val, src_id, dest_id).is_err(); + } + + if let Some((src_val, dest_val)) = src_feature_msrs + .get(&RegisterAddress::IA32_VMX_VMFUNC.0) + .zip(dest_feature_msrs.get(&RegisterAddress::IA32_VMX_VMFUNC.0)) + && let Err(bits_only_in_src) = check_subset(*src_val, *dest_val) + { + is_err = true; + error!( + "IA32_VMX_VMFUNC compatibility check failed: {src_id} value:={:#x}, {dest_id} value:={:#x}", + *src_val, *dest_val + ); + for_each_bitpos(bits_only_in_src, |bit_pos| { + debug!( + "IA32_VMX_VMFUNC compatibility check failed: VM entry allows bit X:={bit_pos} of the VM-function controls to be 1 for {src_id}, but not for {dest_id}" + ); + }); + } + + if is_err { Err(()) } else { Ok(()) } +} + +/// `a` if `condition` else `b` +fn conditional_select(a: u32, b: u32, condition: bool) -> u32 { + let a_mask = u32::from(condition).wrapping_neg(); + let b_mask = !a_mask; + (a & a_mask) | (b & b_mask) +} + +/// Check that the values of MSR IA32_ARCH_CAPABILITIES are compatible. +/// +/// If this check fails then programs that work when the value is `src_val`, may possibly +/// no longer work if the value is `dest_val`. +/// +/// See: Ch.2 Table 2-2. IA-32 Architectural MSRs in Intel SDM Vol.4 +fn check_arch_capabilities_compatibility( + src_val: u64, + dest_val: u64, + src_id: &str, + dest_id: &str, +) -> Result<(), ()> { + // Make a mask out of + const TSX_CONTROL: u64 = 1 << 7; + const MCU_CONTROL: u64 = 1 << 9; + const MISC_PACKAGE_CTLS: u64 = 1 << 10; + const ENERGY_FILTERING_CTL: u64 = 1 << 11; + const DOITM: u64 = 1 << 12; + const MCU_ENUMERATION: u64 = 1 << 16; + const FB_CLEAR: u64 = 1 << 17; + const FB_CLEAR_CTRL: u64 = 1 << 18; + const XAPIC_DISABLE_STATUS: u64 = 1 << 21; + const MCU_EXTENDED_SERVICE: u64 = 1 << 22; + const OVERCLOCKING_STATUS: u64 = 1 << 23; + const GDS_CTRL: u64 = 1 << 25; + // TODO: Should we perhaps ignore checking this (is it too strict)? + const RFDS_CLEAR: u64 = 1 << 28; + const IGN_UMONITOR_SUPPORT: u64 = 1 << 29; + const MON_UMON_MITG_SUPPORT: u64 = 1 << 30; + const PBOPT_SUPPORT: u64 = 1 << 32; + + let mask: u64 = { + TSX_CONTROL + | MCU_CONTROL + | MISC_PACKAGE_CTLS + | ENERGY_FILTERING_CTL + | DOITM + | MCU_ENUMERATION + | FB_CLEAR + | FB_CLEAR_CTRL + | XAPIC_DISABLE_STATUS + | MCU_EXTENDED_SERVICE + | OVERCLOCKING_STATUS + | GDS_CTRL + | IGN_UMONITOR_SUPPORT + | MON_UMON_MITG_SUPPORT + | PBOPT_SUPPORT + | RFDS_CLEAR + }; + if let Err(only_in_src) = check_subset(src_val & mask, dest_val & mask) { + error!( + "IA32_ARCH_CAPABILITIES compatibility check failed: {src_id} value:={src_val:#x}, {dest_id} value:={dest_val:#x}" + ); + let definitions = msr_definitions::<{ RegisterAddress::IA32_ARCH_CAPABILITIES.0 }>(); + log_features_only_in_src(only_in_src, src_id, definitions, "IA32_ARCH_CAPABILITIES"); + Err(()) + } else { + Ok(()) + } +} + +/// Check that the values of MSR IA32_VMX_BASIC are compatible. +/// +/// See Intel SDM Vol.3D A.1 for more information about the IA32_VMX_BASIC MSR +fn check_vmx_basic_compatibility( + src_val: u64, + dest_val: u64, + src_id: &str, + dest_id: &str, +) -> Result<(), ()> { + let mut is_err = false; + // All bits between 0 and 53 are expected to be equal (except bit 49) + let req_eq_mask: u64 = ((1 << 54) - 1) & (!(1 << 49)); + let src_req_eq = src_val & req_eq_mask; + let dest_req_eq = dest_val & req_eq_mask; + if src_req_eq != dest_req_eq { + is_err = true; + let definitions = msr_definitions::<{ RegisterAddress::IA32_VMX_BASIC.0 }>(); + log_inequalities( + src_req_eq, + dest_req_eq, + definitions, + src_id, + dest_id, + "IA32_VMX_BASIC compatibility", + ); + } + // bits 49, 54, 55, and 56 indicate some form of capability and we need to check + // that these bits in the `src_value` are a subset of those in `dest_value` + let req_subset_eq_mask: u64 = (1 << 54) | (1 << 55) | (1 << 56) | (1 << 49); + let src_val_seq = req_subset_eq_mask & src_val; + let dest_val_seq = req_subset_eq_mask & dest_val; + is_err |= check_subset(src_val_seq, dest_val_seq).is_err(); + + if is_err { + error!( + "IA32_VMX_BASIC compatibility check failed: {src_id} value:={src_val:#x}, {dest_id} value:={dest_val:#x}" + ); + Err(()) + } else { + Ok(()) + } +} + +/// Check that no values are only in a +/// +/// Upon error a bitset is returned with the +/// bits that are only available in `src_val` +fn check_subset(src_val: u64, dest_val: u64) -> Result<(), u64> { + let only_in_src_val = src_val & (src_val ^ dest_val); + if only_in_src_val != 0 { + Err(only_in_src_val) + } else { + Ok(()) + } +} + +/// Checks the following: +/// 1. For any X < 32; If bit X of src_val is 0 then bit X of dest_val is also 0 +/// 2. For any X >= 32; If bit X of src_val is 1 then bit X of dest_val is also 1 +struct CtlsCheck { + bitset_only_zero_src_lo: Option, + bitset_only_one_src_hi: Option, +} + +fn check_negative_subset_lo_and_subset_hi(src_val: u64, dest_val: u64) -> Result<(), CtlsCheck> { + let lo_mask = (1_u64 << 32) - 1; + let hi_mask = !lo_mask; + + let lo_check = check_subset((!src_val) & lo_mask, (!dest_val) & lo_mask); + + let hi_check = check_subset(src_val & hi_mask, dest_val & hi_mask); + + if lo_check.is_ok() && hi_check.is_ok() { + Ok(()) + } else { + Err(CtlsCheck { + bitset_only_zero_src_lo: lo_check.err(), + bitset_only_one_src_hi: hi_check.err(), + }) + } +} + +/// Check that the values of MSR IA32_VMX_MISC are compatible. +/// +/// See Intel SDM Vol.3D A.6 for more information about the IA32_VMX_MISC MSR +fn check_vmx_misc_msr( + src_value: u64, + dest_value: u64, + src_id: &str, + dest_id: &str, +) -> Result<(), ()> { + let mut is_err = false; + let subset_eq_check_mask: u64 = { + (1 << 5) + | (1 << 6) + | (1 << 7) + | (1 << 8) + | (1 << 14) + | (1 << 15) + | (1 << 28) + | (1 << 29) + | (1 << 30) + }; + if let Err(only_in_src) = check_subset( + subset_eq_check_mask & src_value, + subset_eq_check_mask & dest_value, + ) { + is_err = true; + let definitions = msr_definitions::<{ RegisterAddress::IA32_VMX_MISC.0 }>(); + log_features_only_in_src(only_in_src, src_id, definitions, "IA32_VMX_MISC"); + } + + let eq_mask: u64 = { + // TODO: Do we also need to check that the MSEG revisions match? + (16..=24).fold(0_u64, |acc, next| acc | (1 << next)) + }; + + let src_req_eq_val = src_value & eq_mask; + let dest_req_eq_val = dest_value & eq_mask; + if src_req_eq_val != dest_req_eq_val { + is_err = true; + let definitions = msr_definitions::<{ RegisterAddress::IA32_VMX_MISC.0 }>(); + log_inequalities( + src_req_eq_val, + dest_req_eq_val, + definitions, + src_id, + dest_id, + "IA32_VMX_MISC", + ); + } + + let leq_mask: u64 = { (25..=27).fold(0_u64, |acc, next| acc | (1 << next)) }; + + let src_req_leq = src_value & leq_mask; + let dest_req_leq = dest_value & leq_mask; + if src_req_leq > dest_req_leq { + is_err = true; + debug!( + "IA32_VMX_MISC compatibility check failed when checking definition: {:?}, {src_id} has value:={src_req_leq}, {dest_id} has value:={dest_req_leq}", + max_msr_store_lists_def(), + ); + } + + if is_err { + error!( + "IA32_VMX_MISC compatibility check failed: {src_id} value:={src_value:#x}, {dest_id} value:={dest_value:#x}" + ); + Err(()) + } else { + Ok(()) + } +} + +/// Check compatibility of MSRs IA32_VMX_CR{I}_FIXED0 for I = 0, 4. +/// +/// See Intel SDM Vol.3D A.7 & A.8 for more information about these MSRs. +/// +/// NOTE: We don't need to check compatibility for CR{I}_FIXED1 because +/// that is ensured by CPUID. +fn check_cr_i_compatibility( + src_fixed0: u64, + dest_fixed0: u64, + src_id: &str, + dest_id: &str, +) -> Result<(), ()> { + let cri = const { + match I { + 0 => "CR0", + 4 => "CR4", + _ => { + panic!("only 0 and 4 may be used") + } + } + }; + + // Need to ensure that there are no bits that are only 0 in src_fixed0 and also no bits + // that are only 1 in src_fixed1. + + if let Err(only_zero_in_src) = check_subset(!src_fixed0, !dest_fixed0) { + error!( + "IA32_VMX_{cri}_FIXED0 compatibility check failed: {src_id} value:={src_fixed0:#x}, {dest_id} value:={dest_fixed0:#x}" + ); + for_each_bitpos(only_zero_in_src, |bit_pos| { + debug!( + "IA32_VMX_{cri}_FIXED0 compatibility check failed: bit {bit_pos} is allowed to be 0 in {cri} for {src_id}, but not for {dest_id}" + ); + }); + Err(()) + } else { + Ok(()) + } +} + +/// Check compatibility of MSRs IA32_VMX_VMCS_ENUM. +/// +/// See Intel SDM Vol.3D A.9 for more information about IA32_VMX_VMCS_ENUM. +fn check_vmx_vmcs_enum_compatibility( + src_value: u64, + dest_value: u64, + src_id: &str, + dest_id: &str, +) -> Result<(), ()> { + let mask = (1..=9).fold(0_u64, |acc, next| acc | (1 << next)); + let src_req_leq = src_value & mask; + let dest_req_leq = dest_value & mask; + if src_req_leq > dest_req_leq { + error!( + "VMX_VMCS_ENUM compatibility check failed: MAX_INDEX for {src_id}:={src_req_leq} is greater than MAX_INDEX:={dest_req_leq} for {dest_id}" + ); + Err(()) + } else { + Ok(()) + } +} + +/// Check compatibility of MSRs IA32_VMX_EPT_VPID_CAP. +/// +/// See (Intel TODO:) Vol. 3D A.10 for more information about IA32_VMX_EPT_VPID_CAP. +// Only if IA32_VMX_PROCBASED_CTLS[63] & (IA32_VMX_PROCBASED_CTLS2[33] | IA32_VMX_PROCBASED_CTLS2[37]) +fn check_vpid_and_ept_capabilities( + src_value: u64, + dest_value: u64, + src_id: &str, + dest_id: &str, +) -> Result<(), ()> { + let mut is_err = false; + let subset_eq_mask = { (1 << 44) - 1 }; + + if let Err(bits_only_in_src) = + check_subset(src_value & subset_eq_mask, dest_value & subset_eq_mask) + { + is_err = true; + let definitions = msr_definitions::<{ RegisterAddress::IA32_VMX_EPT_VPID_CAP.0 }>(); + log_features_only_in_src( + bits_only_in_src, + src_id, + definitions, + "IA32_VMX_EPT_VPID_CAP", + ); + } + + let leq_mask = { (48..=53).fold(0_u64, |acc, next| acc | (1 << next)) }; + let src_req_leq = src_value & leq_mask; + let dest_req_leq = dest_value & leq_mask; + if src_req_leq > dest_req_leq { + is_err = true; + debug!( + "IA32_VMX_EPT_VPID_CAP compatibility check failed: maximum HLAT prefix size is {src_req_leq} for {src_id}, but {dest_req_leq} for {dest_id}" + ); + } + if is_err { + error!( + "IA32_VMX_EPT_VPID_CAP compatibility check failed: {src_id} value:={src_value:#x}, {dest_id} value:={dest_value:#x}" + ); + Err(()) + } else { + Ok(()) + } +} + +fn for_each_bitpos(bits: u64, mut cb: impl FnMut(u8)) { + let mut bits = bits; + while bits != 0 { + let pos = bits.trailing_zeros() as u8; + cb(pos); + let lsb = bits & bits.wrapping_neg(); + bits ^= lsb; + } +} + +#[inline(never)] +#[cold] +fn log_features_only_in_src( + only_in_src: u64, + src_id: &str, + definitions: &[ValueDefinition], + check_id: &str, +) { + for_each_bitpos(only_in_src, |bit_pos| { + let Some(def) = definitions + .iter() + .find(|def| (def.bits_range.0..=def.bits_range.1).contains(&bit_pos)) + else { + debug!( + "{check_id} compatibility check failed: bit:={bit_pos} is only set for {src_id}" + ); + warn!( + "unable to produce proper debug log: No MSR value definition found for bit:={bit_pos} check:={check_id} compatibility" + ); + return; + }; + debug!( + "{check_id} compatibility check failed: feature bit {bit_pos} only set for {src_id}: feature definition:={def:?}" + ); + }); +} + +#[inline(never)] +#[cold] +fn log_inequalities( + src_val: u64, + dest_val: u64, + definitions: &[ValueDefinition], + src_id: &str, + dest_id: &str, + check_id: &str, +) { + for def in definitions { + let mask = + (def.bits_range.0..=def.bits_range.1).fold(0_u64, |acc, next| acc | (1_u64 << next)); + let val_src = mask & src_val; + let val_dest = mask & dest_val; + if src_val != dest_val { + debug!( + "Check: {check_id} compatibility failed: on definition:={def:?}, values are required to be equal, but we have {src_id} value:={val_src:#x}, {dest_id} value:={val_dest:#x}" + ); + } + } +} + +#[inline(never)] +#[cold] +const fn max_msr_store_lists_def() -> &'static ValueDefinition { + const { + let defs = msr_definitions::<{ RegisterAddress::IA32_VMX_MISC.0 }>(); + // Currently stored at index = 8, if this changes we make sure that we fail at compile time. + // We do not perform a search as the order is unlikely to change frequently and we want to keep + // compile times down. + let def = &defs[8]; + assert!( + def.bits_range.0 == 25, + "MAX_MSR_STORE_LISTS definition is no longer at index 8 in the ValueDefinitions corresponding to IA32_VMX_MISC, please update the index" + ); + assert!( + def.bits_range.1 == 27, + "MAX_MSR_STORE_LISTS definition is no longer at index 8 in the ValueDefinitions corresponding to IA32_VMX_MISC, please update the index" + ); + def + } +} diff --git a/arch/src/x86_64/msr_definitions/intel/non_architectural_msrs.rs b/arch/src/x86_64/msr_definitions/intel/non_architectural_msrs.rs new file mode 100644 index 0000000000..3eddbce7cd --- /dev/null +++ b/arch/src/x86_64/msr_definitions/intel/non_architectural_msrs.rs @@ -0,0 +1,142 @@ +// Copyright © 2025 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! This module contains a list of all known non-architectural MSRS for various Intel +//! CPUs. This list only helps us detect new MSRs that we are not (yet) aware of when +//! generating CPU profiles, but has no importance beyond that. + +/// A list of known non-architectural MSRs +/// +/// Note: KVM_GET_MSR_FEATURE_INDEX_LIST may return non-architectural MSRS. We append those +/// to [`crate::x86_64::msr_definitions_intel::INTEL_MSR_FEATURE_DEFINITIONS`] and not here. +pub(in crate::x86_64) const NON_ARCHITECTURAL_INTEL_MSRS: [u32; 1255] = [ + 0x2a, 0x34, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, + 0x66, 0x67, 0xa0, 0xa1, 0xcd, 0xe2, 0xe4, 0x11e, 0x13c, 0x19d, 0x17d, 0x1a2, 0x1a4, 0x1a6, + 0x1a7, 0x1ad, 0x1c8, 0x1c9, 0x3f8, 0x3f9, 0x3fa, 0x3fd, 0x606, 0x610, 0x611, 0x638, 0x639, + 0x660, 0x664, 0x668, 0x669, 0x66e, 0x107cc, 0x107cd, 0x107ce, 0x107cf, 0x107d0, 0x107d1, + 0x107d2, 0x107d3, 0x107d8, 0x1aa, 0x1ae, 0x1fc, 0x300, 0x301, 0x3fc, 0x4e0, 0x4e2, 0x4e3, + 0x60a, 0x60b, 0x60c, 0x60d, 0x613, 0x614, 0x618, 0x619, 0x61b, 0x61c, 0x632, 0x641, 0x64c, + 0x64f, 0x680, 0x681, 0x682, 0x683, 0x684, 0x685, 0x686, 0x687, 0x688, 0x689, 0x68a, 0x68b, + 0x68c, 0x68d, 0x68e, 0x68f, 0x690, 0x691, 0x692, 0x693, 0x694, 0x695, 0x696, 0x697, 0x698, + 0x699, 0x69a, 0x69b, 0x69c, 0x69d, 0x69e, 0x69f, 0x6c0, 0x6c1, 0x6c2, 0x6c3, 0x6c4, 0x6c5, + 0x6c6, 0x6c7, 0x6c8, 0x6c9, 0x6ca, 0x6cb, 0x6cc, 0x6cd, 0x6ce, 0x6cf, 0x6d0, 0x6d1, 0x6d2, + 0x6d3, 0x6d4, 0x6d5, 0x6d6, 0x6d7, 0x6d8, 0x6d9, 0x6da, 0x6db, 0x6dc, 0x6dd, 0x6de, 0x6df, + 0xdc0, 0xdc1, 0xdc2, 0xdc3, 0xdc4, 0xdc5, 0xdc6, 0xdc7, 0xdc8, 0xdc9, 0xdca, 0xdcb, 0xdcc, + 0xdcd, 0xdce, 0xdcf, 0xdd0, 0xdd1, 0xdd2, 0xdd3, 0xdd4, 0xdd5, 0xdd6, 0xdd7, 0xdd8, 0xdd9, + 0xdda, 0xddb, 0xddc, 0xddd, 0xdde, 0xddf, 0x33, 0x2a0, 0x1309, 0x130a, 0x130b, 0x14c1, 0x14c2, + 0x14c3, 0x14c4, 0x1ac, 0x3f6, 0x393, 0x394, 0x395, 0x396, 0x3b0, 0x3b1, 0x3b2, 0x3b3, 0x3b4, + 0x3b5, 0x3b6, 0x3b7, 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0xc00, 0xc01, + 0xc02, 0xc10, 0xc11, 0xc20, 0xc21, 0xc22, 0xc30, 0xc31, 0xc32, 0xc33, 0xc34, 0xc35, 0xc36, + 0xc37, 0xc40, 0xc41, 0xc42, 0xc50, 0xc51, 0xc52, 0xc53, 0xc54, 0xc55, 0xc56, 0xc57, 0xc60, + 0xc61, 0xc62, 0xc70, 0xc71, 0xc72, 0xc73, 0xc74, 0xc75, 0xc76, 0xc77, 0xd94, 0xd95, 0xd96, + 0xd97, 0xd98, 0xd99, 0xd9a, 0xd9b, 0xda1, 0xda2, 0xdb3, 0xdb4, 0xdb5, 0xdb6, 0xdb7, 0xdb8, + 0xdb9, 0xdba, 0xdbb, 0xde0, 0xde1, 0xde2, 0xdf0, 0xdf1, 0xdf2, 0xdf3, 0xdf4, 0xdf5, 0xdf6, + 0xdf7, 0xdf8, 0xdf9, 0xdfa, 0xdfb, 0xe02, 0xe03, 0xe04, 0xe05, 0xe06, 0xe07, 0xe08, 0xe09, + 0xe0a, 0xe0b, 0xe0c, 0xe0d, 0xe0e, 0xe0f, 0xe10, 0xe11, 0xe12, 0xe13, 0xe14, 0xe15, 0xe16, + 0xe17, 0xe18, 0xe19, 0xe1a, 0xe1b, 0xe1c, 0xe1d, 0xe1e, 0xe1f, 0xe20, 0xe21, 0xe22, 0xe23, + 0xe24, 0xe25, 0xe26, 0xe27, 0xe28, 0xe29, 0xe2a, 0xe2b, 0xe2c, 0xe2d, 0xe2e, 0xe2f, 0xe30, + 0xe31, 0xe32, 0xe33, 0xe34, 0xe35, 0xe36, 0xe37, 0xe38, 0xe39, 0xe3a, 0xe3b, 0xe3c, 0xe3d, + 0xe3e, 0xe3f, 0xe45, 0xe46, 0xe49, 0xe4a, 0xe4d, 0xe4e, 0xe54, 0xe55, 0xe56, 0xe59, 0xe5a, + 0xe5c, 0xe5d, 0xe5e, 0xf40, 0xf41, 0xf42, 0xf50, 0xf51, 0xf52, 0xf53, 0xf54, 0xf55, 0xf56, + 0xf57, 0xf58, 0xf59, 0xf5a, 0xf5b, 0xfc0, 0xfc1, 0xfc2, 0xfd0, 0xfd1, 0xfd2, 0xfd3, 0xfd4, + 0xfd5, 0xfd6, 0xfd7, 0xfd8, 0xfd9, 0xfda, 0xfdb, 0x3fe, 0x63a, 0x640, 0x642, 0x700, 0x701, + 0x702, 0x703, 0x704, 0x705, 0x706, 0x707, 0x708, 0x709, 0x710, 0x711, 0x712, 0x713, 0x715, + 0x717, 0x718, 0x719, 0x720, 0x721, 0x722, 0x723, 0x725, 0x726, 0x727, 0x728, 0x729, 0x730, + 0x731, 0x732, 0x733, 0x735, 0x736, 0x737, 0x738, 0x739, 0x740, 0x741, 0x742, 0x743, 0x745, + 0x746, 0x747, 0x748, 0x749, 0x17f, 0x39c, 0xc08, 0xc09, 0xc16, 0xc17, 0xc24, 0xc38, 0xc39, + 0xda4, 0xde4, 0x648, 0x649, 0x64a, 0x64b, 0xc06, 0xc35, 0xe44, 0xe50, 0xe51, 0xe52, 0xe53, + 0xe57, 0xe58, 0xe59, 0xe64, 0xe70, 0xe71, 0xe72, 0xe73, 0xe74, 0xe76, 0xe77, 0xe78, 0xe79, + 0xe7a, 0xe84, 0xe90, 0xe91, 0xe92, 0xe93, 0xe94, 0xe96, 0xe97, 0xe98, 0xe99, 0xe9a, 0xea4, + 0xeb0, 0xeb1, 0xeb2, 0xeb3, 0xeb4, 0xeb6, 0xeb7, 0xeb8, 0xeb9, 0xeba, 0xec4, 0xed0, 0xed1, + 0xed2, 0xed3, 0xed4, 0xed6, 0xed7, 0xed8, 0xed9, 0xeda, 0x6b0, 0x6b1, 0x716, 0x630, 0x631, + 0x35, 0x53, 0xe2, 0x1af, 0x61e, 0x620, 0x70a, 0x714, 0x71a, 0x724, 0x72a, 0x72b, 0x72c, 0x72d, + 0x72e, 0x72f, 0x734, 0x73a, 0x73b, 0x73c, 0x73d, 0x73e, 0x73f, 0x744, 0xe40, 0xe41, 0xe42, + 0xe43, 0xe44, 0xe45, 0xe46, 0xe47, 0xe48, 0xe49, 0xe4b, 0xe60, 0xe61, 0xe62, 0xe63, 0xe64, + 0xe65, 0xe66, 0xe67, 0xe68, 0xe69, 0xe6a, 0xe6b, 0xe70, 0xe71, 0xe72, 0xe73, 0xe74, 0xe75, + 0xe76, 0xe77, 0xe78, 0xe79, 0xe7a, 0xe7b, 0xe80, 0xe81, 0xe82, 0xe83, 0xe84, 0xe85, 0xe86, + 0xe87, 0xe88, 0xe89, 0xe8b, 0xe90, 0xe91, 0xe92, 0xe93, 0xe94, 0xe95, 0xe96, 0xe97, 0xe98, + 0xe99, 0xe9a, 0xe9b, 0xea0, 0xea1, 0xea2, 0xea3, 0xea4, 0xea5, 0xea6, 0xea7, 0xea8, 0xea9, + 0xeaa, 0xeab, 0xeb0, 0xeb1, 0xeb5, 0xebb, 0xec0, 0xec1, 0xec2, 0xec3, 0xec4, 0xec5, 0xec6, + 0xec7, 0xec8, 0xec9, 0xeca, 0xecb, 0xed0, 0xed1, 0xed2, 0xed3, 0xed4, 0xed5, 0xed6, 0xed7, + 0xed8, 0xed9, 0xeda, 0xedb, 0xee0, 0xee1, 0xee2, 0xee3, 0xee4, 0xee5, 0xee6, 0xee7, 0xee8, + 0xee9, 0xeea, 0xeeb, 0xef0, 0xef1, 0xef2, 0xef3, 0xef4, 0xef5, 0xef6, 0xef7, 0xef8, 0xef9, + 0xefa, 0xefb, 0xf00, 0xf01, 0xf02, 0xf03, 0xf04, 0xf05, 0xf06, 0xf07, 0xf08, 0xf09, 0xf0a, + 0xf0b, 0xf10, 0xf11, 0xf12, 0xf13, 0xf14, 0xf15, 0xf16, 0xf17, 0xf18, 0xf19, 0xf1a, 0xf1b, + 0x3f7, 0x64d, 0x64e, 0x64f, 0x652, 0x653, 0x655, 0x656, 0x658, 0x659, 0x65a, 0x65b, 0x65c, + 0x80, 0x1f4, 0x1f5, 0x1fb, 0x2f4, 0x2f5, 0x620, 0x350, 0x351, 0x354, 0x355, 0x3f8, 0x620, + 0x660, 0x662, 0x394, 0x395, 0x396, 0x3b0, 0x3b1, 0x3b2, 0x3b3, 0x700, 0x701, 0x702, 0x703, + 0x708, 0x709, 0x70a, 0x70b, 0x710, 0x711, 0x712, 0x713, 0x718, 0x719, 0x71a, 0x71b, 0x720, + 0x721, 0x722, 0x723, 0x728, 0x729, 0x72a, 0x72b, 0x730, 0x731, 0x732, 0x733, 0x738, 0x739, + 0x73a, 0x73b, 0xe02, 0x33, 0xa0, 0xa5, 0x151, 0x1f1, 0x329, 0x3f2, 0x541, 0x657, 0x65e, 0x65f, + 0xa0, 0xa7, 0x601, 0x650, 0x664, 0x1a4, 0x3f7, 0x540, 0x541, 0x1309, 0x130a, 0x130b, 0x14c1, + 0x14c2, 0x14c3, 0x14c4, 0x14c5, 0x14c6, 0x396, 0x2000, 0x2001, 0x2002, 0x2003, 0x2008, 0x2009, + 0x200a, 0x200b, 0x2010, 0x2011, 0x2012, 0x2013, 0x2018, 0x2019, 0x201a, 0x201b, 0x2020, 0x2021, + 0x2022, 0x2023, 0x2028, 0x2029, 0x202a, 0x202b, 0x2030, 0x2031, 0x2032, 0x2033, 0x2038, 0x2039, + 0x203a, 0x203b, 0x2040, 0x2041, 0x2042, 0x2043, 0x2048, 0x2049, 0x204a, 0x204b, 0x2fd0, 0x2fd1, + 0x2fd2, 0x2fd3, 0x2fd4, 0x2fd5, 0x2fd8, 0x2fd9, 0x2fda, 0x2fdb, 0x2fdc, 0x2fdd, 0x2fde, 0x2fdf, + 0x2ff0, 0x2ff2, 0xe2, 0x17d, 0x1a2, 0x1ad, 0x1ae, 0x606, 0x618, 0x619, 0x61b, 0x61c, 0x620, + 0x639, 0x612, 0x618, 0x619, 0x61b, 0x61c, 0x33, 0xa7, 0xed, 0xee, 0xef, 0xf0, 0x105, 0x1a4, + 0x1ad, 0x1ae, 0x2c2, 0x2c3, 0x2c4, 0x2c5, 0x2c6, 0x2c7, 0x2c8, 0x2c9, 0x2d9, 0x540, 0x619, + 0x64d, 0x65c, 0x665, 0x666, 0xc70, 0xc71, 0xc72, 0xc73, 0xc74, 0xc75, 0xc76, 0xc76, 0xc77, + 0x33, 0x80, 0xe2, 0xe4, 0x13c, 0x140, 0x1a2, 0x1a4, 0x1a6, 0x1a7, 0x1aa, 0x1ad, 0x1f1, 0x1f5, + 0x1fc, 0x2a0, 0xc70, 0xc71, 0xc72, 0xc73, 0xc74, 0xc75, 0xc76, 0xc77, 0x80, 0xe2, 0xe4, 0x13c, + 0x140, 0x1a2, 0x1a4, 0x1a6, 0x1a7, 0x1aa, 0x1ad, 0x1f1, 0x1f5, 0x1fc, 0x2a0, 0x4e0, 0x601, + 0x620, 0x638, 0x64f, 0x650, 0x65c, 0x6b0, 0x6b1, 0x9ff, 0x329, 0x540, 0x541, 0x4f0, 0x1309, + 0x130a, 0x130b, 0x14c1, 0x14c2, 0x14c3, 0x14c4, 0x14c5, 0x14c6, 0x14c7, 0x14c8, 0x1a8e, 0x33, + 0x34, 0x39, 0x98, 0x99, 0x9a, 0x105, 0x140, 0x2a1, 0x2a2, 0x2a3, 0x2a4, 0x2a5, 0x2a6, 0x2a7, + 0x2b8, 0x2b9, 0x2ba, 0x2bb, 0x2bc, 0x2bd, 0x2be, 0x2bf, 0x2c2, 0x2c3, 0x2bf, 0x2c2, 0x2c4, + 0x2c5, 0x2c6, 0x2c7, 0x2c8, 0x2c9, 0x2d9, 0x4f0, 0x4f8, 0x540, 0x541, 0x664, 0x9ff, 0xc84, + 0x1a8f, 0x33, 0x34, 0x39, 0x98, 0x99, 0x9a, 0x140, 0x2a1, 0x2a2, 0x2a3, 0x2c2, 0x2c3, 0x2c4, + 0x2c5, 0x2c6, 0x2c7, 0x2c8, 0x2c9, 0x2d6, 0x2d7, 0x2d9, 0x3f9, 0x3fa, 0x3fc, 0x3fd, 0x3fe, + 0x4f0, 0x664, 0x9ff, 0xc84, 0x1a8f, 0x601, 0x630, 0x631, 0x632, 0x651, 0x658, 0x659, 0x65a, + 0x65b, 0x329, 0x540, 0x1878, 0x34, 0xe2, 0xe4, 0x13c, 0x17d, 0x1a2, 0x1a4, 0x1a6, 0x1a7, 0x1ad, + 0x1c8, 0x1c9, 0x3f8, 0x3f9, 0x3fa, 0x3fc, 0x3fd, 0x3ff, 0x606, 0x60d, 0x610, 0x611, 0x613, + 0x614, 0x618, 0x619, 0x61b, 0x61c, 0x638, 0x639, 0x648, 0x649, 0x64a, 0x64b, 0x64c, 0x690, + 0x2a, 0x2b, 0x2c, 0x180, 0x181, 0x182, 0x183, 0x184, 0x185, 0x190, 0x191, 0x192, 0x193, 0x194, + 0x196, 0x197, 0x19d, 0x1a1, 0x1d7, 0x1d8, 0x1da, 0x1db, 0x1dc, 0x300, 0x301, 0x302, 0x303, + 0x304, 0x305, 0x306, 0x307, 0x308, 0x310, 0x311, 0x360, 0x361, 0x362, 0x363, 0x364, 0x365, + 0x366, 0x367, 0x368, 0x369, 0x36a, 0x36b, 0x36c, 0x36d, 0x36e, 0x36f, 0x370, 0x371, 0x3a0, + 0x3a1, 0x3a2, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, 0x3aa, 0x3ab, 0x3ac, 0x3ad, + 0x3ae, 0x3af, 0x3b0, 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, 0x3ba, + 0x3bb, 0x3bc, 0x3bd, 0x3be, 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4, 0x3c5, 0x3c8, 0x3c9, 0x3ca, + 0x3cb, 0x3cc, 0x3cd, 0x3e0, 0x3e1, 0x3f0, 0x3f2, 0x681, 0x682, 0x683, 0x684, 0x685, 0x686, + 0x687, 0x688, 0x689, 0x68a, 0x68b, 0x68c, 0x68d, 0x68e, 0x68f, 0x6c0, 0x6c1, 0x6c2, 0x6c3, + 0x6c4, 0x6c5, 0x6c6, 0x6c7, 0x6c8, 0x6c9, 0x6ca, 0x6cb, 0x6cc, 0x6cd, 0x6ce, 0x6cf, 0x107cc, + 0x107cd, 0x107ce, 0x107cf, 0x107d0, 0x107d1, 0x107d2, 0x107d3, 0x107cc, 0x107cd, 0x107ce, + 0x107cf, 0x107d0, 0x107d1, 0x107d2, 0x107d3, 0x2a, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, + 0x47, 0xcd, 0x11e, 0x1c9, 0x2a, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x119, 0x11e, 0x19d, + 0x1c9, 0x2a, 0x33, 0x88, 0x89, 0x8a, 0x118, 0x116, 0x119, 0x11a, 0x11b, 0x11e, 0x1db, 0x1dc, + 0x11, 0x12, 0x13, +]; + +// TODO: Look out for 0x13c (used to check for AES instruction on Intel Atom and ..)? +// TODO: 0x35 gives THREAD_COUNT will some programs stop working if we deny this MSR? + +// TODO: It is perfectly possible to convert the following test into compile time checks. +// We take care of that later. +#[cfg(test)] +mod tests { + use super::super::msr_based_features::INTEL_MSR_FEATURE_DEFINITIONS; + use super::super::{FORBIDDEN_IA32_MSR_RANGES, PERMITTED_IA32_MSRS}; + use super::NON_ARCHITECTURAL_INTEL_MSRS; + #[test] + fn print_unique() { + let mut unique_count = 0; + for msr in NON_ARCHITECTURAL_INTEL_MSRS { + if (!PERMITTED_IA32_MSRS.contains(&msr)) + && (!FORBIDDEN_IA32_MSR_RANGES + .iter() + .any(|r| (r.0..=r.1).contains(&msr))) + && (!INTEL_MSR_FEATURE_DEFINITIONS + .as_slice() + .iter() + .any(|(address, _)| address.0 == msr)) + { + unique_count += 1; + } + } + assert_eq!(unique_count, NON_ARCHITECTURAL_INTEL_MSRS.len()); + } +} diff --git a/arch/src/x86_64/msr_definitions/kvm.rs b/arch/src/x86_64/msr_definitions/kvm.rs new file mode 100644 index 0000000000..5b0fb60df8 --- /dev/null +++ b/arch/src/x86_64/msr_definitions/kvm.rs @@ -0,0 +1,99 @@ +// Copyright © 2025 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! This module lists KVM defined MSRS. It is currently only used when generating CPU profiles +//! (hence feature gated), but may possibly be extended and utilized for better debug logs in +//! the future. +#[cfg(feature = "cpu_profile_generation")] +pub(in crate::x86_64) use forbidden_msrs::PROFILE_UNPERMITTED_MSRS; +pub(in crate::x86_64) use permitted_msrs::PROFILE_PERMITTED_KVM_MSRS; + +use crate::x86_64::CpuidReg; +use crate::x86_64::cpuid_definitions::Parameters; + +#[cfg(feature = "cpu_profile_generation")] +mod forbidden_msrs { + // TODO: Check that the deprecated KVM_CLOCKSOURCE MSRS + // are disabled via CPUID for non-host CPU profiles + const MSR_KVM_WALL_CLOCK: u32 = 0x11; + const MSR_KVM_SYSTEM_TIME: u32 = 0x12; + /// KVM defined MSRS that CPU profiles should not include in their permitted MSR definitions. + /// + /// This list helps us detect new MSRs that the profile generation tool may not be + /// aware of, but is not logically necessary for anything beyond that. + pub(in crate::x86_64) const PROFILE_UNPERMITTED_MSRS: [u32; 2] = + [MSR_KVM_WALL_CLOCK, MSR_KVM_SYSTEM_TIME]; +} + +mod permitted_msrs { + use super::{CpuidReg, Parameters}; + use crate::x86_64::cpuid_definitions::kvm::assert_not_denied_cpuid_feature; + + const MSR_KVM_WALL_CLOCK_NEW: u32 = 0x4b564d00; + const MSR_KVM_SYSTEM_TIME_NEW: u32 = 0x4b564d01; + const _KVM_CLOCKSOURCE2_CHECK: () = assert_not_denied_cpuid_feature::<3>(&Parameters { + leaf: 0x4000_0001, + sub_leaf: (0..=0), + register: CpuidReg::EAX, + }); + + const MSR_KVM_ASYNC_PF_EN: u32 = 0x4b564d02; + const _KVM_ASYNC_PF_CHECK: () = assert_not_denied_cpuid_feature::<4>(&Parameters { + leaf: 0x4000_0001, + sub_leaf: (0..=0), + register: CpuidReg::EAX, + }); + + const MSR_KVM_STEAL_TIME: u32 = 0x4b564d03; + const _KVM_STEAL_TIME_CHECK: () = assert_not_denied_cpuid_feature::<5>(&Parameters { + leaf: 0x4000_0001, + sub_leaf: (0..=0), + register: CpuidReg::EAX, + }); + + const MSR_KVM_EOI_EN: u32 = 0x4b564d04; + const _KVM_EOI_EN_CHECK: () = assert_not_denied_cpuid_feature::<6>(&Parameters { + leaf: 0x4000_0001, + sub_leaf: (0..=0), + register: CpuidReg::EAX, + }); + + const MSR_KVM_POLL_CONTROL: u32 = 0x4b564d05; + const _KVM_POLL_CONTROL_CHECK: () = assert_not_denied_cpuid_feature::<12>(&Parameters { + leaf: 0x4000_0001, + sub_leaf: (0..=0), + register: CpuidReg::EAX, + }); + + const MSR_KVM_ASYNC_PF_INT: u32 = 0x4b564d06; + const MSR_KVM_ASYNC_PF_ACK: u32 = 0x4b564d07; + const _KVM_ASYNC_PF_INT_ACK_CHECK: () = assert_not_denied_cpuid_feature::<14>(&Parameters { + leaf: 0x4000_0001, + sub_leaf: (0..=0), + register: CpuidReg::EAX, + }); + + const MSR_KVM_MIGRATION_CONTROL: u32 = 0x4b564d08; + const _KVM_MIGRATION_CONTROL_CHECK: () = assert_not_denied_cpuid_feature::<17>(&Parameters { + leaf: 0x4000_0001, + sub_leaf: (0..=0), + register: CpuidReg::EAX, + }); + + /// KVM defined MSRS that CPU profiles may inclide in their permitted MSR definitions. + /// + /// This list is (currently) only utilized when generating CPU profiles. + pub(in crate::x86_64) const PROFILE_PERMITTED_KVM_MSRS: [u32; 9] = [ + MSR_KVM_WALL_CLOCK_NEW, + MSR_KVM_SYSTEM_TIME_NEW, + MSR_KVM_ASYNC_PF_EN, + MSR_KVM_STEAL_TIME, + MSR_KVM_EOI_EN, + MSR_KVM_POLL_CONTROL, + MSR_KVM_ASYNC_PF_INT, + MSR_KVM_ASYNC_PF_ACK, + MSR_KVM_MIGRATION_CONTROL, + ]; +} diff --git a/arch/src/x86_64/msr_definitions/mod.rs b/arch/src/x86_64/msr_definitions/mod.rs new file mode 100644 index 0000000000..49661b7b15 --- /dev/null +++ b/arch/src/x86_64/msr_definitions/mod.rs @@ -0,0 +1,101 @@ +// Copyright © 2025 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// + +use serde::{Deserialize, Serialize}; +pub mod intel; +#[cfg(all(feature = "kvm", any(test, feature = "cpu_profile_generation")))] +pub mod kvm; + +pub mod hyperv; + +use crate::{deserialize_u32_hex, serialize_u32_hex}; +/// The register address of an MSR +#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct RegisterAddress( + #[serde( + serialize_with = "serialize_u32_hex", + deserialize_with = "deserialize_u32_hex" + )] + pub u32, +); + +/// Describes a policy for how the corresponding MSR data should be considered when building +/// a CPU profile. +/// +/// This is the MSR analogue of [cpuid_definitions::ProfilePolicy](crate::x86_64::cpuid_definitions::ProfilePolicy) +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ProfilePolicy { + /// Store the corresponding data when building the CPU profile. + /// + /// When the CPU profile gets utilized the corresponding data will be set into the modified + /// MSR(s) + Inherit, + /// Ignore the corresponding data when building the CPU profile. + /// + /// When the CPU profile gets utilized the corresponding data will then instead get + /// extracted from the host. + /// + /// This variant is typically set for data that has no effect on migration compatibility, + /// but there may be some exceptions such as data which is necessary to run the VM at all, + /// but must coincide with whatever is on the host. + Passthrough, + /// Set the following hardcoded value in the CPU profile. + /// + /// This variant is typically used for features/values that don't work well with live migration (even when using the exact same physical CPU model). + Static(u64), + /// Deny read and write accesses to this MSR. + /// + /// This can only be applied to an MSR in its entirety and not to individual bit ranges + Deny, +} + +/// A description of a range of bits in an MSR. +/// +/// This is the MSR analogue of [cpuid_definitions::ValueDefinition](crate::x86_64::cpuid_definitions::ValueDefinition) +#[derive(Clone, Copy, Debug)] +pub struct ValueDefinition { + /// A short name for the value. + pub short: &'static str, + /// A description of the value. + pub description: &'static str, + /// The range of bits in the MSR corresponding to this feature or value. + /// + /// This is not a `RangeInclusive` because that type does unfortunately not implement `Copy`. + pub bits_range: (u8, u8), + /// The policy corresponding to this value when building CPU profiles. + pub policy: ProfilePolicy, +} + +/// Describes values within an MSR. +/// +/// NOTE: The only way to interact with this value (beyond this crate) is via the const [`Self::as_slice()`](Self::as_slice) method. +/// +/// This is the MSR analogue of [cpuid_definitions::ValueDefinitions](crate::x86_64::cpuid_definitions::ValueDefinitions) +#[derive(Clone, Copy, Debug)] +pub struct ValueDefinitions(&'static [ValueDefinition]); +impl ValueDefinitions { + /// Constructor permitting at most 64 entries. + const fn new(msr_descriptions: &'static [ValueDefinition]) -> Self { + // Note that this function is only called within this module, at compile time, hence it is fine to have some + // additional sanity checks such as the following assert. + assert!(msr_descriptions.len() <= 64); + Self(msr_descriptions) + } + /// Converts this into a slice representation. This is the only way to read values of this type. + pub const fn as_slice(&self) -> &'static [ValueDefinition] { + self.0 + } +} + +/// Describes multiple MSRs. +/// +/// Each wrapped [`ValueDefinitions`] corresponds to the given [`RegisterAddress`] in the same tuple. +pub struct MsrDefinitions([(RegisterAddress, ValueDefinitions); NUM]); + +impl MsrDefinitions { + pub const fn as_slice(&self) -> &[(RegisterAddress, ValueDefinitions); NUM] { + &self.0 + } +} diff --git a/arch/src/x86_64/msr_filter.rs b/arch/src/x86_64/msr_filter.rs new file mode 100644 index 0000000000..6d3c78aa1c --- /dev/null +++ b/arch/src/x86_64/msr_filter.rs @@ -0,0 +1,361 @@ +// Copyright © 2025 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::cell::Cell; +use std::fmt::Write; + +use hypervisor::MsrFilterRange; + +use super::Error; + +/// The maximum number of MSR filter ranges an MSR filter may consist of. +const MAX_FILTERS: usize = { + #[cfg(feature = "kvm")] + { + hypervisor::kvm::KVM_MSR_FILTER_MAX_RANGES + } + #[cfg(not(feature = "kvm"))] + { + // TODO: Change this when adding support for CPU profiles with MSHV + 1 + } +}; + +/// THE maximum number of bytes the bitmap arena used for the filter may occupy. +/// This is to ensure that we do not allocate too much memory for the bitmaps in +/// the filter ranges. +const MAX_BITMAP_SIZE: usize = MAX_FILTERS * 1024 * 1024; + +/// Apply a filter which denies guests any kind of access to the MSRs in `denied_msrs`. +/// +/// # Assumptions +/// +/// This function may explicitly mark certain MSRs different from those in `denied_msrs` as +/// both READ + Write permitted. We assume that the hypervisor will permit this filter being set +/// regardless and rather injects an exception if guests attempt to read/modify these MSRs in anyway +/// that is incompatible with the hardware and/or hypervisor. +/// +/// # Errors +/// +/// This errors if any of the following conditions hold: +/// +/// 1. Too much memory is required to construct the MSR filter that covers all of the denied MSRs. +/// 2. The VM/Hypervisor fails to apply the MSR filter. +pub fn filter_denied_msrs( + mut denied_msrs: Vec, + vm: &dyn hypervisor::Vm, +) -> Result<(), crate::Error> { + if denied_msrs.is_empty() { + return Ok(()); + } + denied_msrs.sort_unstable(); + + for msr in &denied_msrs { + log::debug!("MSR:={msr:#x} is set to be denied"); + } + + let mut bitmap_arena = Vec::new(); + let (filter, num_filter_ranges) = denied_to_filter(&denied_msrs, &mut bitmap_arena)?; + + if let Err(e) = vm.msr_filter(&filter[..num_filter_ranges], false) { + // Log more details at the debug level. Note that this error is likely to be reproducible and happens close to startup, hence + // it should be relatively easy to set the necessary log level if/when debugging becomes desirable. + for filter_range in &filter[..num_filter_ranges] { + // We want to encode the bitmap as a string of the form "[, ,... ]" + let mut bitmap_hex_encoded = String::with_capacity((4 * filter_range.bitmap.len()) + 2); + let _ = write!(&mut bitmap_hex_encoded, "["); + for b in filter_range.bitmap.iter() { + let _ = write!(&mut bitmap_hex_encoded, "{b:#x},"); + } + // Remove the final "," from the string + bitmap_hex_encoded.pop(); + let _ = write!(&mut bitmap_hex_encoded, "]"); + log::debug!( + "Failed to set MSR filter containing filter range: base:={:#x}, nmsrs:={:#x}, bitmap:={}", + filter_range.base, + filter_range.nmsrs, + bitmap_hex_encoded + ); + } + Err(Into::into(Error::MsrFilter(e))) + } else { + Ok(()) + } +} + +/// Essentially partitions `denied_sorted` into up to [`MAX_FILTERS`] ranges of +/// indices. +/// +/// These ranges may then be used to place the MSRs into distinct `[MsrFilterRanges](MsrFilterRange)`. +/// In other words; If (a,b) is an entry in the output of this function, then all MSRs in +/// `denied_sorted[a..=b]` are intended to be placed in the same filter range. +/// +/// This partition minimizes the amount of memory necessary to construct the bitmaps for each +/// MSR filter range, that collectively cover all MSRs in `denied_sorted`, under the constraint +/// that none of the MSR filter ranges can intersect the x2APIC-related MSR range (0x801..=0x8ff). +/// +/// ## Performance +/// +/// This function has complexity` O(MAX_FILTERS * denied_sorted.len())` and does not allocate. +fn denied_to_range_indices<'a>( + denied_sorted: &[u32], + r_buff: &'a mut [(usize, usize); MAX_FILTERS], +) -> &'a [(usize, usize)] { + let mut d_prevs = [u32::MAX; MAX_FILTERS]; + let mut r_cnt = 0; + let mut min_dprev = u32::MAX; + let mut min_pos = 0_usize; + + let compute_dprev = |p: u32, n: u32| { + // Make dprev impractically large if it overlaps the x2apic MSR range + if (p <= 0x8ff) && (n > 0x800) { + u32::MAX + } else { + n - p + } + }; + + // Called as soon as we discover a full contiguous range of MSRs to be denied + // `r_s` is the index of the first MSR in this range and `r_e` the last. + let mut eval_deny_range = |r_s: usize, r_e: usize| { + const LAST_IDX: usize = MAX_FILTERS - 1; + let is_first = r_cnt == 0; + + let d_prev = if is_first { + u32::MAX + } else { + let l_prev_idx = r_buff[r_cnt - 1].1; + let l_prev = denied_sorted[l_prev_idx]; + compute_dprev(l_prev, denied_sorted[r_s]) + }; + + if r_cnt < MAX_FILTERS { + d_prevs[r_cnt] = d_prev; + r_buff[r_cnt] = (r_s, r_e); + if d_prev < min_dprev { + min_dprev = d_prev; + min_pos = r_cnt; + } + r_cnt += 1; + } else { + // Need to join ranges to find space + // The idea is to merge the range groups closest to each other + if d_prev <= min_dprev { + // Make the final range group cover this range + r_buff[LAST_IDX].1 = r_e; + } else { + // Merge some previously gathered range groups to make space + r_buff[min_pos - 1].1 = r_buff[min_pos].1; + // shift every thing after min_pos left + { + shift_left(&mut r_buff[min_pos..]); + shift_left(&mut d_prevs[min_pos..]); + } + // Now we have space for the new entry + r_buff[LAST_IDX] = (r_s, r_e); + d_prevs[LAST_IDX] = d_prev; + // Recompute minimum meta data + min_dprev = *d_prevs.iter().min().unwrap(); + min_pos = d_prevs.iter().position(|d| *d == min_dprev).unwrap(); + } + } + }; + // Produce all range groups + let mut offset = 0_usize; + let mut deny_slice = denied_sorted; + while let Some(deny_slice_skip1) = deny_slice.get(1..) { + let Some(pos) = deny_slice_skip1 + .iter() + .zip(deny_slice) + .position(|(n, p)| (n - p) > 1) + else { + break; + }; + let r_s = offset; + let r_e = offset + pos; + eval_deny_range(r_s, r_e); + offset = r_e + 1; + deny_slice = &denied_sorted[offset..]; + } + // Since there is no gap beyond the last element, we have one final deny range to + // evaluate + eval_deny_range(offset, denied_sorted.len() - 1); + &r_buff[..r_cnt] +} + +/// Construct `range_indices.len() (<= MAX_FILTERS)` [`MsrFilterRanges`](MsrFilterRange) +/// to deny all MSRs in `denied_sorted`. +/// +/// For each pair `(r_s, r_e)` in `range_indices` there will be a corresponding +/// filter range denying the MSRs in [`denied_sorted[r_s..=r_e]`]. +/// +/// # Errors +/// +/// This function can only error if more than [`MAX_BITMAP_SIZE`] bytes are required +/// to construct the filters. +/// +/// # Performance +/// +/// This function allocates once (but a possibly large allocation) and has otherwise +/// computational complexity `O(MAX_FILTERS * denied_sorted.len())`. +fn range_indices_to_filter<'a>( + denied_sorted: &[u32], + range_indices: &[(usize, usize)], + bitmap_arena: &'a mut Vec, +) -> Result<[MsrFilterRange<'a>; MAX_FILTERS], Error> { + let mut out = [MsrFilterRange::default().with_read_write_flags(); MAX_FILTERS]; + let bytes_to_allocate: usize = range_indices + .iter() + .copied() + .map(|(s, e)| ((denied_sorted[e] - denied_sorted[s]) + 1).div_ceil(8)) + .map(|v| v as usize) + .sum(); + + if bytes_to_allocate > MAX_BITMAP_SIZE { + return Err(Error::MsrFilterTooLarge); + } + + bitmap_arena.extend(std::iter::repeat_n(u8::MAX, bytes_to_allocate)); + + let mut arena_slice = &mut bitmap_arena[..]; + for (idx, (r_s, r_e)) in range_indices.iter().enumerate() { + let base = denied_sorted[*r_s]; + let nmsrs = (denied_sorted[*r_e] - denied_sorted[*r_s]) + 1; + let (bm, rest) = arena_slice.split_at_mut(nmsrs.div_ceil(8) as usize); + arena_slice = rest; + for msr in &denied_sorted[*r_s..=*r_e] { + let d_base = *msr - base; + let byte_idx = (d_base) / 8; + let bit = 1 << (d_base % 8); + bm[byte_idx as usize] ^= bit; + } + // Set the fields in the range filter + { + let filter_range = &mut out[idx]; + filter_range.base = base; + filter_range.nmsrs = nmsrs; + filter_range.bitmap = bm; + } + } + + Ok(out) +} + +/// Prepare up to [`MAX_FILTERS`] [`MsrFilterRanges`](`MsrFilterRange`) +/// that collectively deny each of the MSRs specified in `denied_sorted`. +/// +/// The second component returned from this function is the number of +/// valid entries in the returned array. +/// +/// # Errors +/// +/// This function can only error if more than [`MAX_BITMAP_SIZE`] bytes are required +/// to construct the filters. +fn denied_to_filter<'a>( + denied_sorted: &[u32], + bitmap_arena: &'a mut Vec, +) -> Result<([MsrFilterRange<'a>; MAX_FILTERS], usize), Error> { + let mut range_indices_buffer = [(0, 0); MAX_FILTERS]; + let range_indices = denied_to_range_indices(denied_sorted, &mut range_indices_buffer); + + range_indices_to_filter(denied_sorted, range_indices, bitmap_arena) + .map(|filter| (filter, range_indices.len())) +} + +/// Convenience function that moves all elements apart from the first and last left by one. +/// +/// The slice's first element will be removed from the slice, while the modified +/// slice's last element will be equal to the second last (prior to calling this method). +fn shift_left(slice: &mut [T]) { + for w in Cell::from_mut(slice).as_slice_of_cells().windows(2) { + Cell::swap(&w[0], &w[1]); + } +} + +#[cfg(test)] +mod tests { + use hypervisor::MsrFilterRange; + use proptest::prelude::*; + + use super::{MAX_BITMAP_SIZE, MAX_FILTERS, denied_to_filter}; + + /// transforms entries out of the x2apic MSR range and sorts + dedups the vector + fn prepare(bases: Vec) -> Vec { + // Remove bases in the x2apic MSR range + let mut v: Vec = bases + .into_iter() + .map(|b| { + if (0x800..=0x8ff).contains(&b) { + b % 0x800 + } else { + b + } + }) + .collect(); + v.sort_unstable(); + v.dedup(); + v + } + + fn filter_to_msrs(filter: &[MsrFilterRange<'_>]) -> Vec { + let mut out = Vec::new(); + for filter_range in filter { + let base = filter_range.base; + let mut num_msrs: u32 = 0; + for byte in filter_range.bitmap { + let mut inverse = !(*byte); + while inverse != 0 { + let idx = inverse.trailing_zeros(); + if num_msrs + idx > filter_range.nmsrs { + break; + } + out.push(base + num_msrs + idx); + let lsb = inverse & inverse.wrapping_neg(); + inverse ^= lsb; + } + num_msrs += 8; + } + } + out + } + + proptest! { + #[test] + fn denied_to_filer_works_short(prepared_msrs in (prop::collection::vec(0..u32::MAX, 1..MAX_FILTERS)).prop_map(prepare)) { + let mut bitmap_arena = Vec::new(); + let Ok((filter, num_filter_ranges)) = denied_to_filter(&prepared_msrs, &mut bitmap_arena) else { + return Ok(()); + }; + let mut recomputed_msrs = filter_to_msrs(&filter[..num_filter_ranges]); + recomputed_msrs.sort_unstable(); + prop_assert_eq!(prepared_msrs, recomputed_msrs); + } + } + + proptest! { + #[test] + fn denied_to_filer_works(prepared_msrs in (prop::collection::vec(0..u32::MAX, 17..70)).prop_map(prepare)) { + let mut bitmap_arena = Vec::new(); + let Ok((filter, num_filter_ranges)) = denied_to_filter(&prepared_msrs, &mut bitmap_arena) else { + return Ok(()); + }; + let mut recomputed_msrs = filter_to_msrs(&filter[..num_filter_ranges]); + recomputed_msrs.sort_unstable(); + prop_assert_eq!(prepared_msrs, recomputed_msrs); + } + } + + // Simple test that doesn't take too long to execute. We can + // include a more thorough test later if desired. + #[test] + fn catches_attempt_to_allocate_too_much_memory() { + let mut bitmap_arena = Vec::new(); + let denied_msrs: Vec = (0..MAX_FILTERS * 8 * 2) + .map(|i| i * MAX_BITMAP_SIZE) + .map(|v| u32::try_from(v).unwrap()) + .collect(); + let _ = denied_to_filter(&denied_msrs, &mut bitmap_arena).unwrap_err(); + } +} diff --git a/arch/src/x86_64/regs.rs b/arch/src/x86_64/regs.rs index c93f39520b..baaedf57ed 100644 --- a/arch/src/x86_64/regs.rs +++ b/arch/src/x86_64/regs.rs @@ -10,7 +10,7 @@ use std::{mem, result}; use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt}; use hypervisor::arch::x86::regs::CR0_PE; -use hypervisor::arch::x86::{FpuState, SpecialRegisters}; +use hypervisor::arch::x86::{FpuState, MsrEntry, SpecialRegisters}; use thiserror::Error; use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError}; @@ -33,6 +33,8 @@ pub enum Error { /// Setting up MSRs failed. #[error("Setting up MSRs failed")] SetModelSpecificRegisters(#[source] hypervisor::HypervisorCpuError), + #[error("Setting up MSRs failed: Not all MSRs could be set. See logs for more info.")] + SetModelSpecificRegistersPartial, /// Failed to set SREGs for this CPU. #[error("Failed to set SREGs for this CPU")] SetStatusRegisters(#[source] hypervisor::HypervisorCpuError), @@ -81,11 +83,35 @@ pub fn setup_fpu(vcpu: &dyn hypervisor::Vcpu) -> Result<()> { /// # Arguments /// /// * `vcpu` - Structure for the VCPU that holds the VCPU's fd. -pub fn setup_msrs(vcpu: &dyn hypervisor::Vcpu) -> Result<()> { - vcpu.set_msrs(vcpu.boot_msr_entries()) - .map_err(Error::SetModelSpecificRegisters)?; +/// * `feature_msr_updates` - A (possibly empty) slice of MSR-based features +/// that should be set as as part of the setup. If the slice is empty then +/// only boot msr entries are set, otherwise the given slice will also be +/// included in the setup. +pub fn setup_msrs(vcpu: &dyn hypervisor::Vcpu, feature_msr_updates: &[MsrEntry]) -> Result<()> { + let boot_entries = vcpu.boot_msr_entries(); + let mut entries_for_update = Vec::new(); + let setup_entries: &mut &[MsrEntry] = &mut (&boot_entries[..]); - Ok(()) + if !feature_msr_updates.is_empty() { + entries_for_update.extend_from_slice(feature_msr_updates); + entries_for_update.extend_from_slice(boot_entries); + *setup_entries = &entries_for_update[..]; + } + let num_msrs_written = vcpu + .set_msrs(setup_entries) + .map_err(Error::SetModelSpecificRegisters)?; + if num_msrs_written < setup_entries.len() { + for msr in &setup_entries[num_msrs_written..] { + log::error!( + "Could not set MSR with register address:={:#x} and value:={:#x}", + msr.index, + msr.data + ); + } + Err(Into::into(Error::SetModelSpecificRegistersPartial)) + } else { + Ok(()) + } } /// Configure base registers for a given CPU. diff --git a/hypervisor/src/hypervisor.rs b/hypervisor/src/hypervisor.rs index b610070745..a330b5b9eb 100644 --- a/hypervisor/src/hypervisor.rs +++ b/hypervisor/src/hypervisor.rs @@ -62,6 +62,10 @@ pub enum HypervisorError { #[error("Failed to get the list of supported MSRs")] GetMsrList(#[source] anyhow::Error), /// + /// Failed to get MSRs from the hypervisor. + #[error("Failed to get MSRs")] + GetMsr(#[source] anyhow::Error), + /// /// API version is not compatible /// #[error("Incompatible API version")] @@ -125,9 +129,18 @@ pub trait Hypervisor: Send + Sync { /// Get the supported CpuID /// fn get_supported_cpuid(&self) -> Result>; - /// Get the supported MSRs. #[cfg(target_arch = "x86_64")] - fn get_supported_msrs(&self) -> Result>; + /// + /// Get the MSR-based features supported by the hardware and hypervisor + /// + fn get_msr_based_features(&self) -> Result>; + + /// + /// Get the MSR indices supported by the hardware and hypervisor + /// + #[cfg(target_arch = "x86_64")] + fn get_msr_index_list(&self) -> Result>; + /// /// Check particular extensions if any /// diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 7afafa7b90..b50c1af3f4 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -25,6 +25,7 @@ use std::sync::{Arc, RwLock}; use anyhow::anyhow; use kvm_ioctls::{NoDatamatch, VcpuFd, VmFd}; +use log::trace; #[cfg(target_arch = "x86_64")] use log::warn; use vmm_sys_util::eventfd::EventFd; @@ -64,12 +65,12 @@ use x86_64::check_required_kvm_extensions; #[cfg(target_arch = "x86_64")] pub use x86_64::{CpuId, ExtendedControlRegisters, MsrEntries, VcpuKvmState}; -#[cfg(target_arch = "x86_64")] -use crate::ClockData; #[cfg(target_arch = "x86_64")] use crate::arch::x86::{ CpuIdEntry, FpuState, LapicState, MsrEntry, NUM_IOAPIC_PINS, SpecialRegisters, XsaveState, }; +#[cfg(target_arch = "x86_64")] +use crate::{ClockData, MsrFilterRange}; use crate::{CpuState, IoEventAddress, IrqRoutingEntry, MpState, StandardRegisters}; // aarch64 dependencies #[cfg(target_arch = "aarch64")] @@ -136,6 +137,8 @@ const TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT: u64 = 0x10004; const TDG_VP_VMCALL_SUCCESS: u64 = 0; #[cfg(feature = "tdx")] const TDG_VP_VMCALL_INVALID_OPERAND: u64 = 0x8000000000000000; +/// Maximum number of MSR ranges that KVM can filter +pub const KVM_MSR_FILTER_MAX_RANGES: usize = 16; #[cfg(feature = "tdx")] ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong); @@ -505,6 +508,69 @@ impl KvmVm { /// let vm = hypervisor.create_vm(HypervisorVmConfig::default()).expect("new VM fd creation failed"); /// ``` impl vm::Vm for KvmVm { + #[cfg(target_arch = "x86_64")] + fn msr_filter<'a>(&self, filter: &[MsrFilterRange<'a>], default_deny: bool) -> vm::Result<()> { + // Found here https://github.com/torvalds/linux/blob/master/include/uapi/linux/kvm.h#L929C9-L929C31 + const KVM_CAP_MSR_FILTER: u64 = 189; + // Can be computed from https://github.com/torvalds/linux/blob/master/include/uapi/linux/kvm.h#L1458 + const KVM_X86_SET_MSR_FILTER: u64 = 0x4188aec6; + + let cap_result = self.fd.check_extension_raw(KVM_CAP_MSR_FILTER); + if cap_result <= 0 { + return Err(vm::HypervisorVmError::MissingMsrFilterCapability { + error_code: cap_result, + }); + } + // Workaround until https://github.com/rust-vmm/kvm/pull/359 is merged + #[repr(C)] + #[derive(Clone, Copy, Default)] + struct KvmMsrFilterRange { + flags: u32, + nmrs: u32, + base: u32, + bitmap: *const u8, + } + + #[repr(C)] + struct KvmMsrFilter { + flags: u32, + ranges: [KvmMsrFilterRange; KVM_MSR_FILTER_MAX_RANGES], + } + + let mut kvm_filter = KvmMsrFilter { + flags: u32::from(default_deny), + ranges: [Default::default(); KVM_MSR_FILTER_MAX_RANGES], + }; + + let num_ranges = kvm_filter.ranges.len(); + if num_ranges > KVM_MSR_FILTER_MAX_RANGES { + return Err(vm::HypervisorVmError::TooManyMsrFilterRanges { + num_ranges, + num_permitted_ranges: KVM_MSR_FILTER_MAX_RANGES, + }); + } + + for (range, kvm_range) in filter.iter().zip(kvm_filter.ranges.iter_mut()) { + kvm_range.flags = range.flags; + kvm_range.nmrs = range.nmsrs; + kvm_range.base = range.base; + kvm_range.bitmap = range.bitmap.as_ptr(); + } + // SAFETY: SYSCALL with valid parameters. All raw pointers are derived from references that are valid for the duration of this entire method call. + let result = unsafe { + libc::ioctl( + self.fd.as_raw_fd(), + KVM_X86_SET_MSR_FILTER, + (&raw const kvm_filter).cast::(), + ) + }; + if result == 0 { + Ok(()) + } else { + Err(vm::HypervisorVmError::MsrFilter { error_code: result }) + } + } + #[cfg(target_arch = "x86_64")] /// /// Sets the address of the one-page region in the VM's address space. @@ -1265,20 +1331,47 @@ impl hypervisor::Hypervisor for KvmHypervisor { } #[cfg(target_arch = "x86_64")] - fn get_supported_msrs(&self) -> hypervisor::Result> { - let msr_list = self.get_msr_list()?; - let num_msrs = msr_list.as_fam_struct_ref().nmsrs as usize; - let mut msrs: Vec = vec![ - MsrEntry { + fn get_msr_based_features(&self) -> hypervisor::Result> { + let list = self + .kvm + .get_msr_feature_index_list() + .map_err(|e| hypervisor::HypervisorError::GetMsrList(e.into()))?; + let list_len = list.as_fam_struct_ref().nmsrs; + trace!("number of MSR-based feature register addresses:={list_len}"); + let kvm_msrs: Vec = list + .as_slice() + .iter() + .copied() + .map(|index| kvm_msr_entry { + index, ..Default::default() - }; - num_msrs - ]; - let indices = msr_list.as_slice(); - for (pos, index) in indices.iter().enumerate() { - msrs[pos].index = *index; - } - Ok(msrs) + }) + .collect(); + let mut kvm_msrs = MsrEntries::from_entries(&kvm_msrs).unwrap(); + let num_writes = self + .kvm + .get_msrs(&mut kvm_msrs) + .map_err(|e| hypervisor::HypervisorError::GetMsr(e.into()))?; + trace!("number of MSR-based feature MSRs written to by KVM:={num_writes}"); + Ok(kvm_msrs + .as_slice() + .iter() + .copied() + .map(MsrEntry::from) + .collect()) + } + + #[cfg(target_arch = "x86_64")] + fn get_msr_index_list(&self) -> hypervisor::Result> { + let list = self.get_msr_list()?; + let num_msrs = list.as_fam_struct_ref().nmsrs; + let actual_num_msrs = list.as_slice().len(); + assert_eq!( + actual_num_msrs, num_msrs as usize, + "BUG: the length of the MSR Index LIST FAM wrapper does not coincide with + the nmrs field value " + ); + Ok(list.as_slice().to_vec()) } #[cfg(target_arch = "aarch64")] @@ -3076,4 +3169,17 @@ mod unit_tests { vcpu0.set_regs(&core_regs).unwrap(); assert_eq!(vcpu0.get_regs().unwrap(), core_regs); } + + #[cfg(target_arch = "x86_64")] + mod test_x86_64 { + #[test] + fn deny_msrs_to_ranges_works_happy_path() { + todo!() + } + + #[test] + fn deny_msrs_to_ranges_works_error_path() { + todo!() + } + } } diff --git a/hypervisor/src/kvm/x86_64/mod.rs b/hypervisor/src/kvm/x86_64/mod.rs index e338346c3f..710101c43e 100644 --- a/hypervisor/src/kvm/x86_64/mod.rs +++ b/hypervisor/src/kvm/x86_64/mod.rs @@ -63,6 +63,7 @@ pub fn check_required_kvm_extensions(kvm: &Kvm) -> KvmResult<()> { check_extension!(Cap::VcpuEvents); check_extension!(Cap::Xcrs); check_extension!(Cap::Xsave); + check_extension!(Cap::GetMsrFeatures); Ok(()) } diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 2e52c09ac5..51b8d6cb5e 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -189,6 +189,29 @@ pub enum VcpuInit { Mshv(mshv_bindings::MshvVcpuInit), } +#[cfg(target_arch = "x86_64")] +/// Parameters for filtering read and/or write accesses to a range of MSRs. +#[derive(Debug, Clone, Copy, Default)] +pub struct MsrFilterRange<'a> { + /// The type of operation(s) to filter: `1 << 0`, `1 << 1`, `(1 << 0) | (1 << 1)` refers to read, write, read and write respectively. + // TODO: Consider using an enum here + pub flags: u32, + /// The number of MSRs in this filter range. + pub nmsrs: u32, + /// The first MSR index the bitmap starts at. + pub base: u32, + /// A '1' bit allows the operations in `flags`, while `0` denies them. + pub bitmap: &'a [u8], +} + +impl<'a> MsrFilterRange<'a> { + /// Modify the `flags` so that the ops in the bitmap refer to both reads and writes. + pub fn with_read_write_flags(mut self) -> Self { + self.flags = 1 | (1 << 1); + self + } +} + #[derive(Debug, Clone, PartialEq)] pub enum RegList { #[cfg(all(feature = "kvm", any(target_arch = "aarch64", target_arch = "riscv64")))] diff --git a/hypervisor/src/mshv/mod.rs b/hypervisor/src/mshv/mod.rs index a2f501b7b9..ac420d6116 100644 --- a/hypervisor/src/mshv/mod.rs +++ b/hypervisor/src/mshv/mod.rs @@ -382,8 +382,15 @@ impl hypervisor::Hypervisor for MshvHypervisor { Ok(cpuid) } - fn get_supported_msrs(&self) -> hypervisor::Result> { - todo!() + #[cfg(target_arch = "x86_64")] + fn get_msr_index_list(&self) -> hypervisor::Result> { + // TODO: We need to implement this before upstreaming + unimplemented!() + } + + #[cfg(target_arch = "x86_64")] + fn get_msr_based_features(&self) -> hypervisor::Result> { + unimplemented!() } /// Get maximum number of vCPUs @@ -2464,4 +2471,13 @@ impl vm::Vm for MshvVm { } Ok(()) } + + #[cfg(target_arch = "x86_64")] + fn msr_filter<'a>( + &self, + _filter: &[crate::MsrFilterRange<'a>], + _default_deny: bool, + ) -> vm::Result<()> { + todo!() + } } diff --git a/hypervisor/src/vm.rs b/hypervisor/src/vm.rs index 16d01f0c70..fac1b5edb2 100644 --- a/hypervisor/src/vm.rs +++ b/hypervisor/src/vm.rs @@ -20,16 +20,17 @@ use igvm_defs::IGVM_VHS_SNP_ID_BLOCK; use thiserror::Error; use vmm_sys_util::eventfd::EventFd; -#[cfg(target_arch = "x86_64")] -use crate::ClockData; #[cfg(target_arch = "aarch64")] use crate::arch::aarch64::gic::{Vgic, VgicConfig}; #[cfg(target_arch = "riscv64")] use crate::arch::riscv64::aia::{Vaia, VaiaConfig}; #[cfg(feature = "tdx")] use crate::arch::x86::CpuIdEntry; +#[cfg(target_arch = "x86_64")] use crate::arch::x86::MsrEntry; use crate::cpu::Vcpu; +#[cfg(target_arch = "x86_64")] +use crate::{ClockData, MsrFilterRange}; use crate::{IoEventAddress, IrqRoutingEntry}; /// @@ -60,6 +61,22 @@ pub enum HypervisorVmError { #[error("Failed to create Vcpu")] CreateVcpu(#[source] anyhow::Error), /// + /// Could not filter the given MSRs because too many MSR filter ranges were provided. + /// + #[error( + "Too many separate MSR ranges to filter. Number of given ranges:={num_ranges}, but number of permitted ranges:={num_permitted_ranges}" + )] + TooManyMsrFilterRanges { + num_ranges: usize, + num_permitted_ranges: usize, + }, + #[error( + "Could not filter the given MSR ranges: Failed to confirm MSR filtering capability: error_code:={error_code}" + )] + MissingMsrFilterCapability { error_code: i32 }, + #[error("Could not filter the given MSR ranges. Error code:={error_code}")] + MsrFilter { error_code: i32 }, + /// /// Identity map address error /// #[error("Failed to set identity map address")] @@ -322,12 +339,27 @@ pub trait Vm: Send + Sync + Any { fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>; /// Unregister an event that will, when signaled, trigger the `gsi` IRQ. fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>; + #[cfg(target_arch = "x86_64")] + /// Filter the given ranges of MSRs. This can be used to specify certain MSRs + /// that guests may not access. + /// + /// If the `default_deny` flag is set, MSRs that do not match any of the given + /// ranges, will be automatically denied, otherwise they are allowed. + /// + /// # Important + /// + /// This method should be called once before creating any vCPUs and never again. + fn msr_filter<'a>(&self, filter: &[MsrFilterRange<'a>], default_deny: bool) -> Result<()>; /// Creates a new KVM vCPU file descriptor and maps the memory corresponding + /// + /// The `msr_buffer` is used to store MSR state. The entries given here are + /// expected to hold indices/register addresses supported by both the host's + /// hardware and the hypervisor. fn create_vcpu( &self, id: u32, vm_ops: Option>, - #[cfg(target_arch = "x86_64")] msrs: Vec, + #[cfg(target_arch = "x86_64")] msr_buffer: Vec, ) -> Result>; #[cfg(target_arch = "aarch64")] fn create_vgic(&self, config: &VgicConfig) -> Result>>; diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 006ab357b7..ef400335c5 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -142,6 +142,12 @@ pub enum Error { #[error("Error generating common CPUID")] CommonCpuId(#[source] arch::Error), + #[error("Error computing required MSR updates")] + RequiredMsrUpdates(#[source] arch::Error), + + #[error("Error applying MSR filter")] + MsrFilter(#[source] arch::Error), + #[error("Error configuring vCPU")] VcpuConfiguration(#[source] arch::Error), @@ -431,16 +437,17 @@ impl Vcpu { /// * `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) + /// * `msr_buffer`(x86_64 only) - A buffer for supported MSRs. pub fn new( id: u32, apic_id: u32, vm: &dyn hypervisor::Vm, vm_ops: Option>, #[cfg(target_arch = "x86_64")] cpu_vendor: CpuVendor, - #[cfg(target_arch = "x86_64")] msrs: Vec, + #[cfg(target_arch = "x86_64")] msr_buffer: Vec, ) -> Result { let vcpu = vm - .create_vcpu(apic_id, vm_ops, msrs) + .create_vcpu(apic_id, vm_ops, msr_buffer) .map_err(|e| Error::VcpuCreate(e.into()))?; // Initially the cpuid per vCPU is the one supported by this VM. Ok(Vcpu { @@ -466,6 +473,7 @@ impl Vcpu { #[cfg(target_arch = "aarch64")] vm: &dyn hypervisor::Vm, boot_setup: Option<(EntryPoint, &GuestMemoryAtomic)>, #[cfg(target_arch = "x86_64")] cpuid: Vec, + #[cfg(target_arch = "x86_64")] feature_msr_updates: &[MsrEntry], #[cfg(target_arch = "x86_64")] kvm_hyperv: bool, #[cfg(target_arch = "x86_64")] topology: (u16, u16, u16, u16), #[cfg(target_arch = "x86_64")] nested: bool, @@ -486,6 +494,7 @@ impl Vcpu { self.id, boot_setup, cpuid, + feature_msr_updates, kvm_hyperv, self.vendor, topology, @@ -603,7 +612,10 @@ pub struct CpuManager { #[cfg(target_arch = "x86_64")] cpuid: Vec, #[cfg(target_arch = "x86_64")] - msrs: Vec, + /// A buffer for MSRs supported by the hardware and hypervisor + msr_buffer: Vec, + #[cfg(target_arch = "x86_64")] + profile_msr_based_features: Vec, #[cfg_attr(target_arch = "aarch64", allow(dead_code))] vm: Arc, vcpus_kill_signalled: Arc, @@ -849,9 +861,10 @@ impl CpuManager { interrupt_controller: None, #[cfg(target_arch = "x86_64")] cpuid: Vec::new(), - msrs: hypervisor - .get_supported_msrs() - .map_err(|e| Error::VcpuCreate(e.into()))?, + #[cfg(target_arch = "x86_64")] + msr_buffer: Self::construct_msr_buffer(hypervisor.as_ref())?, + #[cfg(target_arch = "x86_64")] + profile_msr_based_features: Vec::new(), vm, vcpus_kill_signalled: Arc::new(AtomicBool::new(false)), vcpus_pause_signalled: Arc::new(AtomicBool::new(false)), @@ -875,6 +888,20 @@ impl CpuManager { }))) } + #[cfg(target_arch = "x86_64")] + fn construct_msr_buffer(hypervisor: &dyn hypervisor::Hypervisor) -> Result> { + let msr_indices = hypervisor + .get_msr_index_list() + .map_err(|e| Error::VcpuCreate(e.into()))?; + Ok(msr_indices + .into_iter() + .map(|index| MsrEntry { + index, + ..Default::default() + }) + .collect()) + } + #[cfg(target_arch = "x86_64")] pub fn populate_cpuid( &mut self, @@ -900,6 +927,44 @@ impl CpuManager { Ok(()) } + #[cfg(target_arch = "x86_64")] + /// Prepares common MSR-based feature value updates that will be set when vCPUs are configured. + /// + /// This is only relevant when (non-host) CPU profiles are present, otherwise it is infallible + /// and we set an empty vector. + pub fn apply_msr_updates(&mut self) -> Result<()> { + let profile_msr_based_features = { + if let Some(arch::x86_64::cpu_profile::RequiredMsrUpdates { + msr_based_features, + denied_msrs, + }) = arch::x86_64::compute_required_msr_updates( + self.hypervisor.as_ref(), + self.config.profile, + self.config.kvm_hyperv, + ) + .map_err(Error::RequiredMsrUpdates)? + { + // Remove denied MSRS from the MSR buffer + self.msr_buffer.retain(|entry| { + !denied_msrs + .contains(&arch::x86_64::msr_definitions::RegisterAddress(entry.index)) + }); + // Create and apply a filter to prevent guests from accessing the denied MSRs + // TODO: Better error! + arch::x86_64::filter_denied_msrs( + denied_msrs.into_iter().map(|reg| reg.0).collect(), + self.vm.as_ref(), + ) + .map_err(Error::MsrFilter)?; + msr_based_features + } else { + Vec::new() + } + }; + self.profile_msr_based_features = profile_msr_based_features; + Ok(()) + } + fn create_vcpu( &mut self, cpu_id: u32, @@ -922,7 +987,7 @@ impl CpuManager { #[cfg(target_arch = "x86_64")] self.hypervisor.get_cpu_vendor(), #[cfg(target_arch = "x86_64")] - self.msrs.clone(), + self.msr_buffer.clone(), )?; if let Some(snapshot) = snapshot { @@ -992,6 +1057,7 @@ impl CpuManager { vcpu.configure( boot_setup, self.cpuid.clone(), + &self.profile_msr_based_features, self.config.kvm_hyperv, topology, self.config.nested, @@ -3126,8 +3192,9 @@ mod unit_tests { let vm = hv .create_vm(HypervisorVmConfig::default()) .expect("new VM fd creation failed"); + // TODO: Use a proper MSR buffer here let vcpu = vm.create_vcpu(0, None, vec![]).unwrap(); - setup_msrs(vcpu.as_ref()).unwrap(); + setup_msrs(vcpu.as_ref(), &[]).unwrap(); // This test will check against the last MSR entry configured (the tenth one). // See create_msr_entries for details. diff --git a/vmm/src/seccomp_filters.rs b/vmm/src/seccomp_filters.rs index 671997797a..3f16812d51 100644 --- a/vmm/src/seccomp_filters.rs +++ b/vmm/src/seccomp_filters.rs @@ -80,6 +80,7 @@ mod kvm { pub const KVM_CHECK_EXTENSION: u64 = 0xae03; pub const KVM_GET_VCPU_MMAP_SIZE: u64 = 0xae04; pub const KVM_CREATE_VCPU: u64 = 0xae41; + pub const KVM_X86_SET_MSR_FILTER: u64 = 0x4188aec6; pub const KVM_CREATE_IRQCHIP: u64 = 0xae60; pub const KVM_RUN: u64 = 0xae80; pub const KVM_SET_MP_STATE: u64 = 0x4004_ae99; @@ -208,6 +209,7 @@ fn create_vmm_ioctl_seccomp_rule_common_kvm() -> Result, Backen Ok(or![ and![Cond::new(1, ArgLen::Dword, Eq, KVM_CHECK_EXTENSION)?], and![Cond::new(1, ArgLen::Dword, Eq, KVM_CREATE_DEVICE,)?], + and![Cond::new(1, ArgLen::Dword, Eq, KVM_X86_SET_MSR_FILTER,)?], and![Cond::new(1, ArgLen::Dword, Eq, KVM_CREATE_IRQCHIP,)?], and![Cond::new(1, ArgLen::Dword, Eq, KVM_CREATE_VCPU)?], and![Cond::new(1, ArgLen::Dword, Eq, KVM_CREATE_VM)?], @@ -373,6 +375,7 @@ fn create_vmm_ioctl_seccomp_rule_kvm() -> Result, BackendError> const KVM_GET_FPU: u64 = 0x81a0_ae8c; const KVM_GET_LAPIC: u64 = 0x8400_ae8e; const KVM_GET_MSR_INDEX_LIST: u64 = 0xc004_ae02; + const KVM_GET_MSR_FEATURE_INDEX_LIST: u64 = 0xc004_ae0a; const KVM_GET_MSRS: u64 = 0xc008_ae88; const KVM_GET_SREGS: u64 = 0x8138_ae83; const KVM_GET_TSC_KHZ: u64 = 0xaea3; @@ -402,6 +405,12 @@ fn create_vmm_ioctl_seccomp_rule_kvm() -> Result, BackendError> and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_FPU)?], and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_LAPIC)?], and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_MSR_INDEX_LIST)?], + and![Cond::new( + 1, + ArgLen::Dword, + Eq, + KVM_GET_MSR_FEATURE_INDEX_LIST + )?], and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_MSRS)?], and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_SREGS)?], and![Cond::new(1, ArgLen::Dword, Eq, KVM_GET_TSC_KHZ)?], diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 49402b7dcc..ed78190083 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -618,16 +618,19 @@ impl Vm { .map_err(Error::CpuManager)?; #[cfg(target_arch = "x86_64")] - cpu_manager - .lock() - .unwrap() - .populate_cpuid( + { + // Populate CPUID and MSR-based features + let mut lock = cpu_manager.lock().unwrap(); + lock.populate_cpuid( hypervisor.as_ref(), #[cfg(feature = "tdx")] tdx_enabled, ) .map_err(Error::CpuManager)?; + lock.apply_msr_updates().map_err(Error::CpuManager)?; + } + // The initial TDX configuration must be done before the vCPUs are // created #[cfg(feature = "tdx")]