1pub mod cpuid;
6pub mod custom_cpu_template;
8pub mod static_cpu_templates;
10pub 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#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
24pub enum CpuConfigurationError {
25 CpuidFeatureNotSupported(u32, u32),
27 MsrNotSupported(u32),
29 CpuidFromKvmCpuid(#[from] crate::cpu_config::x86_64::cpuid::CpuidTryFromKvmCpuid),
31 VcpuIoctl(#[from] crate::vstate::vcpu::KvmVcpuError),
33}
34
35#[derive(Debug, Clone, PartialEq)]
37pub struct CpuConfiguration {
38 pub cpuid: Cpuid,
40 pub msrs: BTreeMap<u32, u64>,
44}
45
46impl CpuConfiguration {
47 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 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 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 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 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 #[test]
251 fn test_invalid_template() {
252 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 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}