1use bitflags::bitflags;
3use core::fmt::{self, Debug, Display, Formatter};
4use core::mem::size_of;
5use core::slice;
6use core::str;
7
8use crate::{get_bits, CpuIdResult, Vendor};
9
10pub struct ExtendedProcessorFeatureIdentifiers {
15 vendor: Vendor,
16 eax: u32,
17 ebx: u32,
18 ecx: ExtendedFunctionInfoEcx,
19 edx: ExtendedFunctionInfoEdx,
20}
21
22impl ExtendedProcessorFeatureIdentifiers {
23 pub(crate) fn new(vendor: Vendor, data: CpuIdResult) -> Self {
24 Self {
25 vendor,
26 eax: data.eax,
27 ebx: data.ebx,
28 ecx: ExtendedFunctionInfoEcx::from_bits_truncate(data.ecx),
29 edx: ExtendedFunctionInfoEdx::from_bits_truncate(data.edx),
30 }
31 }
32
33 pub fn extended_signature(&self) -> u32 {
46 self.eax
47 }
48
49 pub fn pkg_type(&self) -> u32 {
57 get_bits(self.ebx, 28, 31)
58 }
59
60 pub fn brand_id(&self) -> u32 {
68 get_bits(self.ebx, 0, 15)
69 }
70
71 pub fn has_lahf_sahf(&self) -> bool {
76 self.ecx.contains(ExtendedFunctionInfoEcx::LAHF_SAHF)
77 }
78
79 pub fn has_cmp_legacy(&self) -> bool {
84 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::CMP_LEGACY)
85 }
86
87 pub fn has_svm(&self) -> bool {
92 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::SVM)
93 }
94
95 pub fn has_ext_apic_space(&self) -> bool {
103 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::EXT_APIC_SPACE)
104 }
105
106 pub fn has_alt_mov_cr8(&self) -> bool {
111 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::ALTMOVCR8)
112 }
113
114 pub fn has_lzcnt(&self) -> bool {
123 self.ecx.contains(ExtendedFunctionInfoEcx::LZCNT)
124 }
125
126 pub fn has_sse4a(&self) -> bool {
133 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::SSE4A)
134 }
135
136 pub fn has_misaligned_sse_mode(&self) -> bool {
142 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::MISALIGNSSE)
143 }
144
145 pub fn has_prefetchw(&self) -> bool {
153 self.ecx.contains(ExtendedFunctionInfoEcx::PREFETCHW)
154 }
155
156 pub fn has_osvw(&self) -> bool {
161 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::OSVW)
162 }
163
164 pub fn has_ibs(&self) -> bool {
169 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::IBS)
170 }
171
172 pub fn has_xop(&self) -> bool {
177 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::XOP)
178 }
179
180 pub fn has_skinit(&self) -> bool {
188 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::SKINIT)
189 }
190
191 pub fn has_wdt(&self) -> bool {
198 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::WDT)
199 }
200
201 pub fn has_lwp(&self) -> bool {
206 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::LWP)
207 }
208
209 pub fn has_fma4(&self) -> bool {
214 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::FMA4)
215 }
216
217 pub fn has_tbm(&self) -> bool {
222 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::TBM)
223 }
224
225 pub fn has_topology_extensions(&self) -> bool {
232 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::TOPEXT)
233 }
234
235 pub fn has_perf_cntr_extensions(&self) -> bool {
242 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::PERFCTREXT)
243 }
244
245 pub fn has_nb_perf_cntr_extensions(&self) -> bool {
252 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::PERFCTREXTNB)
253 }
254
255 pub fn has_data_access_bkpt_extension(&self) -> bool {
262 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::DATABRKPEXT)
263 }
264
265 pub fn has_perf_tsc(&self) -> bool {
272 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::PERFTSC)
273 }
274
275 pub fn has_perf_cntr_llc_extensions(&self) -> bool {
280 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::PERFCTREXTLLC)
281 }
282
283 pub fn has_monitorx_mwaitx(&self) -> bool {
288 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::MONITORX)
289 }
290
291 pub fn has_addr_mask_extension(&self) -> bool {
296 self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::ADDRMASKEXT)
297 }
298
299 pub fn has_syscall_sysret(&self) -> bool {
304 self.edx.contains(ExtendedFunctionInfoEdx::SYSCALL_SYSRET)
305 }
306
307 pub fn has_execute_disable(&self) -> bool {
312 self.edx.contains(ExtendedFunctionInfoEdx::EXECUTE_DISABLE)
313 }
314
315 pub fn has_mmx_extensions(&self) -> bool {
320 self.vendor == Vendor::Amd && self.edx.contains(ExtendedFunctionInfoEdx::MMXEXT)
321 }
322
323 pub fn has_fast_fxsave_fxstor(&self) -> bool {
328 self.vendor == Vendor::Amd && self.edx.contains(ExtendedFunctionInfoEdx::FFXSR)
329 }
330
331 pub fn has_1gib_pages(&self) -> bool {
336 self.edx.contains(ExtendedFunctionInfoEdx::GIB_PAGES)
337 }
338
339 pub fn has_rdtscp(&self) -> bool {
344 self.edx.contains(ExtendedFunctionInfoEdx::RDTSCP)
345 }
346
347 pub fn has_64bit_mode(&self) -> bool {
352 self.edx.contains(ExtendedFunctionInfoEdx::I64BIT_MODE)
353 }
354
355 pub fn has_amd_3dnow_extensions(&self) -> bool {
360 self.vendor == Vendor::Amd && self.edx.contains(ExtendedFunctionInfoEdx::THREEDNOWEXT)
361 }
362
363 pub fn has_3dnow(&self) -> bool {
368 self.vendor == Vendor::Amd && self.edx.contains(ExtendedFunctionInfoEdx::THREEDNOW)
369 }
370}
371
372impl Debug for ExtendedProcessorFeatureIdentifiers {
373 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
374 let mut ds = f.debug_struct("ExtendedProcessorFeatureIdentifiers");
375 ds.field("extended_signature", &self.extended_signature());
376
377 if self.vendor == Vendor::Amd {
378 ds.field("pkg_type", &self.pkg_type());
379 ds.field("brand_id", &self.brand_id());
380 }
381 ds.field("ecx_features", &self.ecx);
382 ds.field("edx_features", &self.edx);
383 ds.finish()
384 }
385}
386
387bitflags! {
388 #[repr(transparent)]
389 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
390 struct ExtendedFunctionInfoEcx: u32 {
391 const LAHF_SAHF = 1 << 0;
392 const CMP_LEGACY = 1 << 1;
393 const SVM = 1 << 2;
394 const EXT_APIC_SPACE = 1 << 3;
395 const ALTMOVCR8 = 1 << 4;
396 const LZCNT = 1 << 5;
397 const SSE4A = 1 << 6;
398 const MISALIGNSSE = 1 << 7;
399 const PREFETCHW = 1 << 8;
400 const OSVW = 1 << 9;
401 const IBS = 1 << 10;
402 const XOP = 1 << 11;
403 const SKINIT = 1 << 12;
404 const WDT = 1 << 13;
405 const LWP = 1 << 15;
406 const FMA4 = 1 << 16;
407 const TBM = 1 << 21;
408 const TOPEXT = 1 << 22;
409 const PERFCTREXT = 1 << 23;
410 const PERFCTREXTNB = 1 << 24;
411 const DATABRKPEXT = 1 << 26;
412 const PERFTSC = 1 << 27;
413 const PERFCTREXTLLC = 1 << 28;
414 const MONITORX = 1 << 29;
415 const ADDRMASKEXT = 1 << 30;
416 }
417}
418
419bitflags! {
420 #[repr(transparent)]
421 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
422 struct ExtendedFunctionInfoEdx: u32 {
423 const SYSCALL_SYSRET = 1 << 11;
424 const EXECUTE_DISABLE = 1 << 20;
425 const MMXEXT = 1 << 22;
426 const FFXSR = 1 << 24;
427 const GIB_PAGES = 1 << 26;
428 const RDTSCP = 1 << 27;
429 const I64BIT_MODE = 1 << 29;
430 const THREEDNOWEXT = 1 << 30;
431 const THREEDNOW = 1 << 31;
432 }
433}
434
435pub struct ProcessorBrandString {
442 data: [CpuIdResult; 3],
443}
444
445impl ProcessorBrandString {
446 pub(crate) fn new(data: [CpuIdResult; 3]) -> Self {
447 Self { data }
448 }
449
450 pub fn as_str(&self) -> &str {
455 let slice: &[u8] = unsafe {
458 slice::from_raw_parts(
459 self.data.as_ptr() as *const u8,
460 self.data.len() * size_of::<CpuIdResult>(),
461 )
462 };
463
464 let slice = slice.split(|&x| x == 0).next().unwrap();
466 str::from_utf8(slice)
467 .unwrap_or("Invalid Processor Brand String")
468 .trim()
469 }
470}
471
472impl Debug for ProcessorBrandString {
473 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
474 f.debug_struct("ProcessorBrandString")
475 .field("as_str", &self.as_str())
476 .finish()
477 }
478}
479
480#[derive(PartialEq, Eq, Debug)]
485pub struct L1CacheTlbInfo {
486 eax: u32,
487 ebx: u32,
488 ecx: u32,
489 edx: u32,
490}
491
492impl L1CacheTlbInfo {
493 pub(crate) fn new(data: CpuIdResult) -> Self {
494 Self {
495 eax: data.eax,
496 ebx: data.ebx,
497 ecx: data.ecx,
498 edx: data.edx,
499 }
500 }
501
502 pub fn dtlb_2m_4m_associativity(&self) -> Associativity {
504 let assoc_bits = get_bits(self.eax, 24, 31) as u8;
505 Associativity::for_l1(assoc_bits)
506 }
507
508 pub fn dtlb_2m_4m_size(&self) -> u8 {
514 get_bits(self.eax, 16, 23) as u8
515 }
516
517 pub fn itlb_2m_4m_associativity(&self) -> Associativity {
519 let assoc_bits = get_bits(self.eax, 8, 15) as u8;
520 Associativity::for_l1(assoc_bits)
521 }
522
523 pub fn itlb_2m_4m_size(&self) -> u8 {
529 get_bits(self.eax, 0, 7) as u8
530 }
531
532 pub fn dtlb_4k_associativity(&self) -> Associativity {
534 let assoc_bits = get_bits(self.ebx, 24, 31) as u8;
535 Associativity::for_l1(assoc_bits)
536 }
537
538 pub fn dtlb_4k_size(&self) -> u8 {
540 get_bits(self.ebx, 16, 23) as u8
541 }
542
543 pub fn itlb_4k_associativity(&self) -> Associativity {
545 let assoc_bits = get_bits(self.ebx, 8, 15) as u8;
546 Associativity::for_l1(assoc_bits)
547 }
548
549 pub fn itlb_4k_size(&self) -> u8 {
551 get_bits(self.ebx, 0, 7) as u8
552 }
553
554 pub fn dcache_size(&self) -> u8 {
556 get_bits(self.ecx, 24, 31) as u8
557 }
558
559 pub fn dcache_associativity(&self) -> Associativity {
561 let assoc_bits = get_bits(self.ecx, 16, 23) as u8;
562 Associativity::for_l1(assoc_bits)
563 }
564
565 pub fn dcache_lines_per_tag(&self) -> u8 {
567 get_bits(self.ecx, 8, 15) as u8
568 }
569
570 pub fn dcache_line_size(&self) -> u8 {
572 get_bits(self.ecx, 0, 7) as u8
573 }
574
575 pub fn icache_size(&self) -> u8 {
577 get_bits(self.edx, 24, 31) as u8
578 }
579
580 pub fn icache_associativity(&self) -> Associativity {
582 let assoc_bits = get_bits(self.edx, 16, 23) as u8;
583 Associativity::for_l1(assoc_bits)
584 }
585
586 pub fn icache_lines_per_tag(&self) -> u8 {
588 get_bits(self.edx, 8, 15) as u8
589 }
590
591 pub fn icache_line_size(&self) -> u8 {
593 get_bits(self.edx, 0, 7) as u8
594 }
595}
596
597#[derive(PartialEq, Eq, Debug)]
602pub struct L2And3CacheTlbInfo {
603 eax: u32,
604 ebx: u32,
605 ecx: u32,
606 edx: u32,
607}
608
609impl L2And3CacheTlbInfo {
610 pub(crate) fn new(data: CpuIdResult) -> Self {
611 Self {
612 eax: data.eax,
613 ebx: data.ebx,
614 ecx: data.ecx,
615 edx: data.edx,
616 }
617 }
618
619 pub fn dtlb_2m_4m_associativity(&self) -> Associativity {
624 let assoc_bits = get_bits(self.eax, 28, 31) as u8;
625 Associativity::for_l2(assoc_bits)
626 }
627
628 pub fn dtlb_2m_4m_size(&self) -> u16 {
637 get_bits(self.eax, 16, 27) as u16
638 }
639
640 pub fn itlb_2m_4m_associativity(&self) -> Associativity {
645 let assoc_bits = get_bits(self.eax, 12, 15) as u8;
646 Associativity::for_l2(assoc_bits)
647 }
648
649 pub fn itlb_2m_4m_size(&self) -> u16 {
658 get_bits(self.eax, 0, 11) as u16
659 }
660
661 pub fn dtlb_4k_associativity(&self) -> Associativity {
666 let assoc_bits = get_bits(self.ebx, 28, 31) as u8;
667 Associativity::for_l2(assoc_bits)
668 }
669
670 pub fn dtlb_4k_size(&self) -> u16 {
675 get_bits(self.ebx, 16, 27) as u16
676 }
677
678 pub fn itlb_4k_associativity(&self) -> Associativity {
683 let assoc_bits = get_bits(self.ebx, 12, 15) as u8;
684 Associativity::for_l2(assoc_bits)
685 }
686
687 pub fn itlb_4k_size(&self) -> u16 {
692 get_bits(self.ebx, 0, 11) as u16
693 }
694
695 pub fn l2cache_line_size(&self) -> u8 {
700 get_bits(self.ecx, 0, 7) as u8
701 }
702
703 pub fn l2cache_lines_per_tag(&self) -> u8 {
708 get_bits(self.ecx, 8, 11) as u8
709 }
710
711 pub fn l2cache_associativity(&self) -> Associativity {
716 let assoc_bits = get_bits(self.ecx, 12, 15) as u8;
717 Associativity::for_l2(assoc_bits)
718 }
719
720 pub fn l2cache_size(&self) -> u16 {
725 get_bits(self.ecx, 16, 31) as u16
726 }
727
728 pub fn l3cache_line_size(&self) -> u8 {
733 get_bits(self.edx, 0, 7) as u8
734 }
735
736 pub fn l3cache_lines_per_tag(&self) -> u8 {
741 get_bits(self.edx, 8, 11) as u8
742 }
743
744 pub fn l3cache_associativity(&self) -> Associativity {
749 let assoc_bits = get_bits(self.edx, 12, 15) as u8;
750 Associativity::for_l3(assoc_bits)
751 }
752
753 pub fn l3cache_size(&self) -> u16 {
760 get_bits(self.edx, 18, 31) as u16
761 }
762}
763
764#[derive(PartialEq, Eq, Debug)]
766pub enum Associativity {
767 Disabled,
768 DirectMapped,
769 NWay(u8),
770 FullyAssociative,
771 Unknown,
772}
773
774impl Display for Associativity {
775 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
776 let s = match self {
777 Associativity::Disabled => "Disabled",
778 Associativity::DirectMapped => "Direct mapped",
779 Associativity::NWay(n) => {
780 return write!(f, "NWay({})", n);
781 }
782 Associativity::FullyAssociative => "Fully associative",
783 Associativity::Unknown => "Unknown (check leaf 0x8000_001d)",
784 };
785 f.write_str(s)
786 }
787}
788
789impl Associativity {
790 fn for_l1(n: u8) -> Associativity {
792 match n {
793 0x0 => Associativity::Disabled, 0x1 => Associativity::DirectMapped,
795 0x2..=0xfe => Associativity::NWay(n),
796 0xff => Associativity::FullyAssociative,
797 }
798 }
799
800 fn for_l2(n: u8) -> Associativity {
802 match n {
803 0x0 => Associativity::Disabled,
804 0x1 => Associativity::DirectMapped,
805 0x2 => Associativity::NWay(2),
806 0x4 => Associativity::NWay(4),
807 0x5 => Associativity::NWay(6), 0x6 => Associativity::NWay(8),
809 0x8 => Associativity::NWay(16),
811 0x9 => Associativity::Unknown, 0xa => Associativity::NWay(32),
813 0xb => Associativity::NWay(48),
814 0xc => Associativity::NWay(64),
815 0xd => Associativity::NWay(96),
816 0xe => Associativity::NWay(128),
817 0xF => Associativity::FullyAssociative,
818 _ => Associativity::Unknown,
819 }
820 }
821
822 fn for_l3(n: u8) -> Associativity {
824 Associativity::for_l2(n)
825 }
826}
827
828#[derive(Debug, PartialEq, Eq)]
833pub struct ApmInfo {
834 _eax: u32,
836 ebx: RasCapabilities,
837 ecx: u32,
838 edx: ApmInfoEdx,
839}
840
841impl ApmInfo {
842 pub(crate) fn new(data: CpuIdResult) -> Self {
843 Self {
844 _eax: data.eax,
845 ebx: RasCapabilities::from_bits_truncate(data.ebx),
846 ecx: data.ecx,
847 edx: ApmInfoEdx::from_bits_truncate(data.edx),
848 }
849 }
850
851 pub fn has_mca_overflow_recovery(&self) -> bool {
860 self.ebx.contains(RasCapabilities::MCAOVFLRECOV)
861 }
862
863 pub fn has_succor(&self) -> bool {
872 self.ebx.contains(RasCapabilities::SUCCOR)
873 }
874
875 pub fn has_hwa(&self) -> bool {
882 self.ebx.contains(RasCapabilities::HWA)
883 }
884
885 pub fn cpu_pwr_sample_time_ratio(&self) -> u32 {
893 self.ecx
894 }
895
896 pub fn has_ts(&self) -> bool {
901 self.edx.contains(ApmInfoEdx::TS)
902 }
903
904 pub fn has_freq_id_ctrl(&self) -> bool {
912 self.edx.contains(ApmInfoEdx::FID)
913 }
914
915 pub fn has_volt_id_ctrl(&self) -> bool {
923 self.edx.contains(ApmInfoEdx::VID)
924 }
925
926 pub fn has_thermtrip(&self) -> bool {
931 self.edx.contains(ApmInfoEdx::TTP)
932 }
933
934 pub fn has_tm(&self) -> bool {
939 self.edx.contains(ApmInfoEdx::TM)
940 }
941
942 pub fn has_100mhz_steps(&self) -> bool {
947 self.edx.contains(ApmInfoEdx::MHZSTEPS100)
948 }
949
950 pub fn has_hw_pstate(&self) -> bool {
958 self.edx.contains(ApmInfoEdx::HWPSTATE)
959 }
960
961 pub fn has_invariant_tsc(&self) -> bool {
966 self.edx.contains(ApmInfoEdx::INVTSC)
967 }
968
969 pub fn has_cpb(&self) -> bool {
974 self.edx.contains(ApmInfoEdx::CPB)
975 }
976
977 pub fn has_ro_effective_freq_iface(&self) -> bool {
986 self.edx.contains(ApmInfoEdx::EFFFREQRO)
987 }
988
989 pub fn has_feedback_iface(&self) -> bool {
997 self.edx.contains(ApmInfoEdx::PROCFEEDBACKIF)
998 }
999
1000 pub fn has_power_reporting_iface(&self) -> bool {
1005 self.edx.contains(ApmInfoEdx::PROCPWRREPORT)
1006 }
1007}
1008
1009bitflags! {
1010 #[repr(transparent)]
1011 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1012 struct ApmInfoEdx: u32 {
1013 const TS = 1 << 0;
1014 const FID = 1 << 1;
1015 const VID = 1 << 2;
1016 const TTP = 1 << 3;
1017 const TM = 1 << 4;
1018 const MHZSTEPS100 = 1 << 6;
1019 const HWPSTATE = 1 << 7;
1020 const INVTSC = 1 << 8;
1021 const CPB = 1 << 9;
1022 const EFFFREQRO = 1 << 10;
1023 const PROCFEEDBACKIF = 1 << 11;
1024 const PROCPWRREPORT = 1 << 12;
1025 }
1026}
1027
1028bitflags! {
1029 #[repr(transparent)]
1030 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1031 struct RasCapabilities: u32 {
1032 const MCAOVFLRECOV = 1 << 0;
1033 const SUCCOR = 1 << 1;
1034 const HWA = 1 << 2;
1035 }
1036}
1037
1038#[derive(PartialEq, Eq)]
1048pub struct ProcessorCapacityAndFeatureInfo {
1049 eax: u32,
1050 ebx: ProcessorCapacityAndFeatureEbx,
1051 ecx: u32,
1052 edx: u32,
1053}
1054
1055impl ProcessorCapacityAndFeatureInfo {
1056 pub(crate) fn new(data: CpuIdResult) -> Self {
1057 Self {
1058 eax: data.eax,
1059 ebx: ProcessorCapacityAndFeatureEbx::from_bits_truncate(data.ebx),
1060 ecx: data.ecx,
1061 edx: data.edx,
1062 }
1063 }
1064
1065 pub fn physical_address_bits(&self) -> u8 {
1070 get_bits(self.eax, 0, 7) as u8
1071 }
1072
1073 pub fn linear_address_bits(&self) -> u8 {
1078 get_bits(self.eax, 8, 15) as u8
1079 }
1080
1081 pub fn guest_physical_address_bits(&self) -> u8 {
1090 get_bits(self.eax, 16, 23) as u8
1091 }
1092
1093 pub fn has_cl_zero(&self) -> bool {
1098 self.ebx.contains(ProcessorCapacityAndFeatureEbx::CLZERO)
1099 }
1100
1101 pub fn has_inst_ret_cntr_msr(&self) -> bool {
1106 self.ebx
1107 .contains(ProcessorCapacityAndFeatureEbx::INST_RETCNT_MSR)
1108 }
1109
1110 pub fn has_restore_fp_error_ptrs(&self) -> bool {
1115 self.ebx
1116 .contains(ProcessorCapacityAndFeatureEbx::RSTR_FP_ERR_PTRS)
1117 }
1118
1119 pub fn has_invlpgb(&self) -> bool {
1124 self.ebx.contains(ProcessorCapacityAndFeatureEbx::INVLPGB)
1125 }
1126
1127 pub fn has_rdpru(&self) -> bool {
1132 self.ebx.contains(ProcessorCapacityAndFeatureEbx::RDPRU)
1133 }
1134
1135 pub fn has_mcommit(&self) -> bool {
1140 self.ebx.contains(ProcessorCapacityAndFeatureEbx::MCOMMIT)
1141 }
1142
1143 pub fn has_wbnoinvd(&self) -> bool {
1148 self.ebx.contains(ProcessorCapacityAndFeatureEbx::WBNOINVD)
1149 }
1150
1151 pub fn has_int_wbinvd(&self) -> bool {
1156 self.ebx
1157 .contains(ProcessorCapacityAndFeatureEbx::INT_WBINVD)
1158 }
1159
1160 pub fn has_unsupported_efer_lmsle(&self) -> bool {
1165 self.ebx
1166 .contains(ProcessorCapacityAndFeatureEbx::EFER_LMSLE_UNSUPP)
1167 }
1168
1169 pub fn has_invlpgb_nested(&self) -> bool {
1174 self.ebx
1175 .contains(ProcessorCapacityAndFeatureEbx::INVLPGB_NESTED)
1176 }
1177
1178 pub fn perf_tsc_size(&self) -> usize {
1185 let s = get_bits(self.ecx, 16, 17) as u8;
1186 match s & 0b11 {
1187 0b00 => 40,
1188 0b01 => 48,
1189 0b10 => 56,
1190 0b11 => 64,
1191 _ => unreachable!("AND with 0b11 in match"),
1192 }
1193 }
1194
1195 pub fn apic_id_size(&self) -> u8 {
1204 get_bits(self.ecx, 12, 15) as u8
1205 }
1206
1207 pub fn maximum_logical_processors(&self) -> usize {
1217 usize::pow(2, self.apic_id_size() as u32)
1218 }
1219
1220 pub fn num_phys_threads(&self) -> usize {
1225 get_bits(self.ecx, 0, 7) as usize + 1
1226 }
1227
1228 pub fn invlpgb_max_pages(&self) -> u16 {
1233 get_bits(self.edx, 0, 15) as u16
1234 }
1235
1236 pub fn max_rdpru_id(&self) -> u16 {
1241 get_bits(self.edx, 16, 31) as u16
1242 }
1243}
1244
1245impl Debug for ProcessorCapacityAndFeatureInfo {
1246 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1247 f.debug_struct("ProcessorCapacityAndFeatureInfo")
1248 .field("physical_address_bits", &self.physical_address_bits())
1249 .field("linear_address_bits", &self.linear_address_bits())
1250 .field(
1251 "guest_physical_address_bits",
1252 &self.guest_physical_address_bits(),
1253 )
1254 .field("has_cl_zero", &self.has_cl_zero())
1255 .field("has_inst_ret_cntr_msr", &self.has_inst_ret_cntr_msr())
1256 .field(
1257 "has_restore_fp_error_ptrs",
1258 &self.has_restore_fp_error_ptrs(),
1259 )
1260 .field("has_invlpgb", &self.has_invlpgb())
1261 .field("has_rdpru", &self.has_rdpru())
1262 .field("has_mcommit", &self.has_mcommit())
1263 .field("has_wbnoinvd", &self.has_wbnoinvd())
1264 .field("has_int_wbinvd", &self.has_int_wbinvd())
1265 .field(
1266 "has_unsupported_efer_lmsle",
1267 &self.has_unsupported_efer_lmsle(),
1268 )
1269 .field("has_invlpgb_nested", &self.has_invlpgb_nested())
1270 .field("perf_tsc_size", &self.perf_tsc_size())
1271 .field("apic_id_size", &self.apic_id_size())
1272 .field(
1273 "maximum_logical_processors",
1274 &self.maximum_logical_processors(),
1275 )
1276 .field("num_phys_threads", &self.num_phys_threads())
1277 .field("invlpgb_max_pages", &self.invlpgb_max_pages())
1278 .field("max_rdpru_id", &self.max_rdpru_id())
1279 .finish()
1280 }
1281}
1282
1283bitflags! {
1284 #[repr(transparent)]
1285 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1286 struct ProcessorCapacityAndFeatureEbx: u32 {
1287 const CLZERO = 1 << 0;
1288 const INST_RETCNT_MSR = 1 << 1;
1289 const RSTR_FP_ERR_PTRS = 1 << 2;
1290 const INVLPGB = 1 << 3;
1291 const RDPRU = 1 << 4;
1292 const MCOMMIT = 1 << 8;
1293 const WBNOINVD = 1 << 9;
1294 const INT_WBINVD = 1 << 13;
1295 const EFER_LMSLE_UNSUPP = 1 << 20;
1296 const INVLPGB_NESTED = 1 << 21;
1297 }
1298}
1299
1300#[derive(PartialEq, Eq, Debug)]
1309pub struct SvmFeatures {
1310 eax: u32,
1311 ebx: u32,
1312 _ecx: u32,
1314 edx: SvmFeaturesEdx,
1315}
1316
1317impl SvmFeatures {
1318 pub(crate) fn new(data: CpuIdResult) -> Self {
1319 Self {
1320 eax: data.eax,
1321 ebx: data.ebx,
1322 _ecx: data.ecx,
1323 edx: SvmFeaturesEdx::from_bits_truncate(data.edx),
1324 }
1325 }
1326
1327 pub fn revision(&self) -> u8 {
1329 get_bits(self.eax, 0, 7) as u8
1330 }
1331
1332 pub fn supported_asids(&self) -> u32 {
1334 self.ebx
1335 }
1336
1337 pub fn has_nested_paging(&self) -> bool {
1339 self.edx.contains(SvmFeaturesEdx::NP)
1340 }
1341
1342 pub fn has_lbr_virtualization(&self) -> bool {
1344 self.edx.contains(SvmFeaturesEdx::LBR_VIRT)
1345 }
1346
1347 pub fn has_svm_lock(&self) -> bool {
1349 self.edx.contains(SvmFeaturesEdx::SVML)
1350 }
1351
1352 pub fn has_nrip(&self) -> bool {
1354 self.edx.contains(SvmFeaturesEdx::NRIPS)
1355 }
1356
1357 pub fn has_tsc_rate_msr(&self) -> bool {
1359 self.edx.contains(SvmFeaturesEdx::TSC_RATE_MSR)
1360 }
1361
1362 pub fn has_vmcb_clean_bits(&self) -> bool {
1364 self.edx.contains(SvmFeaturesEdx::VMCB_CLEAN)
1365 }
1366
1367 pub fn has_flush_by_asid(&self) -> bool {
1372 self.edx.contains(SvmFeaturesEdx::FLUSH_BY_ASID)
1373 }
1374
1375 pub fn has_decode_assists(&self) -> bool {
1377 self.edx.contains(SvmFeaturesEdx::DECODE_ASSISTS)
1378 }
1379
1380 pub fn has_pause_filter(&self) -> bool {
1382 self.edx.contains(SvmFeaturesEdx::PAUSE_FILTER)
1383 }
1384
1385 pub fn has_pause_filter_threshold(&self) -> bool {
1387 self.edx.contains(SvmFeaturesEdx::PAUSE_FILTER_THRESHOLD)
1388 }
1389
1390 pub fn has_avic(&self) -> bool {
1392 self.edx.contains(SvmFeaturesEdx::AVIC)
1393 }
1394
1395 pub fn has_vmsave_virtualization(&self) -> bool {
1397 self.edx.contains(SvmFeaturesEdx::VMSAVE_VIRT)
1398 }
1399
1400 pub fn has_gif(&self) -> bool {
1402 self.edx.contains(SvmFeaturesEdx::VGIF)
1403 }
1404
1405 pub fn has_gmet(&self) -> bool {
1407 self.edx.contains(SvmFeaturesEdx::GMET)
1408 }
1409
1410 pub fn has_sss_check(&self) -> bool {
1412 self.edx.contains(SvmFeaturesEdx::SSS_CHECK)
1413 }
1414
1415 pub fn has_spec_ctrl(&self) -> bool {
1417 self.edx.contains(SvmFeaturesEdx::SPEC_CTRL)
1418 }
1419
1420 pub fn has_host_mce_override(&self) -> bool {
1423 self.edx.contains(SvmFeaturesEdx::HOST_MCE_OVERRIDE)
1424 }
1425
1426 pub fn has_tlb_ctrl(&self) -> bool {
1429 self.edx.contains(SvmFeaturesEdx::TLB_CTL)
1430 }
1431}
1432
1433bitflags! {
1434 #[repr(transparent)]
1435 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1436 struct SvmFeaturesEdx: u32 {
1437 const NP = 1 << 0;
1438 const LBR_VIRT = 1 << 1;
1439 const SVML = 1 << 2;
1440 const NRIPS = 1 << 3;
1441 const TSC_RATE_MSR = 1 << 4;
1442 const VMCB_CLEAN = 1 << 5;
1443 const FLUSH_BY_ASID = 1 << 6;
1444 const DECODE_ASSISTS = 1 << 7;
1445 const PAUSE_FILTER = 1 << 10;
1446 const PAUSE_FILTER_THRESHOLD = 1 << 12;
1447 const AVIC = 1 << 13;
1448 const VMSAVE_VIRT = 1 << 15;
1449 const VGIF = 1 << 16;
1450 const GMET = 1 << 17;
1451 const SSS_CHECK = 1 << 19;
1452 const SPEC_CTRL = 1 << 20;
1453 const HOST_MCE_OVERRIDE = 1 << 23;
1454 const TLB_CTL = 1 << 24;
1455 }
1456}
1457
1458#[derive(PartialEq, Eq, Debug)]
1463pub struct Tlb1gbPageInfo {
1464 eax: u32,
1465 ebx: u32,
1466 _ecx: u32,
1468 _edx: u32,
1470}
1471
1472impl Tlb1gbPageInfo {
1473 pub(crate) fn new(data: CpuIdResult) -> Self {
1474 Self {
1475 eax: data.eax,
1476 ebx: data.ebx,
1477 _ecx: data.ecx,
1478 _edx: data.edx,
1479 }
1480 }
1481
1482 pub fn dtlb_l1_1gb_associativity(&self) -> Associativity {
1484 let assoc_bits = get_bits(self.eax, 28, 31) as u8;
1485 Associativity::for_l2(assoc_bits)
1486 }
1487
1488 pub fn dtlb_l1_1gb_size(&self) -> u8 {
1490 get_bits(self.eax, 16, 27) as u8
1491 }
1492
1493 pub fn itlb_l1_1gb_associativity(&self) -> Associativity {
1495 let assoc_bits = get_bits(self.eax, 12, 15) as u8;
1496 Associativity::for_l2(assoc_bits)
1497 }
1498
1499 pub fn itlb_l1_1gb_size(&self) -> u8 {
1501 get_bits(self.eax, 0, 11) as u8
1502 }
1503
1504 pub fn dtlb_l2_1gb_associativity(&self) -> Associativity {
1506 let assoc_bits = get_bits(self.ebx, 28, 31) as u8;
1507 Associativity::for_l2(assoc_bits)
1508 }
1509
1510 pub fn dtlb_l2_1gb_size(&self) -> u8 {
1512 get_bits(self.ebx, 16, 27) as u8
1513 }
1514
1515 pub fn itlb_l2_1gb_associativity(&self) -> Associativity {
1517 let assoc_bits = get_bits(self.ebx, 12, 15) as u8;
1518 Associativity::for_l2(assoc_bits)
1519 }
1520
1521 pub fn itlb_l2_1gb_size(&self) -> u8 {
1523 get_bits(self.ebx, 0, 11) as u8
1524 }
1525}
1526
1527#[derive(PartialEq, Eq, Debug)]
1532pub struct PerformanceOptimizationInfo {
1533 eax: PerformanceOptimizationInfoEax,
1534 _ebx: u32,
1536 _ecx: u32,
1538 _edx: u32,
1540}
1541
1542impl PerformanceOptimizationInfo {
1543 pub(crate) fn new(data: CpuIdResult) -> Self {
1544 Self {
1545 eax: PerformanceOptimizationInfoEax::from_bits_truncate(data.eax),
1546 _ebx: data.ebx,
1547 _ecx: data.ecx,
1548 _edx: data.edx,
1549 }
1550 }
1551
1552 pub fn has_fp128(&self) -> bool {
1554 self.eax.contains(PerformanceOptimizationInfoEax::FP128)
1555 }
1556
1557 pub fn has_movu(&self) -> bool {
1560 self.eax.contains(PerformanceOptimizationInfoEax::MOVU)
1561 }
1562
1563 pub fn has_fp256(&self) -> bool {
1565 self.eax.contains(PerformanceOptimizationInfoEax::FP256)
1566 }
1567}
1568
1569bitflags! {
1570 #[repr(transparent)]
1571 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1572 struct PerformanceOptimizationInfoEax: u32 {
1573 const FP128 = 1 << 0;
1574 const MOVU = 1 << 1;
1575 const FP256 = 1 << 2;
1576 }
1577}
1578
1579#[derive(PartialEq, Eq)]
1584pub struct ProcessorTopologyInfo {
1585 eax: u32,
1586 ebx: u32,
1587 ecx: u32,
1588 _edx: u32,
1590}
1591
1592impl ProcessorTopologyInfo {
1593 pub(crate) fn new(data: CpuIdResult) -> Self {
1594 Self {
1595 eax: data.eax,
1596 ebx: data.ebx,
1597 ecx: data.ecx,
1598 _edx: data.edx,
1599 }
1600 }
1601
1602 pub fn x2apic_id(&self) -> u32 {
1604 self.eax
1605 }
1606
1607 pub fn core_id(&self) -> u8 {
1612 get_bits(self.ebx, 0, 7) as u8
1613 }
1614
1615 pub fn threads_per_core(&self) -> u8 {
1620 get_bits(self.ebx, 8, 15) as u8 + 1
1621 }
1622
1623 pub fn node_id(&self) -> u8 {
1625 get_bits(self.ecx, 0, 7) as u8
1626 }
1627
1628 pub fn nodes_per_processor(&self) -> u8 {
1630 get_bits(self.ecx, 8, 10) as u8 + 1
1631 }
1632}
1633
1634impl Debug for ProcessorTopologyInfo {
1635 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1636 f.debug_struct("ProcessorTopologyInfo")
1637 .field("x2apic_id", &self.x2apic_id())
1638 .field("core_id", &self.core_id())
1639 .field("threads_per_core", &self.threads_per_core())
1640 .field("node_id", &self.node_id())
1641 .field("nodes_per_processor", &self.nodes_per_processor())
1642 .finish()
1643 }
1644}
1645
1646#[derive(Debug, PartialEq, Eq)]
1651pub struct MemoryEncryptionInfo {
1652 eax: MemoryEncryptionInfoEax,
1653 ebx: u32,
1654 ecx: u32,
1655 edx: u32,
1656}
1657
1658impl MemoryEncryptionInfo {
1659 pub(crate) fn new(data: CpuIdResult) -> Self {
1660 Self {
1661 eax: MemoryEncryptionInfoEax::from_bits_truncate(data.eax),
1662 ebx: data.ebx,
1663 ecx: data.ecx,
1664 edx: data.edx,
1665 }
1666 }
1667
1668 pub fn has_sme(&self) -> bool {
1670 self.eax.contains(MemoryEncryptionInfoEax::SME)
1671 }
1672
1673 pub fn has_sev(&self) -> bool {
1675 self.eax.contains(MemoryEncryptionInfoEax::SEV)
1676 }
1677
1678 pub fn has_page_flush_msr(&self) -> bool {
1680 self.eax.contains(MemoryEncryptionInfoEax::PAGE_FLUSH_MSR)
1681 }
1682
1683 pub fn has_sev_es(&self) -> bool {
1685 self.eax.contains(MemoryEncryptionInfoEax::SEV_ES)
1686 }
1687
1688 pub fn has_sev_snp(&self) -> bool {
1690 self.eax.contains(MemoryEncryptionInfoEax::SEV_SNP)
1691 }
1692
1693 pub fn has_vmpl(&self) -> bool {
1695 self.eax.contains(MemoryEncryptionInfoEax::VMPL)
1696 }
1697
1698 pub fn has_hw_enforced_cache_coh(&self) -> bool {
1700 self.eax.contains(MemoryEncryptionInfoEax::HWENFCACHECOH)
1701 }
1702
1703 pub fn has_64bit_mode(&self) -> bool {
1705 self.eax.contains(MemoryEncryptionInfoEax::HOST64)
1706 }
1707
1708 pub fn has_restricted_injection(&self) -> bool {
1710 self.eax.contains(MemoryEncryptionInfoEax::RESTINJECT)
1711 }
1712
1713 pub fn has_alternate_injection(&self) -> bool {
1715 self.eax.contains(MemoryEncryptionInfoEax::ALTINJECT)
1716 }
1717
1718 pub fn has_debug_swap(&self) -> bool {
1720 self.eax.contains(MemoryEncryptionInfoEax::DBGSWP)
1721 }
1722
1723 pub fn has_prevent_host_ibs(&self) -> bool {
1725 self.eax.contains(MemoryEncryptionInfoEax::PREVHOSTIBS)
1726 }
1727
1728 pub fn has_vte(&self) -> bool {
1730 self.eax.contains(MemoryEncryptionInfoEax::VTE)
1731 }
1732
1733 pub fn c_bit_position(&self) -> u8 {
1735 get_bits(self.ebx, 0, 5) as u8
1736 }
1737
1738 pub fn physical_address_reduction(&self) -> u8 {
1740 get_bits(self.ebx, 6, 11) as u8
1741 }
1742
1743 pub fn max_encrypted_guests(&self) -> u32 {
1745 self.ecx
1746 }
1747
1748 pub fn min_sev_no_es_asid(&self) -> u32 {
1750 self.edx
1751 }
1752}
1753
1754bitflags! {
1755 #[repr(transparent)]
1756 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1757 struct MemoryEncryptionInfoEax: u32 {
1758 const SME = 1 << 0;
1759 const SEV = 1 << 1;
1760 const PAGE_FLUSH_MSR = 1 << 2;
1761 const SEV_ES = 1 << 3;
1762 const SEV_SNP = 1 << 4;
1763 const VMPL = 1 << 5;
1764 const HWENFCACHECOH = 1 << 10;
1765 const HOST64 = 1 << 11;
1766 const RESTINJECT = 1 << 12;
1767 const ALTINJECT = 1 << 13;
1768 const DBGSWP = 1 << 14;
1769 const PREVHOSTIBS = 1 << 15;
1770 const VTE = 1 << 16;
1771 }
1772}