vmm/cpu_config/x86_64/cpuid/
common.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3#![allow(clippy::restriction)]
4
5use crate::arch::x86_64::generated::msr_index::{
6    MSR_IA32_BNDCFGS, MSR_IA32_CR_PAT, MSR_MTRRdefType, MSR_MTRRfix4K_C0000, MSR_MTRRfix4K_C8000,
7    MSR_MTRRfix4K_D0000, MSR_MTRRfix4K_D8000, MSR_MTRRfix4K_E0000, MSR_MTRRfix4K_E8000,
8    MSR_MTRRfix4K_F0000, MSR_MTRRfix4K_F8000, MSR_MTRRfix16K_80000, MSR_MTRRfix16K_A0000,
9    MSR_MTRRfix64K_00000,
10};
11
12/// Error type for [`get_cpuid`].
13#[derive(Debug, thiserror::Error, displaydoc::Display, PartialEq, Eq)]
14pub enum GetCpuidError {
15    /// Un-supported leaf: {0}
16    UnsupportedLeaf(u32),
17    /// Invalid subleaf: {0}
18    InvalidSubleaf(u32),
19}
20
21/// Extract entry from the cpuid.
22///
23/// # Errors
24///
25/// - When the given `leaf` is more than `max_leaf` supported by CPUID.
26/// - When the CPUID leaf `sub-leaf` is invalid (all its register equal 0).
27pub fn get_cpuid(leaf: u32, subleaf: u32) -> Result<std::arch::x86_64::CpuidResult, GetCpuidError> {
28    let max_leaf =
29        // JUSTIFICATION: There is no safe alternative.
30        // SAFETY: This is safe because the host supports the `cpuid` instruction
31        unsafe { std::arch::x86_64::__get_cpuid_max(leaf & 0x8000_0000).0 };
32    if leaf > max_leaf {
33        return Err(GetCpuidError::UnsupportedLeaf(leaf));
34    }
35
36    let entry = crate::cpu_config::x86_64::cpuid::cpuid_count(leaf, subleaf);
37    if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
38        return Err(GetCpuidError::InvalidSubleaf(subleaf));
39    }
40
41    Ok(entry)
42}
43
44/// Extracts the CPU vendor id from leaf 0x0.
45///
46/// # Errors
47///
48/// When CPUID leaf 0 is not supported.
49pub fn get_vendor_id_from_host() -> Result<[u8; 12], GetCpuidError> {
50    // JUSTIFICATION: There is no safe alternative.
51    // SAFETY: Always safe.
52    get_cpuid(0, 0).map(|vendor_entry| unsafe {
53        // The ordering of the vendor string is ebx,edx,ecx this is not a mistake.
54        std::mem::transmute::<[u32; 3], [u8; 12]>([
55            vendor_entry.ebx,
56            vendor_entry.edx,
57            vendor_entry.ecx,
58        ])
59    })
60}
61
62/// Returns MSRs to be saved based on CPUID features that are enabled.
63pub(crate) fn msrs_to_save_by_cpuid(cpuid: &kvm_bindings::CpuId) -> Vec<u32> {
64    /// Memory Protection Extensions
65    const MPX_BITINDEX: u32 = 14;
66
67    /// Memory Type Range Registers
68    const MTRR_BITINDEX: u32 = 12;
69
70    /// Memory Check Exception
71    const MCE_BITINDEX: u32 = 7;
72
73    /// Scans through the CPUID and determines if a feature bit is set.
74    // TODO: This currently involves a linear search which would be improved
75    //       when we'll refactor the cpuid crate.
76    macro_rules! cpuid_is_feature_set {
77        ($cpuid:ident, $leaf:expr, $index:expr, $reg:tt, $feature_bit:expr) => {{
78            let mut res = false;
79            for entry in $cpuid.as_slice().iter() {
80                if entry.function == $leaf && entry.index == $index {
81                    if entry.$reg & (1 << $feature_bit) != 0 {
82                        res = true;
83                        break;
84                    }
85                }
86            }
87            res
88        }};
89    }
90
91    let mut msrs = Vec::new();
92
93    // Macro used for easy definition of CPUID-MSR dependencies.
94    macro_rules! cpuid_msr_dep {
95        ($leaf:expr, $index:expr, $reg:tt, $feature_bit:expr, $msr:expr) => {
96            if cpuid_is_feature_set!(cpuid, $leaf, $index, $reg, $feature_bit) {
97                msrs.extend($msr)
98            }
99        };
100    }
101
102    // TODO: Add more dependencies.
103    cpuid_msr_dep!(0x7, 0, ebx, MPX_BITINDEX, [MSR_IA32_BNDCFGS]);
104
105    // IA32_MTRR_PHYSBASEn, IA32_MTRR_PHYSMASKn
106    cpuid_msr_dep!(0x1, 0, edx, MTRR_BITINDEX, 0x200..0x210);
107
108    // Other MTRR MSRs
109    cpuid_msr_dep!(
110        0x1,
111        0,
112        edx,
113        MTRR_BITINDEX,
114        [
115            MSR_MTRRfix64K_00000,
116            MSR_MTRRfix16K_80000,
117            MSR_MTRRfix16K_A0000,
118            MSR_MTRRfix4K_C0000,
119            MSR_MTRRfix4K_C8000,
120            MSR_MTRRfix4K_D0000,
121            MSR_MTRRfix4K_D8000,
122            MSR_MTRRfix4K_E0000,
123            MSR_MTRRfix4K_E8000,
124            MSR_MTRRfix4K_F0000,
125            MSR_MTRRfix4K_F8000,
126            MSR_IA32_CR_PAT,
127            MSR_MTRRdefType,
128        ]
129    );
130
131    // MCE MSRs
132    // We are saving 32 MCE banks here as this is the maximum number supported by KVM
133    // and configured by default.
134    // The physical number of the MCE banks depends on the CPU.
135    // The number of emulated MCE banks can be configured via KVM_X86_SETUP_MCE.
136    cpuid_msr_dep!(0x1, 0, edx, MCE_BITINDEX, 0x400..0x480);
137
138    msrs
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144
145    #[test]
146    fn get_cpuid_unsupported_leaf() {
147        let max_leaf =
148            // JUSTIFICATION: There is no safe alternative.
149            // SAFETY: This is safe because the host supports the `cpuid` instruction
150            unsafe { std::arch::x86_64::__get_cpuid_max(0).0 };
151        let max_leaf_plus_one = max_leaf + 1;
152
153        assert_eq!(
154            get_cpuid(max_leaf_plus_one, 0),
155            Err(GetCpuidError::UnsupportedLeaf(max_leaf_plus_one))
156        );
157    }
158}