1use std::borrow::Cow;
7
8use serde::de::Error as SerdeError;
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11use crate::arch::x86_64::cpu_model::{CpuModel, SKYLAKE_FMS};
12use crate::cpu_config::templates::{
13 CpuTemplateType, GetCpuTemplate, GetCpuTemplateError, KvmCapability, RegisterValueFilter,
14};
15use crate::cpu_config::templates_serde::*;
16use crate::cpu_config::x86_64::cpuid::KvmCpuidFlags;
17use crate::cpu_config::x86_64::cpuid::common::get_vendor_id_from_host;
18use crate::cpu_config::x86_64::static_cpu_templates::{StaticCpuTemplate, c3, t2, t2a, t2cl, t2s};
19use crate::logger::warn;
20
21impl GetCpuTemplate for Option<CpuTemplateType> {
22 fn get_cpu_template(&self) -> Result<Cow<'_, CustomCpuTemplate>, GetCpuTemplateError> {
23 use GetCpuTemplateError::*;
24
25 match self {
26 Some(template_type) => match template_type {
27 CpuTemplateType::Custom(template) => Ok(Cow::Borrowed(template)),
28 CpuTemplateType::Static(template) => {
29 if template == &StaticCpuTemplate::None {
31 return Err(InvalidStaticCpuTemplate(StaticCpuTemplate::None));
32 }
33
34 if &get_vendor_id_from_host().map_err(GetCpuVendor)?
35 != template.get_supported_vendor()
36 {
37 return Err(CpuVendorMismatched);
38 }
39
40 let cpu_model = CpuModel::get_cpu_model();
41 if !template.get_supported_cpu_models().contains(&cpu_model) {
42 return Err(InvalidCpuModel);
43 }
44
45 match template {
46 StaticCpuTemplate::C3 => {
47 if cpu_model == SKYLAKE_FMS {
48 warn!(
49 "On processors that do not enumerate FBSDP_NO, PSDP_NO and \
50 SBDR_SSDP_NO on IA32_ARCH_CAPABILITIES MSR, the guest kernel \
51 does not apply the mitigation against MMIO stale data \
52 vulnerability."
53 );
54 }
55 Ok(Cow::Owned(c3::c3()))
56 }
57 StaticCpuTemplate::T2 => Ok(Cow::Owned(t2::t2())),
58 StaticCpuTemplate::T2S => Ok(Cow::Owned(t2s::t2s())),
59 StaticCpuTemplate::T2CL => Ok(Cow::Owned(t2cl::t2cl())),
60 StaticCpuTemplate::T2A => Ok(Cow::Owned(t2a::t2a())),
61 StaticCpuTemplate::None => unreachable!(), }
63 }
64 },
65 None => Ok(Cow::Owned(CustomCpuTemplate::default())),
66 }
67 }
68}
69
70#[allow(missing_docs)]
72#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Hash, Ord, PartialOrd)]
73pub enum CpuidRegister {
74 Eax,
75 Ebx,
76 Ecx,
77 Edx,
78}
79
80#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)]
82pub struct CpuidRegisterModifier {
83 #[serde(
85 deserialize_with = "deserialize_cpuid_register",
86 serialize_with = "serialize_cpuid_register"
87 )]
88 pub register: CpuidRegister,
89 pub bitmap: RegisterValueFilter<u32>,
92}
93
94#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)]
98pub struct CpuidLeafModifier {
99 #[serde(
101 deserialize_with = "deserialize_from_str_u32",
102 serialize_with = "serialize_to_hex_str"
103 )]
104 pub leaf: u32,
105 #[serde(
107 deserialize_with = "deserialize_from_str_u32",
108 serialize_with = "serialize_to_hex_str"
109 )]
110 pub subleaf: u32,
111 #[serde(deserialize_with = "deserialize_kvm_cpuid_flags")]
113 pub flags: KvmCpuidFlags,
114 pub modifiers: Vec<CpuidRegisterModifier>,
116}
117
118#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
120#[serde(deny_unknown_fields)]
121pub struct CustomCpuTemplate {
122 #[serde(default)]
125 pub kvm_capabilities: Vec<KvmCapability>,
126 #[serde(default)]
128 pub cpuid_modifiers: Vec<CpuidLeafModifier>,
129 #[serde(default)]
131 pub msr_modifiers: Vec<RegisterModifier>,
132}
133
134impl CustomCpuTemplate {
135 pub fn msr_index_iter(&self) -> impl ExactSizeIterator<Item = u32> + '_ {
137 self.msr_modifiers.iter().map(|modifier| modifier.addr)
138 }
139
140 pub fn validate(&self) -> Result<(), serde_json::Error> {
142 Ok(())
143 }
144}
145
146#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
149pub struct RegisterModifier {
150 #[serde(
152 deserialize_with = "deserialize_from_str_u32",
153 serialize_with = "serialize_to_hex_str"
154 )]
155 pub addr: u32,
156 pub bitmap: RegisterValueFilter<u64>,
159}
160
161fn deserialize_kvm_cpuid_flags<'de, D>(deserializer: D) -> Result<KvmCpuidFlags, D::Error>
162where
163 D: Deserializer<'de>,
164{
165 let flag = u32::deserialize(deserializer)?;
166 Ok(KvmCpuidFlags(flag))
167}
168
169fn deserialize_cpuid_register<'de, D>(deserializer: D) -> Result<CpuidRegister, D::Error>
170where
171 D: Deserializer<'de>,
172{
173 let cpuid_register_str = String::deserialize(deserializer)?;
174
175 Ok(match cpuid_register_str.as_str() {
176 "eax" => CpuidRegister::Eax,
177 "ebx" => CpuidRegister::Ebx,
178 "ecx" => CpuidRegister::Ecx,
179 "edx" => CpuidRegister::Edx,
180 _ => {
181 return Err(D::Error::custom(
182 "Invalid CPUID register. Must be one of [eax, ebx, ecx, edx]",
183 ));
184 }
185 })
186}
187
188fn serialize_cpuid_register<S>(cpuid_reg: &CpuidRegister, serializer: S) -> Result<S::Ok, S::Error>
189where
190 S: Serializer,
191{
192 match cpuid_reg {
193 CpuidRegister::Eax => serializer.serialize_str("eax"),
194 CpuidRegister::Ebx => serializer.serialize_str("ebx"),
195 CpuidRegister::Ecx => serializer.serialize_str("ecx"),
196 CpuidRegister::Edx => serializer.serialize_str("edx"),
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use serde_json::Value;
203
204 use super::*;
205 use crate::cpu_config::x86_64::test_utils::{TEST_TEMPLATE_JSON, build_test_template};
206
207 #[test]
208 fn test_get_cpu_template_with_no_template() {
209 let cpu_template = None;
212 assert_eq!(
213 cpu_template.get_cpu_template().unwrap(),
214 Cow::Owned(CustomCpuTemplate::default()),
215 );
216 }
217
218 #[test]
219 fn test_get_cpu_template_with_c3_static_template() {
220 let c3 = StaticCpuTemplate::C3;
224 let cpu_template = Some(CpuTemplateType::Static(c3));
225 if &get_vendor_id_from_host().unwrap() == c3.get_supported_vendor() {
226 if c3
227 .get_supported_cpu_models()
228 .contains(&CpuModel::get_cpu_model())
229 {
230 assert_eq!(
231 cpu_template.get_cpu_template().unwrap(),
232 Cow::Owned(c3::c3())
233 );
234 } else {
235 assert_eq!(
236 cpu_template.get_cpu_template().unwrap_err(),
237 GetCpuTemplateError::InvalidCpuModel,
238 );
239 }
240 } else {
241 assert_eq!(
242 cpu_template.get_cpu_template().unwrap_err(),
243 GetCpuTemplateError::CpuVendorMismatched,
244 );
245 }
246 }
247
248 #[test]
249 fn test_get_cpu_template_with_t2_static_template() {
250 let t2 = StaticCpuTemplate::T2;
254 let cpu_template = Some(CpuTemplateType::Static(t2));
255 if &get_vendor_id_from_host().unwrap() == t2.get_supported_vendor() {
256 if t2
257 .get_supported_cpu_models()
258 .contains(&CpuModel::get_cpu_model())
259 {
260 assert_eq!(
261 cpu_template.get_cpu_template().unwrap(),
262 Cow::Owned(t2::t2())
263 );
264 } else {
265 assert_eq!(
266 cpu_template.get_cpu_template().unwrap_err(),
267 GetCpuTemplateError::InvalidCpuModel,
268 );
269 }
270 } else {
271 assert_eq!(
272 cpu_template.get_cpu_template().unwrap_err(),
273 GetCpuTemplateError::CpuVendorMismatched,
274 );
275 }
276 }
277
278 #[test]
279 fn test_get_cpu_template_with_t2s_static_template() {
280 let t2s = StaticCpuTemplate::T2S;
284 let cpu_template = Some(CpuTemplateType::Static(t2s));
285 if &get_vendor_id_from_host().unwrap() == t2s.get_supported_vendor() {
286 if t2s
287 .get_supported_cpu_models()
288 .contains(&CpuModel::get_cpu_model())
289 {
290 assert_eq!(
291 cpu_template.get_cpu_template().unwrap(),
292 Cow::Owned(t2s::t2s())
293 );
294 } else {
295 assert_eq!(
296 cpu_template.get_cpu_template().unwrap_err(),
297 GetCpuTemplateError::InvalidCpuModel,
298 );
299 }
300 } else {
301 assert_eq!(
302 cpu_template.get_cpu_template().unwrap_err(),
303 GetCpuTemplateError::CpuVendorMismatched,
304 );
305 }
306 }
307
308 #[test]
309 fn test_t2cl_template_equality() {
310 let t2cl_custom_template = CpuTemplateType::Custom(t2cl::t2cl());
314 assert_ne!(
318 t2cl_custom_template,
319 CpuTemplateType::Static(StaticCpuTemplate::T2CL)
320 );
321 }
322
323 #[test]
324 fn test_get_cpu_template_with_t2cl_static_template() {
325 let t2cl = StaticCpuTemplate::T2CL;
329 let cpu_template = Some(CpuTemplateType::Static(t2cl));
330 if &get_vendor_id_from_host().unwrap() == t2cl.get_supported_vendor() {
331 if t2cl
332 .get_supported_cpu_models()
333 .contains(&CpuModel::get_cpu_model())
334 {
335 assert_eq!(
336 cpu_template.get_cpu_template().unwrap(),
337 Cow::Owned(t2cl::t2cl())
338 );
339 } else {
340 assert_eq!(
341 cpu_template.get_cpu_template().unwrap_err(),
342 GetCpuTemplateError::InvalidCpuModel,
343 );
344 }
345 } else {
346 assert_eq!(
347 cpu_template.get_cpu_template().unwrap_err(),
348 GetCpuTemplateError::CpuVendorMismatched,
349 );
350 }
351 }
352
353 #[test]
354 fn test_get_cpu_template_with_t2a_static_template() {
355 let t2a = StaticCpuTemplate::T2A;
358 let cpu_template = Some(CpuTemplateType::Static(t2a));
359 if &get_vendor_id_from_host().unwrap() == t2a.get_supported_vendor() {
360 if t2a
361 .get_supported_cpu_models()
362 .contains(&CpuModel::get_cpu_model())
363 {
364 assert_eq!(
365 cpu_template.get_cpu_template().unwrap(),
366 Cow::Owned(t2a::t2a())
367 );
368 } else {
369 assert_eq!(
370 cpu_template.get_cpu_template().unwrap_err(),
371 GetCpuTemplateError::InvalidCpuModel,
372 );
373 }
374 } else {
375 assert_eq!(
376 cpu_template.get_cpu_template().unwrap_err(),
377 GetCpuTemplateError::CpuVendorMismatched,
378 );
379 }
380 }
381
382 #[test]
383 fn test_get_cpu_template_with_none_static_template() {
384 let cpu_template = Some(CpuTemplateType::Static(StaticCpuTemplate::None));
388 assert_eq!(
389 cpu_template.get_cpu_template().unwrap_err(),
390 GetCpuTemplateError::InvalidStaticCpuTemplate(StaticCpuTemplate::None)
391 );
392
393 assert_eq!(format!("{}", StaticCpuTemplate::None), "None");
395 }
396
397 #[test]
398 fn test_get_cpu_template_with_custom_template() {
399 let inner_cpu_template = CustomCpuTemplate::default();
402 let cpu_template = Some(CpuTemplateType::Custom(inner_cpu_template.clone()));
403 assert_eq!(
404 cpu_template.get_cpu_template().unwrap(),
405 Cow::Borrowed(&inner_cpu_template)
406 );
407 }
408
409 #[test]
410 fn test_malformed_json() {
411 let cpu_template_result = serde_json::from_str::<CustomCpuTemplate>(
413 r#"{
414 "cpuid_modifiers": [
415 {
416 "leaf": "0x80000001",
417 "subleaf": "0b000111",
418 "flags": 0,
419 "modifiers": [
420 {
421 "register": "ekx",
422 "bitmap": "0bx00100xxx1xxxxxxxxxxxxxxxxxxxxx1"
423 }
424 ]
425 },
426 ],
427 }"#,
428 );
429 assert!(
430 cpu_template_result
431 .unwrap_err()
432 .to_string()
433 .contains("Invalid CPUID register. Must be one of [eax, ebx, ecx, edx]")
434 );
435
436 let cpu_template_result = serde_json::from_str::<CustomCpuTemplate>(
438 r#"{
439 "msr_modifiers": [
440 {
441 "addr": "0jj0",
442 "bitmap": "0bx00100xxx1xxxx00xxx1xxxxxxxxxxx1"
443 },
444 ]
445 }"#,
446 );
447 let error_msg: String = cpu_template_result.unwrap_err().to_string();
448 assert!(
450 error_msg.contains("No supported number system prefix found in value"),
451 "{}",
452 error_msg
453 );
454
455 let cpu_template_result = serde_json::from_str::<CustomCpuTemplate>(
457 r#"{
458 "cpuid_modifiers": [
459 {
460 "leaf": "k",
461 "subleaf": "0b000111",
462 "flags": 0,
463 "modifiers": [
464 {
465 "register": "eax",
466 "bitmap": "0bx00100xxx1xxxxxxxxxxxxxxxxxxxxx1"
467 }
468 ]
469 },
470 ],
471 }"#,
472 );
473 let error_msg: String = cpu_template_result.unwrap_err().to_string();
474 assert!(
476 error_msg.contains("No supported number system prefix found in value"),
477 "{}",
478 error_msg
479 );
480 let cpu_template_result = serde_json::from_str::<CustomCpuTemplate>(
482 r#"{
483 "msr_modifiers": [
484 {
485 "addr": "0x200",
486 "bitmap": "0bx0?1_0_0x_?x1xxxx00xxx1xxxxxxxxxxx1"
487 },
488 ]
489 }"#,
490 );
491 assert!(cpu_template_result.unwrap_err().to_string().contains(
492 "Failed to parse string [0bx0?1_0_0x_?x1xxxx00xxx1xxxxxxxxxxx1] as a bitmap"
493 ));
494 let cpu_template_result = serde_json::from_str::<CustomCpuTemplate>(
496 r#"{
497 "msr_modifiers": [
498 {
499 "addr": "0x200",
500 "bitmap": "0bx00100x0x1xxxx05xxx1xxxxxxxxxxx1"
501 },
502 ]
503 }"#,
504 );
505 assert!(
506 cpu_template_result.unwrap_err().to_string().contains(
507 "Failed to parse string [0bx00100x0x1xxxx05xxx1xxxxxxxxxxx1] as a bitmap"
508 )
509 );
510 }
511
512 #[test]
513 fn test_deserialization_lifecycle() {
514 let cpu_template = serde_json::from_str::<CustomCpuTemplate>(TEST_TEMPLATE_JSON)
515 .expect("Failed to deserialize custom CPU template.");
516 assert_eq!(5, cpu_template.cpuid_modifiers.len());
517 assert_eq!(4, cpu_template.msr_modifiers.len());
518 }
519
520 #[test]
521 fn test_serialization_lifecycle() {
522 let template = build_test_template();
523 let template_json_str_result = serde_json::to_string_pretty(&template);
524 let template_json = template_json_str_result.unwrap();
525
526 let deserialization_result = serde_json::from_str::<CustomCpuTemplate>(&template_json);
527 assert_eq!(template, deserialization_result.unwrap());
528 }
529
530 #[test]
533 fn test_bitmap_width() {
534 let mut cpuid_checked = false;
535 let mut msr_checked = false;
536
537 let template = build_test_template();
538
539 let x86_template_str =
540 serde_json::to_string(&template).expect("Error serializing x86 template");
541 let json_tree: Value = serde_json::from_str(&x86_template_str)
542 .expect("Error deserializing x86 template JSON string");
543
544 if let Some(cpuid_modifiers_root) = json_tree.get("cpuid_modifiers") {
546 let cpuid_mod_node = &cpuid_modifiers_root.as_array().unwrap()[0];
547 if let Some(modifiers_node) = cpuid_mod_node.get("modifiers") {
548 let mod_node = &modifiers_node.as_array().unwrap()[0];
549 if let Some(bit_map_str) = mod_node.get("bitmap") {
550 assert_eq!(bit_map_str.as_str().unwrap().len(), 34);
552 cpuid_checked = true;
553 }
554 }
555 }
556
557 if let Some(msr_modifiers_root) = json_tree.get("msr_modifiers") {
559 let msr_mod_node = &msr_modifiers_root.as_array().unwrap()[0];
560 if let Some(bit_map_str) = msr_mod_node.get("bitmap") {
561 assert_eq!(bit_map_str.as_str().unwrap().len(), 66);
563 assert!(bit_map_str.as_str().unwrap().starts_with("0b"));
564 msr_checked = true;
565 }
566 }
567
568 assert!(
569 cpuid_checked,
570 "CPUID bitmap width in a x86_64 template was not tested."
571 );
572 assert!(
573 msr_checked,
574 "MSR bitmap width in a x86_64 template was not tested."
575 );
576 }
577}