Skip to content

Commit e5498f8

Browse files
Alexandru-Cezar Sardanalxiord
Alexandru-Cezar Sardan
authored andcommitted
snap/restore: save MSRs based on CPUID
Some additional MSRs need to be saved if certain features are enabled in CPUID. Add the capability to infer which MSRs to save based on the feature bits set in CPUID. The dependencies between CPUID features and MSRs are defined for each CPU vendor. Through this commit, the list of MSRs we need to save becomes architectural_msrs + msrs_based_on_cpuid + msrs_defined_in_template. The dependency list now contains only one entry for the MPX feature bit so it is by no means exhaustive. We'll add more dependencies in following commits. Until then the right coverage should be supplied through templates and the ALLOWED_MSR_RANGES list. Signed-off-by: Alexandru-Cezar Sardan <[email protected]>
1 parent 2a8aa07 commit e5498f8

File tree

7 files changed

+103
-2
lines changed

7 files changed

+103
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cpuid/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ thiserror = "1.0.32"
1313

1414
utils = { path = "../utils"}
1515
arch = { path = "../arch" }
16+
arch_gen = { path = "../arch_gen" }

src/cpuid/src/common.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,25 @@ pub fn is_same_model(cpuid: &CpuId) -> bool {
159159
true
160160
}
161161

162+
/// Scans through the CPUID and determines if a feature bit is set.
163+
// TODO: This currently involves a linear search which would be improved
164+
// when we'll refactor the cpuid crate.
165+
#[macro_export]
166+
macro_rules! cpuid_is_feature_set {
167+
($cpuid:ident, $leaf:expr, $index:expr, $reg:tt, $feature_bit:expr) => {{
168+
let mut res = false;
169+
for entry in $cpuid.as_slice().iter() {
170+
if entry.function == $leaf && entry.index == $index {
171+
if entry.$reg & (1 << $feature_bit) != 0 {
172+
res = true;
173+
break;
174+
}
175+
}
176+
}
177+
res
178+
}};
179+
}
180+
162181
#[cfg(test)]
163182
pub mod tests {
164183
use crate::common::*;

src/cpuid/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub mod bit_helper;
2020

2121
mod template;
2222
pub use crate::template::intel::{c3, t2, t2s};
23+
pub use crate::template::msrs_to_save_by_cpuid;
2324

2425
mod cpu_leaf;
2526

src/cpuid/src/template/intel/mod.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ pub mod t2;
99
/// that are speciffic to an Intel Skylake CPU.
1010
pub mod t2s;
1111

12+
use std::collections::HashSet;
13+
14+
use arch_gen::x86::msr_index::*;
15+
use kvm_bindings::CpuId;
16+
1217
use crate::common::{get_vendor_id_from_host, VENDOR_ID_INTEL};
18+
use crate::cpuid_is_feature_set;
1319
use crate::transformer::Error;
1420

1521
pub fn validate_vendor_id() -> Result<(), Error> {
@@ -20,3 +26,29 @@ pub fn validate_vendor_id() -> Result<(), Error> {
2026

2127
Ok(())
2228
}
29+
30+
/// Returns MSRs to be saved based on the Intel CPUID features that are enabled.
31+
pub(crate) fn msrs_to_save_by_cpuid(cpuid: &CpuId) -> HashSet<u32> {
32+
use crate::cpu_leaf::*;
33+
34+
let mut msrs = HashSet::new();
35+
36+
// Macro used for easy definition of CPUID-MSR dependencies.
37+
macro_rules! cpuid_msr_dep {
38+
($leaf:expr, $index:expr, $reg:tt, $feature_bit:expr, [$($msr:expr),+]) => {
39+
if cpuid_is_feature_set!(cpuid, $leaf, $index, $reg, $feature_bit) {
40+
msrs.extend(&[$($msr),*]);
41+
}
42+
};
43+
}
44+
45+
// TODO: Add more dependencies.
46+
cpuid_msr_dep!(
47+
0x7,
48+
0,
49+
ebx,
50+
leaf_0x7::index0::ebx::MPX_BITINDEX,
51+
[MSR_IA32_BNDCFGS]
52+
);
53+
msrs
54+
}

src/cpuid/src/template/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,22 @@
33

44
// Contains Intel specific templates.
55
pub mod intel;
6+
7+
use std::collections::HashSet;
8+
9+
use kvm_bindings::CpuId;
10+
11+
use crate::common::{get_vendor_id_from_cpuid, VENDOR_ID_INTEL};
12+
use crate::transformer::Error;
13+
14+
/// Returns MSRs to be saved based on CPUID features that are enabled.
15+
pub fn msrs_to_save_by_cpuid(cpuid: &CpuId) -> Result<HashSet<u32>, Error> {
16+
let vendor_id = get_vendor_id_from_cpuid(cpuid).map_err(|_| Error::InvalidVendor)?;
17+
match &vendor_id {
18+
VENDOR_ID_INTEL => Ok(intel::msrs_to_save_by_cpuid(cpuid)),
19+
_ => {
20+
// We don't have MSR-CPUID dependencies set for other vendors yet.
21+
Ok(HashSet::new())
22+
}
23+
}
24+
}

src/vmm/src/vstate/vcpu/x86_64.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::{fmt, result};
1111
use arch::x86_64::interrupts;
1212
use arch::x86_64::msr::SetMSRsError;
1313
use arch::x86_64::regs::{SetupFpuError, SetupRegistersError, SetupSpecialRegistersError};
14-
use cpuid::{c3, filter_cpuid, t2, t2s, VmSpec};
14+
use cpuid::{c3, filter_cpuid, msrs_to_save_by_cpuid, t2, t2s, VmSpec};
1515
use kvm_bindings::{
1616
kvm_debugregs, kvm_lapic_state, kvm_mp_state, kvm_regs, kvm_sregs, kvm_vcpu_events, kvm_xcrs,
1717
kvm_xsave, CpuId, MsrList, Msrs,
@@ -267,6 +267,7 @@ impl KvmVcpu {
267267
})
268268
.map_err(KvmVcpuConfigureError::FilterCpuid)?;
269269

270+
// Update the CPUID based on the template
270271
match vcpu_config.cpu_template {
271272
CpuFeaturesTemplate::T2 => t2::set_cpuid_entries(&mut cpuid, &cpuid_vm_spec)
272273
.map_err(KvmVcpuConfigureError::SetCpuidEntries)?,
@@ -281,9 +282,33 @@ impl KvmVcpu {
281282
.set_cpuid2(&cpuid)
282283
.map_err(KvmVcpuConfigureError::SetCpuid)?;
283284

284-
// Set MSRs
285+
// Initialize some architectural MSRs that will be set for boot.
285286
let mut msr_boot_entries = arch::x86_64::msr::create_boot_msr_entries();
286287

288+
// By this point the Guest CPUID is established. Some CPU features require MSRs
289+
// to configure and interact with those features. If a MSR is writable from
290+
// inside the Guest, or is changed by KVM or Firecracker on behalf of the Guest,
291+
// then we will need to save it every time we take a snapshot, and restore its
292+
// value when we restore the microVM since the Guest may need that value.
293+
// Since CPUID tells us what features are enabled for the Guest, we can infer
294+
// the extra MSRs that we need to save based on a dependency map.
295+
let extra_msrs =
296+
msrs_to_save_by_cpuid(&cpuid).map_err(KvmVcpuConfigureError::FilterCpuid)?;
297+
for msr in extra_msrs {
298+
self.msr_list
299+
.push(msr)
300+
.map_err(KvmVcpuConfigureError::PushMsrEntries)?;
301+
}
302+
303+
// TODO: Some MSRs depend on values of other MSRs. This dependency will need to
304+
// be implemented. For now we define known dependencies statically in the CPU
305+
// templates.
306+
307+
// Depending on which CPU template the user selected, we may need to initialize
308+
// additional MSRs for boot to correctly enable some CPU features. As stated in
309+
// the previous comment, we get from the template a static list of MSRs we need
310+
// to save at snapshot as well.
311+
// C3 and T2 currently don't have extra MSRs to save/set
287312
if vcpu_config.cpu_template == CpuFeaturesTemplate::T2S {
288313
for msr in t2s::msr_entries_to_save() {
289314
self.msr_list
@@ -292,6 +317,9 @@ impl KvmVcpu {
292317
}
293318
t2s::update_msr_entries(&mut msr_boot_entries);
294319
}
320+
// By this point we know that at snapshot, the list of MSRs we need to
321+
// save is `architectural MSRs` + `MSRs inferred through CPUID` + `other
322+
// MSRs defined by the template`
295323

296324
arch::x86_64::msr::set_msrs(&self.fd, &msr_boot_entries)?;
297325
arch::x86_64::regs::setup_regs(&self.fd, kernel_start_addr.raw_value() as u64)?;

0 commit comments

Comments
 (0)