1#![warn(clippy::pedantic)]
9#![allow(
10 clippy::blanket_clippy_restriction_lints,
11 clippy::implicit_return,
12 clippy::pattern_type_mismatch,
13 clippy::std_instead_of_alloc,
14 clippy::std_instead_of_core,
15 clippy::pub_use,
16 clippy::non_ascii_literal,
17 clippy::single_char_lifetime_names,
18 clippy::exhaustive_enums,
19 clippy::exhaustive_structs,
20 clippy::unseparated_literal_suffix,
21 clippy::mod_module_files,
22 clippy::missing_trait_methods
23)]
24#![allow(
26 clippy::unreadable_literal,
27 clippy::similar_names,
28 clippy::same_name_method,
29 clippy::doc_markdown,
30 clippy::module_name_repetitions
31)]
32
33use std::convert::TryFrom;
36use std::mem::{size_of, transmute};
37
38pub mod common;
40
41pub mod amd;
43pub use amd::AmdCpuid;
44
45pub mod intel;
47pub use intel::IntelCpuid;
48
49mod normalize;
51
52pub use normalize::{FeatureInformationError, GetMaxCpusPerPackageError, NormalizeCpuidError};
53
54pub const VENDOR_ID_INTEL: &[u8; 12] = b"GenuineIntel";
56
57pub const VENDOR_ID_AMD: &[u8; 12] = b"AuthenticAMD";
59
60#[allow(clippy::undocumented_unsafe_blocks)]
62pub const VENDOR_ID_INTEL_STR: &str = unsafe { std::str::from_utf8_unchecked(VENDOR_ID_INTEL) };
63
64#[allow(clippy::undocumented_unsafe_blocks)]
66pub const VENDOR_ID_AMD_STR: &str = unsafe { std::str::from_utf8_unchecked(VENDOR_ID_AMD) };
67
68pub const BRAND_STRING_LENGTH: usize = 3 * 4 * 4;
70
71fn cpuid(leaf: u32) -> std::arch::x86_64::CpuidResult {
73 cpuid_count(leaf, 0)
74}
75
76fn cpuid_count(leaf: u32, subleaf: u32) -> std::arch::x86_64::CpuidResult {
78 unsafe { std::arch::x86_64::__cpuid_count(leaf, subleaf) }
81}
82
83#[inline]
96#[must_use]
97pub fn host_brand_string() -> [u8; BRAND_STRING_LENGTH] {
98 let leaf_a = cpuid(0x80000002);
99 let leaf_b = cpuid(0x80000003);
100 let leaf_c = cpuid(0x80000004);
101
102 let arr = [
103 leaf_a.eax, leaf_a.ebx, leaf_a.ecx, leaf_a.edx, leaf_b.eax, leaf_b.ebx, leaf_b.ecx,
104 leaf_b.edx, leaf_c.eax, leaf_c.ebx, leaf_c.ecx, leaf_c.edx,
105 ];
106
107 unsafe { std::mem::transmute(arr) }
110}
111
112pub trait CpuidTrait {
114 #[inline]
117 #[must_use]
118 fn vendor_id(&self) -> Option<[u8; 12]> {
119 let leaf_0 = self.get(&CpuidKey::leaf(0x0))?;
120
121 let (ebx, edx, ecx) = (
123 leaf_0.result.ebx.to_ne_bytes(),
124 leaf_0.result.edx.to_ne_bytes(),
125 leaf_0.result.ecx.to_ne_bytes(),
126 );
127 let arr: [u8; 12] = [
128 ebx[0], ebx[1], ebx[2], ebx[3], edx[0], edx[1], edx[2], edx[3], ecx[0], ecx[1], ecx[2],
129 ecx[3],
130 ];
131 Some(arr)
132 }
133
134 fn get(&self, key: &CpuidKey) -> Option<&CpuidEntry>;
136
137 fn get_mut(&mut self, key: &CpuidKey) -> Option<&mut CpuidEntry>;
139
140 #[inline]
146 fn apply_brand_string(
147 &mut self,
148 brand_string: &[u8; BRAND_STRING_LENGTH],
149 ) -> Result<(), MissingBrandStringLeaves> {
150 {
152 let leaf: &mut CpuidEntry = self
153 .get_mut(&CpuidKey::leaf(0x80000002))
154 .ok_or(MissingBrandStringLeaves)?;
155 leaf.result.eax = u32::from_ne_bytes([
156 brand_string[0],
157 brand_string[1],
158 brand_string[2],
159 brand_string[3],
160 ]);
161 leaf.result.ebx = u32::from_ne_bytes([
162 brand_string[4],
163 brand_string[5],
164 brand_string[6],
165 brand_string[7],
166 ]);
167 leaf.result.ecx = u32::from_ne_bytes([
168 brand_string[8],
169 brand_string[9],
170 brand_string[10],
171 brand_string[11],
172 ]);
173 leaf.result.edx = u32::from_ne_bytes([
174 brand_string[12],
175 brand_string[13],
176 brand_string[14],
177 brand_string[15],
178 ]);
179 }
180
181 {
183 let leaf: &mut CpuidEntry = self
184 .get_mut(&CpuidKey::leaf(0x80000003))
185 .ok_or(MissingBrandStringLeaves)?;
186 leaf.result.eax = u32::from_ne_bytes([
187 brand_string[16],
188 brand_string[17],
189 brand_string[18],
190 brand_string[19],
191 ]);
192 leaf.result.ebx = u32::from_ne_bytes([
193 brand_string[20],
194 brand_string[21],
195 brand_string[22],
196 brand_string[23],
197 ]);
198 leaf.result.ecx = u32::from_ne_bytes([
199 brand_string[24],
200 brand_string[25],
201 brand_string[26],
202 brand_string[27],
203 ]);
204 leaf.result.edx = u32::from_ne_bytes([
205 brand_string[28],
206 brand_string[29],
207 brand_string[30],
208 brand_string[31],
209 ]);
210 }
211
212 {
214 let leaf: &mut CpuidEntry = self
215 .get_mut(&CpuidKey::leaf(0x80000004))
216 .ok_or(MissingBrandStringLeaves)?;
217 leaf.result.eax = u32::from_ne_bytes([
218 brand_string[32],
219 brand_string[33],
220 brand_string[34],
221 brand_string[35],
222 ]);
223 leaf.result.ebx = u32::from_ne_bytes([
224 brand_string[36],
225 brand_string[37],
226 brand_string[38],
227 brand_string[39],
228 ]);
229 leaf.result.ecx = u32::from_ne_bytes([
230 brand_string[40],
231 brand_string[41],
232 brand_string[42],
233 brand_string[43],
234 ]);
235 leaf.result.edx = u32::from_ne_bytes([
236 brand_string[44],
237 brand_string[45],
238 brand_string[46],
239 brand_string[47],
240 ]);
241 }
242
243 Ok(())
244 }
245}
246
247impl CpuidTrait for kvm_bindings::CpuId {
248 #[allow(clippy::transmute_ptr_to_ptr, clippy::unwrap_used)]
250 #[inline]
251 fn get(&self, CpuidKey { leaf, subleaf }: &CpuidKey) -> Option<&CpuidEntry> {
252 let entry_opt = self
253 .as_slice()
254 .iter()
255 .find(|entry| entry.function == *leaf && entry.index == *subleaf);
256
257 entry_opt.map(|entry| {
258 unsafe {
261 let arr: &[u8; size_of::<kvm_bindings::kvm_cpuid_entry2>()] = transmute(entry);
262 let arr2: &[u8; size_of::<CpuidEntry>()] = arr[8..28].try_into().unwrap();
263 transmute::<_, &CpuidEntry>(arr2)
264 }
265 })
266 }
267
268 #[allow(clippy::transmute_ptr_to_ptr, clippy::unwrap_used)]
270 #[inline]
271 fn get_mut(&mut self, CpuidKey { leaf, subleaf }: &CpuidKey) -> Option<&mut CpuidEntry> {
272 let entry_opt = self
273 .as_mut_slice()
274 .iter_mut()
275 .find(|entry| entry.function == *leaf && entry.index == *subleaf);
276 entry_opt.map(|entry| {
277 unsafe {
280 let arr: &mut [u8; size_of::<kvm_bindings::kvm_cpuid_entry2>()] = transmute(entry);
281 let arr2: &mut [u8; size_of::<CpuidEntry>()] =
282 (&mut arr[8..28]).try_into().unwrap();
283 transmute::<_, &mut CpuidEntry>(arr2)
284 }
285 })
286 }
287}
288
289#[derive(Debug, thiserror::Error, Eq, PartialEq)]
291#[error("Missing brand string leaves 0x80000002, 0x80000003 and 0x80000004.")]
292pub struct MissingBrandStringLeaves;
293
294#[rustfmt::skip]
296#[derive(Debug, thiserror::Error, displaydoc::Display, PartialEq, Eq)]
297pub enum CpuidTryFromKvmCpuid {
298 MissingLeaf0,
300 UnsupportedVendor([u8; 12]),
302}
303
304#[derive(Debug, Clone, PartialEq, Eq)]
306pub enum Cpuid {
307 Intel(IntelCpuid),
309 Amd(AmdCpuid),
311}
312
313impl Cpuid {
314 #[inline]
316 #[must_use]
317 pub fn intel_mut(&mut self) -> Option<&mut IntelCpuid> {
318 match self {
319 Self::Intel(intel) => Some(intel),
320 Self::Amd(_) => None,
321 }
322 }
323
324 #[inline]
326 #[must_use]
327 pub fn intel(&self) -> Option<&IntelCpuid> {
328 match self {
329 Self::Intel(intel) => Some(intel),
330 Self::Amd(_) => None,
331 }
332 }
333
334 #[inline]
336 #[must_use]
337 pub fn amd(&self) -> Option<&AmdCpuid> {
338 match self {
339 Self::Intel(_) => None,
340 Self::Amd(amd) => Some(amd),
341 }
342 }
343
344 #[inline]
346 #[must_use]
347 pub fn amd_mut(&mut self) -> Option<&mut AmdCpuid> {
348 match self {
349 Self::Intel(_) => None,
350 Self::Amd(amd) => Some(amd),
351 }
352 }
353
354 #[inline]
356 #[must_use]
357 pub fn inner(&self) -> &std::collections::BTreeMap<CpuidKey, CpuidEntry> {
358 match self {
359 Self::Intel(intel_cpuid) => &intel_cpuid.0,
360 Self::Amd(amd_cpuid) => &amd_cpuid.0,
361 }
362 }
363
364 #[inline]
366 #[must_use]
367 pub fn inner_mut(&mut self) -> &mut std::collections::BTreeMap<CpuidKey, CpuidEntry> {
368 match self {
369 Self::Intel(intel_cpuid) => &mut intel_cpuid.0,
370 Self::Amd(amd_cpuid) => &mut amd_cpuid.0,
371 }
372 }
373}
374
375impl CpuidTrait for Cpuid {
376 #[inline]
378 fn get(&self, key: &CpuidKey) -> Option<&CpuidEntry> {
379 match self {
380 Self::Intel(intel_cpuid) => intel_cpuid.get(key),
381 Self::Amd(amd_cpuid) => amd_cpuid.get(key),
382 }
383 }
384
385 #[inline]
387 fn get_mut(&mut self, key: &CpuidKey) -> Option<&mut CpuidEntry> {
388 match self {
389 Self::Intel(intel_cpuid) => intel_cpuid.get_mut(key),
390 Self::Amd(amd_cpuid) => amd_cpuid.get_mut(key),
391 }
392 }
393}
394
395impl TryFrom<kvm_bindings::CpuId> for Cpuid {
396 type Error = CpuidTryFromKvmCpuid;
397
398 #[inline]
399 fn try_from(kvm_cpuid: kvm_bindings::CpuId) -> Result<Self, Self::Error> {
400 let vendor_id = kvm_cpuid
401 .vendor_id()
402 .ok_or(CpuidTryFromKvmCpuid::MissingLeaf0)?;
403
404 match std::str::from_utf8(&vendor_id) {
405 Ok(VENDOR_ID_INTEL_STR) => Ok(Cpuid::Intel(IntelCpuid::from(kvm_cpuid))),
406 Ok(VENDOR_ID_AMD_STR) => Ok(Cpuid::Amd(AmdCpuid::from(kvm_cpuid))),
407 _ => Err(CpuidTryFromKvmCpuid::UnsupportedVendor(vendor_id)),
408 }
409 }
410}
411
412impl TryFrom<Cpuid> for kvm_bindings::CpuId {
413 type Error = vmm_sys_util::fam::Error;
414
415 fn try_from(cpuid: Cpuid) -> Result<Self, Self::Error> {
416 let entries = cpuid
417 .inner()
418 .iter()
419 .map(|(key, entry)| kvm_bindings::kvm_cpuid_entry2 {
420 function: key.leaf,
421 index: key.subleaf,
422 flags: entry.flags.0,
423 eax: entry.result.eax,
424 ebx: entry.result.ebx,
425 ecx: entry.result.ecx,
426 edx: entry.result.edx,
427 ..Default::default()
428 })
429 .collect::<Vec<_>>();
430
431 kvm_bindings::CpuId::from_entries(&entries)
432 }
433}
434
435#[derive(Debug, Clone, Default, PartialEq, Eq)]
437pub struct CpuidKey {
438 pub leaf: u32,
440 pub subleaf: u32,
442}
443
444impl CpuidKey {
445 #[inline]
447 #[must_use]
448 pub fn leaf(leaf: u32) -> Self {
449 Self { leaf, subleaf: 0 }
450 }
451
452 #[inline]
454 #[must_use]
455 pub fn subleaf(leaf: u32, subleaf: u32) -> Self {
456 Self { leaf, subleaf }
457 }
458}
459
460impl std::cmp::PartialOrd for CpuidKey {
461 #[inline]
462 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
463 Some(std::cmp::Ord::cmp(self, other))
464 }
465}
466
467impl std::cmp::Ord for CpuidKey {
468 #[inline]
469 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
470 self.leaf
471 .cmp(&other.leaf)
472 .then(self.subleaf.cmp(&other.subleaf))
473 }
474}
475
476#[derive(
478 Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash,
479)]
480pub struct KvmCpuidFlags(pub u32);
481impl KvmCpuidFlags {
482 pub const EMPTY: Self = Self(0);
484 pub const SIGNIFICANT_INDEX: Self = Self(1 << 0);
487 pub const STATEFUL_FUNC: Self = Self(1 << 1);
489 pub const STATE_READ_NEXT: Self = Self(1 << 2);
491}
492
493#[allow(clippy::derivable_impls)]
494impl Default for KvmCpuidFlags {
495 #[inline]
496 fn default() -> Self {
497 Self(0)
498 }
499}
500
501#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
503#[repr(C)]
504pub struct CpuidEntry {
505 pub flags: KvmCpuidFlags,
551 pub result: CpuidRegisters,
553}
554
555#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
559#[repr(C)]
560pub struct CpuidRegisters {
561 pub eax: u32,
563 pub ebx: u32,
565 pub ecx: u32,
567 pub edx: u32,
569}
570
571impl From<core::arch::x86_64::CpuidResult> for CpuidRegisters {
572 #[inline]
573 fn from(
574 core::arch::x86_64::CpuidResult { eax, ebx, ecx, edx }: core::arch::x86_64::CpuidResult,
575 ) -> Self {
576 Self { eax, ebx, ecx, edx }
577 }
578}
579
580#[cfg(test)]
581mod tests {
582 use std::collections::BTreeMap;
583
584 use super::*;
585
586 fn build_intel_leaf0_for_cpuid() -> (CpuidKey, CpuidEntry) {
587 (
588 CpuidKey {
589 leaf: 0x0,
590 subleaf: 0x0,
591 },
592 CpuidEntry {
593 flags: KvmCpuidFlags::EMPTY,
594 result: CpuidRegisters {
595 eax: 0x1,
596 ebx: 0x756E6547,
598 ecx: 0x6C65746E,
599 edx: 0x49656E69,
600 },
601 },
602 )
603 }
604
605 fn build_intel_leaf0_for_kvmcpuid() -> kvm_bindings::kvm_cpuid_entry2 {
606 kvm_bindings::kvm_cpuid_entry2 {
607 function: 0x0,
608 index: 0x0,
609 flags: 0x0,
610 eax: 0x1,
611 ebx: 0x756E6547,
613 ecx: 0x6C65746E,
614 edx: 0x49656E69,
615 ..Default::default()
616 }
617 }
618
619 fn build_amd_leaf0_for_cpuid() -> (CpuidKey, CpuidEntry) {
620 (
621 CpuidKey {
622 leaf: 0x0,
623 subleaf: 0x0,
624 },
625 CpuidEntry {
626 flags: KvmCpuidFlags::EMPTY,
627 result: CpuidRegisters {
628 eax: 0x1,
629 ebx: 0x68747541,
631 ecx: 0x444D4163,
632 edx: 0x69746E65,
633 },
634 },
635 )
636 }
637
638 fn build_amd_leaf0_for_kvmcpuid() -> kvm_bindings::kvm_cpuid_entry2 {
639 kvm_bindings::kvm_cpuid_entry2 {
640 function: 0x0,
641 index: 0x0,
642 flags: 0x0,
643 eax: 0x1,
644 ebx: 0x68747541,
646 ecx: 0x444D4163,
647 edx: 0x69746E65,
648 ..Default::default()
649 }
650 }
651
652 fn build_sample_leaf_for_cpuid() -> (CpuidKey, CpuidEntry) {
653 (
654 CpuidKey {
655 leaf: 0x1,
656 subleaf: 0x2,
657 },
658 CpuidEntry {
659 flags: KvmCpuidFlags::SIGNIFICANT_INDEX,
660 result: CpuidRegisters {
661 eax: 0x3,
662 ebx: 0x4,
663 ecx: 0x5,
664 edx: 0x6,
665 },
666 },
667 )
668 }
669
670 fn build_sample_leaf_for_kvmcpuid() -> kvm_bindings::kvm_cpuid_entry2 {
671 kvm_bindings::kvm_cpuid_entry2 {
672 function: 0x1,
673 index: 0x2,
674 flags: 0x1,
675 eax: 0x3,
676 ebx: 0x4,
677 ecx: 0x5,
678 edx: 0x6,
679 ..Default::default()
680 }
681 }
682
683 fn build_sample_intel_cpuid() -> Cpuid {
684 Cpuid::Intel(IntelCpuid(BTreeMap::from([
685 build_intel_leaf0_for_cpuid(),
686 build_sample_leaf_for_cpuid(),
687 ])))
688 }
689
690 fn build_sample_intel_kvmcpuid() -> kvm_bindings::CpuId {
691 kvm_bindings::CpuId::from_entries(&[
692 build_intel_leaf0_for_kvmcpuid(),
693 build_sample_leaf_for_kvmcpuid(),
694 ])
695 .unwrap()
696 }
697
698 fn build_sample_amd_cpuid() -> Cpuid {
699 Cpuid::Amd(AmdCpuid(BTreeMap::from([
700 build_amd_leaf0_for_cpuid(),
701 build_sample_leaf_for_cpuid(),
702 ])))
703 }
704
705 fn build_sample_amd_kvmcpuid() -> kvm_bindings::CpuId {
706 kvm_bindings::CpuId::from_entries(&[
707 build_amd_leaf0_for_kvmcpuid(),
708 build_sample_leaf_for_kvmcpuid(),
709 ])
710 .unwrap()
711 }
712
713 #[test]
714 fn get() {
715 let cpuid = build_sample_intel_cpuid();
716 assert_eq!(
717 cpuid.get(&CpuidKey {
718 leaf: 0x8888,
719 subleaf: 0x0
720 }),
721 None
722 );
723 assert!(
724 cpuid
725 .get(&CpuidKey {
726 leaf: 0x0,
727 subleaf: 0x0,
728 })
729 .is_some()
730 );
731 }
732
733 #[test]
734 fn get_mut() {
735 let mut cpuid = build_sample_intel_cpuid();
736 assert_eq!(
737 cpuid.get_mut(&CpuidKey {
738 leaf: 0x888,
739 subleaf: 0x0,
740 }),
741 None
742 );
743 assert!(
744 cpuid
745 .get_mut(&CpuidKey {
746 leaf: 0x0,
747 subleaf: 0x0,
748 })
749 .is_some()
750 );
751 }
752
753 #[test]
754 fn test_kvmcpuid_to_cpuid() {
755 let kvm_cpuid = build_sample_intel_kvmcpuid();
756 let cpuid = Cpuid::try_from(kvm_cpuid).unwrap();
757 assert_eq!(cpuid, build_sample_intel_cpuid());
758
759 let kvm_cpuid = build_sample_amd_kvmcpuid();
760 let cpuid = Cpuid::try_from(kvm_cpuid).unwrap();
761 assert_eq!(cpuid, build_sample_amd_cpuid());
762 }
763
764 #[test]
765 fn test_cpuid_to_kvmcpuid() {
766 let cpuid = build_sample_intel_cpuid();
767 let kvm_cpuid = kvm_bindings::CpuId::try_from(cpuid).unwrap();
768 assert_eq!(kvm_cpuid, build_sample_intel_kvmcpuid());
769
770 let cpuid = build_sample_amd_cpuid();
771 let kvm_cpuid = kvm_bindings::CpuId::try_from(cpuid).unwrap();
772 assert_eq!(kvm_cpuid, build_sample_amd_kvmcpuid());
773 }
774
775 #[test]
776 fn test_invalid_kvmcpuid_to_cpuid() {
777 let kvm_cpuid =
779 kvm_bindings::CpuId::from_entries(&[kvm_bindings::kvm_cpuid_entry2::default()])
780 .unwrap();
781 let cpuid = Cpuid::try_from(kvm_cpuid);
782 assert_eq!(cpuid, Err(CpuidTryFromKvmCpuid::UnsupportedVendor([0; 12])));
783 }
784}