diff --git a/arch/src/x86_64/cpu_profile/msr_adjustments.rs b/arch/src/x86_64/cpu_profile/msr_adjustments.rs index ddf66b88b..514435a60 100644 --- a/arch/src/x86_64/cpu_profile/msr_adjustments.rs +++ b/arch/src/x86_64/cpu_profile/msr_adjustments.rs @@ -3,9 +3,14 @@ // SPDX-License-Identifier: Apache-2.0 // +use hypervisor::arch::x86::MsrEntry; +use log::{debug, error}; use serde::{Deserialize, Serialize}; -use crate::x86_64::helpers::{deserialize_u32_hex, serialize_u32_hex}; +use crate::x86_64::Error; +use crate::x86_64::helpers::{ + deserialize_u32_hex, deserialize_u64_hex, serialize_u32_hex, serialize_u64_hex, +}; /// The register address of an MSR #[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] @@ -16,3 +21,71 @@ pub struct RegisterAddress( )] pub u32, ); + +/// Used to adjust the value of a Feature MSR. +/// +/// Instances of this struct typically adjust MSR values according to the +/// following formula: `msr_value = (self.mask & msr_value) | self.replacements`. +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct FeatureMsrAdjustment { + /// Packs values to be placed into the given feature MSR value. + #[serde( + serialize_with = "serialize_u64_hex", + deserialize_with = "deserialize_u64_hex" + )] + pub replacements: u64, + + /// Used to zero out the area `replacements` occupy. This mask is not necessarily !replacements, as replacements + /// may pack values of different types that occupy varying ranges of bits. + /// + /// Bit ranges within a feature MSR value that are **not** supposed to be replaced/overwritten should be set in + /// this mask. + #[serde( + serialize_with = "serialize_u64_hex", + deserialize_with = "deserialize_u64_hex" + )] + pub mask: u64, +} + +impl FeatureMsrAdjustment { + /// Adjusts the given `feature_msrs` according to `adjustments`. + /// + /// An error is returned if there exists an MSR register address in + /// `adjustments` without a matching entry in `feature_msrs`. + pub(super) fn adjust_feature_msrs( + feature_msrs: &[MsrEntry], + adjustments: &[(RegisterAddress, FeatureMsrAdjustment)], + ) -> Result, Error> { + let mut missing_msr = false; + let mut output_feature_msrs = Vec::with_capacity(adjustments.len()); + for (reg_address, adjustment) in adjustments { + let Some(entry) = feature_msrs + .iter() + .find(|entry| entry.index == reg_address.0) + else { + missing_msr = true; + error!( + "Did not find feature based MSR entry for MSR {:#x}", + reg_address.0 + ); + continue; + }; + + let mut entry = *entry; + let data = entry.data; + entry.data = (adjustment.mask & data) | adjustment.replacements; + + debug!( + "Prepared adjusted MSR feature: register address={:#x} value={:#x}, previous value={data:#x}", + entry.index, entry.data + ); + output_feature_msrs.push(entry); + } + + if missing_msr { + Err(Error::CpuProfileMissingMsr) + } else { + Ok(output_feature_msrs) + } + } +} diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index d55e978a6..e49c22831 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -170,6 +170,13 @@ pub enum Error { )] MissingExpectedCpuidEntry(#[source] MissingCpuidEntriesError), + /// Error when trying to apply a CPU profile because a necessary MSR was not found. + /// + /// We encourage functions returning this variant to log all missing MSRs for debugging + /// purposes. + #[error("The selected CPU profile cannot be utilized because a necessary MSR was not found")] + CpuProfileMissingMsr, + // Error writing EBDA address #[error("Error writing EBDA address")] EbdaSetup(#[source] vm_memory::GuestMemoryError),