vmm/cpu_config/x86_64/
mod.rs

1// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4/// Module for CPUID instruction related content
5pub mod cpuid;
6/// Module for custom CPU templates
7pub mod custom_cpu_template;
8/// Module for static CPU templates
9pub mod static_cpu_templates;
10/// Module with test utils for custom CPU templates
11pub mod test_utils;
12
13use std::collections::BTreeMap;
14
15use kvm_bindings::CpuId;
16
17use self::custom_cpu_template::CpuidRegister;
18use super::templates::CustomCpuTemplate;
19use crate::Vcpu;
20use crate::cpu_config::x86_64::cpuid::{Cpuid, CpuidKey};
21
22/// Errors thrown while configuring templates.
23#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
24pub enum CpuConfigurationError {
25    /// Template changes a CPUID entry not supported by KVM: Leaf: {0:0x}, Subleaf: {1:0x}
26    CpuidFeatureNotSupported(u32, u32),
27    /// Template changes an MSR entry not supported by KVM: Register Address: {0:0x}
28    MsrNotSupported(u32),
29    /// Can create cpuid from raw: {0}
30    CpuidFromKvmCpuid(#[from] crate::cpu_config::x86_64::cpuid::CpuidTryFromKvmCpuid),
31    /// KVM vcpu ioctl failed: {0}
32    VcpuIoctl(#[from] crate::vstate::vcpu::KvmVcpuError),
33}
34
35/// CPU configuration for x86_64 CPUs
36#[derive(Debug, Clone, PartialEq)]
37pub struct CpuConfiguration {
38    /// CPUID configuration
39    pub cpuid: Cpuid,
40    /// Register values as a key pair for model specific registers
41    /// Key: MSR address
42    /// Value: MSR value
43    pub msrs: BTreeMap<u32, u64>,
44}
45
46impl CpuConfiguration {
47    /// Create new CpuConfiguration.
48    pub fn new(
49        supported_cpuid: CpuId,
50        cpu_template: &CustomCpuTemplate,
51        first_vcpu: &Vcpu,
52    ) -> Result<Self, CpuConfigurationError> {
53        let cpuid = cpuid::Cpuid::try_from(supported_cpuid)?;
54        let msrs = first_vcpu
55            .kvm_vcpu
56            .get_msrs(cpu_template.msr_index_iter())?;
57        Ok(CpuConfiguration { cpuid, msrs })
58    }
59
60    /// Modifies provided config with changes from template
61    pub fn apply_template(
62        self,
63        template: &CustomCpuTemplate,
64    ) -> Result<Self, CpuConfigurationError> {
65        let Self {
66            mut cpuid,
67            mut msrs,
68        } = self;
69
70        let guest_cpuid = cpuid.inner_mut();
71
72        // Apply CPUID modifiers
73        for mod_leaf in template.cpuid_modifiers.iter() {
74            let cpuid_key = CpuidKey {
75                leaf: mod_leaf.leaf,
76                subleaf: mod_leaf.subleaf,
77            };
78            if let Some(entry) = guest_cpuid.get_mut(&cpuid_key) {
79                entry.flags = mod_leaf.flags;
80
81                // Can we modify one reg multiple times????
82                for mod_reg in &mod_leaf.modifiers {
83                    match mod_reg.register {
84                        CpuidRegister::Eax => {
85                            entry.result.eax = mod_reg.bitmap.apply(entry.result.eax)
86                        }
87                        CpuidRegister::Ebx => {
88                            entry.result.ebx = mod_reg.bitmap.apply(entry.result.ebx)
89                        }
90                        CpuidRegister::Ecx => {
91                            entry.result.ecx = mod_reg.bitmap.apply(entry.result.ecx)
92                        }
93                        CpuidRegister::Edx => {
94                            entry.result.edx = mod_reg.bitmap.apply(entry.result.edx)
95                        }
96                    }
97                }
98            } else {
99                return Err(CpuConfigurationError::CpuidFeatureNotSupported(
100                    cpuid_key.leaf,
101                    cpuid_key.subleaf,
102                ));
103            }
104        }
105
106        for modifier in &template.msr_modifiers {
107            if let Some(reg_value) = msrs.get_mut(&modifier.addr) {
108                *reg_value = modifier.bitmap.apply(*reg_value);
109            } else {
110                return Err(CpuConfigurationError::MsrNotSupported(modifier.addr));
111            }
112        }
113
114        Ok(Self { cpuid, msrs })
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use std::collections::BTreeMap;
121
122    use kvm_bindings::KVM_CPUID_FLAG_STATEFUL_FUNC;
123
124    use super::custom_cpu_template::{CpuidLeafModifier, CpuidRegisterModifier, RegisterModifier};
125    use super::*;
126    use crate::cpu_config::templates::RegisterValueFilter;
127    use crate::cpu_config::x86_64::cpuid::{CpuidEntry, IntelCpuid, KvmCpuidFlags};
128
129    fn build_test_template() -> CustomCpuTemplate {
130        CustomCpuTemplate {
131            cpuid_modifiers: vec![CpuidLeafModifier {
132                leaf: 0x3,
133                subleaf: 0x0,
134                flags: KvmCpuidFlags(KVM_CPUID_FLAG_STATEFUL_FUNC),
135                modifiers: vec![
136                    CpuidRegisterModifier {
137                        register: CpuidRegister::Eax,
138                        bitmap: RegisterValueFilter {
139                            filter: 0b0111,
140                            value: 0b0101,
141                        },
142                    },
143                    CpuidRegisterModifier {
144                        register: CpuidRegister::Ebx,
145                        bitmap: RegisterValueFilter {
146                            filter: 0b0111,
147                            value: 0b0100,
148                        },
149                    },
150                    CpuidRegisterModifier {
151                        register: CpuidRegister::Ecx,
152                        bitmap: RegisterValueFilter {
153                            filter: 0b0111,
154                            value: 0b0111,
155                        },
156                    },
157                    CpuidRegisterModifier {
158                        register: CpuidRegister::Edx,
159                        bitmap: RegisterValueFilter {
160                            filter: 0b0111,
161                            value: 0b0001,
162                        },
163                    },
164                ],
165            }],
166            msr_modifiers: vec![
167                RegisterModifier {
168                    addr: 0x9999,
169                    bitmap: RegisterValueFilter {
170                        filter: 0,
171                        value: 0,
172                    },
173                },
174                RegisterModifier {
175                    addr: 0x8000,
176                    bitmap: RegisterValueFilter {
177                        filter: 0,
178                        value: 0,
179                    },
180                },
181            ],
182            ..Default::default()
183        }
184    }
185
186    fn build_supported_cpuid() -> Cpuid {
187        Cpuid::Intel(IntelCpuid(BTreeMap::from([(
188            CpuidKey {
189                leaf: 0x3,
190                subleaf: 0x0,
191            },
192            CpuidEntry::default(),
193        )])))
194    }
195
196    fn empty_cpu_config() -> CpuConfiguration {
197        CpuConfiguration {
198            cpuid: Cpuid::Intel(IntelCpuid(BTreeMap::new())),
199            msrs: Default::default(),
200        }
201    }
202
203    fn supported_cpu_config() -> CpuConfiguration {
204        CpuConfiguration {
205            cpuid: build_supported_cpuid(),
206            msrs: BTreeMap::from([(0x8000, 0b1000), (0x9999, 0b1010)]),
207        }
208    }
209
210    fn unsupported_cpu_config() -> CpuConfiguration {
211        CpuConfiguration {
212            cpuid: build_supported_cpuid(),
213            msrs: BTreeMap::from([(0x8000, 0b1000), (0x8001, 0b1010)]),
214        }
215    }
216
217    #[test]
218    fn test_empty_template() {
219        let host_configuration = empty_cpu_config();
220        let cpu_config_result = host_configuration
221            .clone()
222            .apply_template(&CustomCpuTemplate::default());
223        assert!(
224            cpu_config_result.is_ok(),
225            "{}",
226            cpu_config_result.unwrap_err()
227        );
228        // CPUID will be comparable, but not MSRs.
229        // The configuration will be configuration required by the template,
230        // not a holistic view of all registers.
231        assert_eq!(cpu_config_result.unwrap().cpuid, host_configuration.cpuid);
232    }
233
234    #[test]
235    fn test_apply_template() {
236        let host_configuration = supported_cpu_config();
237        let cpu_config_result = host_configuration
238            .clone()
239            .apply_template(&build_test_template());
240        assert!(
241            cpu_config_result.is_ok(),
242            "{}",
243            cpu_config_result.unwrap_err()
244        );
245        assert_ne!(cpu_config_result.unwrap(), host_configuration);
246    }
247
248    /// Invalid test in this context is when the template
249    /// has modifiers for registers that are not supported.
250    #[test]
251    fn test_invalid_template() {
252        // Test CPUID validation
253        let host_configuration = empty_cpu_config();
254        let guest_template = build_test_template();
255        let cpu_config_result = host_configuration.apply_template(&guest_template);
256        assert!(
257            cpu_config_result.is_err(),
258            "Expected an error as template should have failed to modify a CPUID entry that is not \
259             supported by host configuration",
260        );
261        assert_eq!(
262            cpu_config_result.unwrap_err(),
263            CpuConfigurationError::CpuidFeatureNotSupported(
264                guest_template.cpuid_modifiers[0].leaf,
265                guest_template.cpuid_modifiers[0].subleaf
266            )
267        );
268
269        // Test MSR validation
270        let host_configuration = unsupported_cpu_config();
271        let guest_template = build_test_template();
272        let cpu_config_result = host_configuration.apply_template(&guest_template);
273        assert!(
274            cpu_config_result.is_err(),
275            "Expected an error as template should have failed to modify an MSR value that is not \
276             supported by host configuration",
277        );
278        assert_eq!(
279            cpu_config_result.unwrap_err(),
280            CpuConfigurationError::MsrNotSupported(guest_template.msr_modifiers[0].addr)
281        )
282    }
283}