arch: FeatureMsrAdjustment type

We introduce a type analogous to CpuidOutputRegisterAdjustment, but for
feature MSRs.

Signed-off-by: Oliver Anderson <oliver.anderson@cyberus-technology.de>
On-behalf-of: SAP oliver.anderson@sap.com
This commit is contained in:
Oliver Anderson
2026-06-08 10:41:01 +02:00
committed by Rob Bradford
parent 37aa545c2a
commit 26305445b6
2 changed files with 81 additions and 1 deletions

View File

@@ -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<Vec<MsrEntry>, 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)
}
}
}

View File

@@ -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),