cupid/
lib.rs

1#![cfg_attr(not(cpuid_available), allow(dead_code))]
2
3//! ```
4//! extern crate cupid;
5//!
6//! fn main() {
7//!     let information = cupid::master();
8//!     println!("{:#?}", information);
9//!     if let Some(information) = information {
10//!         if information.sse4_2() {
11//!              println!("SSE 4.2 Available");
12//!         }
13//!     }
14//! }
15//! ```
16
17use std::{fmt, slice, str};
18use std::ops::Deref;
19
20#[repr(u32)]
21enum RequestType {
22    BasicInformation                  = 0x00000000,
23    VersionInformation                = 0x00000001,
24    ThermalPowerManagementInformation = 0x00000006,
25    StructuredExtendedInformation     = 0x00000007,
26    ExtendedTopologyEnumeration       = 0x0000000B,
27    ProcessorExtendedState            = 0x0000000D,
28    ExtendedFunctionInformation       = 0x80000000,
29    ExtendedProcessorSignature        = 0x80000001,
30    BrandString1                      = 0x80000002,
31    BrandString2                      = 0x80000003,
32    BrandString3                      = 0x80000004,
33    // reserved                       = 0x80000005,
34    CacheLine                         = 0x80000006,
35    TimeStampCounter                  = 0x80000007,
36    PhysicalAddressSize               = 0x80000008,
37}
38
39fn cpuid(code: RequestType) -> (u32, u32, u32, u32) {
40    cpuid_ext(code, 0x00000000)
41}
42
43#[cfg(engine_std)]
44fn cpuid_ext(code: RequestType, code2: u32) -> (u32, u32, u32, u32) {
45    #[cfg(target_arch = "x86_64")]
46    use std::arch::x86_64::__cpuid_count;
47    #[cfg(target_arch = "x86")]
48    use std::arch::x86::__cpuid_count;
49
50    let r = unsafe { __cpuid_count(code as u32, code2) };
51    (r.eax, r.ebx, r.ecx, r.edx)
52}
53
54#[cfg(engine_c)]
55fn cpuid_ext(code: RequestType, code2: u32) -> (u32, u32, u32, u32) {
56    extern {
57        // This function name encodes an ABI compatibility
58        // version. When we release a new major version of the
59        // crate, this should be bumped to allow co-existing
60        // installations. If we need to change this interface,
61        // we should likely bump this version as well!
62        fn __cupid_cpuid_shim_0_6(code: u32, code2: u32, output: *mut u32);
63    }
64
65    let mut ret = [0; 4];
66
67    unsafe {
68        __cupid_cpuid_shim_0_6(code as u32, code2, ret.as_mut_ptr());
69    }
70
71    (ret[0], ret[1], ret[2], ret[3])
72}
73
74#[cfg(not(cpuid_available))]
75fn cpuid_ext(_code: RequestType, _code2: u32) -> (u32, u32, u32, u32) {
76    unreachable!();
77}
78
79/// The main entrypoint to the CPU information
80pub fn master() -> Option<Master> {
81    #[cfg(cpuid_available)] {
82        Some(Master::new())
83    }
84    #[cfg(not(cpuid_available))] {
85        None
86    }
87}
88
89// This matches the Intel Architecture guide, with bits 31 -> 0.
90// The bit positions are inclusive.
91fn bits_of(val: u32, start_bit: u8, end_bit: u8) -> u32 {
92    let mut silly = 0;
93
94    for _ in start_bit..end_bit+1 {
95        silly <<= 1;
96        silly |= 1;
97    }
98
99    (val >> start_bit) & silly
100}
101
102fn as_bytes(v: &u32) -> &[u8] {
103    let start = v as *const u32 as *const u8;
104    // TODO: use u32::BYTES
105    unsafe { slice::from_raw_parts(start, 4) }
106}
107
108macro_rules! bit {
109    ($reg:ident, {$($idx:expr => $name:ident),+}) => {
110        $(pub fn $name(self) -> bool {
111            ((self.$reg >> $idx) & 1) != 0
112        })+
113    }
114}
115
116macro_rules! dump {
117    ($me:expr, $f: expr, $sname:expr, {$($name:ident),+}) => {
118        $f.debug_struct($sname)
119            $(.field(stringify!($name), &$me.$name()))+
120            .finish()
121    }
122}
123
124macro_rules! delegate_flag {
125    ($item:ident, {$($name:ident),+}) => {
126        $(pub fn $name(&self) -> bool {
127            self.$item.map(|i| i.$name()).unwrap_or(false)
128        })+
129    }
130}
131
132macro_rules! master_attr_reader {
133    ($name:ident, $kind:ty) => {
134        pub fn $name(&self) -> Option<&$kind> {
135            self.$name.as_ref()
136        }
137    }
138}
139
140#[derive(Copy, Clone)]
141pub struct VersionInformation {
142    eax: u32,
143    ebx: u32,
144    ecx: u32,
145    edx: u32,
146}
147
148impl VersionInformation {
149    fn new() -> VersionInformation {
150        let (a, b, c, d) = cpuid(RequestType::VersionInformation);
151        VersionInformation { eax: a, ebx: b, ecx: c, edx: d }
152    }
153
154    pub fn family_id(self) -> u32 {
155        let family_id = bits_of(self.eax, 8, 11);
156        let extended_family_id = bits_of(self.eax, 20, 27);
157
158        if family_id != 0x0F {
159            family_id
160        } else {
161            extended_family_id + family_id
162        }
163    }
164
165    pub fn model_id(self) -> u32 {
166        let family_id = self.family_id();
167        let model_id = bits_of(self.eax, 4, 7);
168        let extended_model_id = bits_of(self.eax, 16, 19);
169
170        if family_id == 0x06 || family_id == 0x0F {
171            (extended_model_id << 4) + model_id
172        } else {
173            model_id
174        }
175    }
176
177    pub fn stepping(self) -> u32 {
178        bits_of(self.eax, 0, 3)
179    }
180
181    fn processor_signature(self) -> u32 {
182        self.eax
183    }
184
185    /// Maximum number of addressable IDs for logical processors in this physical package.
186    ///
187    /// The number of unique initial APIC IDs reserved for addressing different logical processors in a physical package.
188    /// This field is only valid if `VersionInformation::htt()` is true.
189    pub fn max_logical_processor_ids(self) -> Option<u8> {
190        if self.htt() {
191            Some((self.ebx >> 16) as u8)
192        } else {
193            None
194        }
195    }
196
197    /// This number is assigned to the local APIC on the processor during power up.
198    ///
199    /// This field was introduced in the Pentium 4 processor.
200    ///
201    /// ### Notes
202    ///
203    /// Unless the program is constrained to run on a single
204    /// processor, multiple consecutive calls to this function can
205    /// return different values.
206    pub fn local_logical_processor_id(self) -> u8 {
207        (self.ebx >> 24) as u8
208    }
209
210    pub fn brand_string(self) -> Option<&'static str> {
211        let brand_index = bits_of(self.ebx, 0, 7);
212        let processor_signature = self.processor_signature();
213
214        match brand_index {
215            0x00 => None,
216            0x01 => Some("Intel(R) Celeron(R)"),
217            0x02 => Some("Intel(R) Pentium(R) III"),
218            0x03 => {
219                if processor_signature == 0x06B1 {
220                    Some("Intel(R) Celeron(R)")
221                } else {
222                    Some("Intel(R) Pentium(R) III Xeon(R)")
223                }
224            },
225            0x04 => Some("Intel(R) Pentium(R) III"),
226            0x06 => Some("Mobile Intel(R) Pentium(R) III-M"),
227            0x07 => Some("Mobile Intel(R) Celeron(R)"),
228            0x08 => Some("Intel(R) Pentium(R) 4"),
229            0x09 => Some("Intel(R) Pentium(R) 4"),
230            0x0A => Some("Intel(R) Celeron(R)"),
231            0x0B => {
232                if processor_signature == 0x0F13 {
233                    Some("Intel(R) Xeon(R) MP")
234                } else {
235                    Some("Intel(R) Xeon(R)")
236                }
237            },
238            0x0C => Some("Intel(R) Xeon(R) MP"),
239            0x0E => {
240                if processor_signature == 0x0F13 {
241                    Some("Intel(R) Xeon(R)")
242                } else {
243                    Some("Mobile Intel(R) Pentium(R) 4-M")
244                }
245            },
246            0x0F => Some("Mobile Intel(R) Celeron(R)"),
247            0x11 => Some("Mobile Genuine Intel(R)"),
248            0x12 => Some("Intel(R) Celeron(R) M"),
249            0x13 => Some("Mobile Intel(R) Celeron(R)"),
250            0x14 => Some("Intel(R) Celeron(R)"),
251            0x15 => Some("Mobile Genuine Intel(R)"),
252            0x16 => Some("Intel(R) Pentium(R) M"),
253            0x17 => Some("Mobile Intel(R) Celeron(R)"),
254            _ => None,
255        }
256    }
257
258    bit!(ecx, {
259         0 => sse3,
260         1 => pclmulqdq,
261         2 => dtes64,
262         3 => monitor,
263         4 => ds_cpl,
264         5 => vmx,
265         6 => smx,
266         7 => eist,
267         8 => tm2,
268         9 => ssse3,
269        10 => cnxt_id,
270        11 => sdbg,
271        12 => fma,
272        13 => cmpxchg16b,
273        14 => xtpr_update_control,
274        15 => pdcm,
275        // 16 - reserved
276        17 => pcid,
277        18 => dca,
278        19 => sse4_1,
279        20 => sse4_2,
280        21 => x2apic,
281        22 => movbe,
282        23 => popcnt,
283        24 => tsc_deadline,
284        25 => aesni,
285        26 => xsave,
286        27 => osxsave,
287        28 => avx,
288        29 => f16c,
289        30 => rdrand
290        // 31 - unused
291    });
292
293    bit!(edx, {
294        0 => fpu,
295        1 => vme,
296        2 => de,
297        3 => pse,
298        4 => tsc,
299        5 => msr,
300        6 => pae,
301        7 => mce,
302        8 => cx8,
303        9 => apic,
304        // 10 - reserved
305        11 => sep,
306        12 => mtrr,
307        13 => pge,
308        14 => mca,
309        15 => cmov,
310        16 => pat,
311        17 => pse_36,
312        18 => psn,
313        19 => clfsh,
314        // 20 - reserved
315        21 => ds,
316        22 => acpi,
317        23 => mmx,
318        24 => fxsr,
319        25 => sse,
320        26 => sse2,
321        27 => ss,
322        28 => htt,
323        29 => tm,
324        // 30 -reserved
325        31 => pbe
326    });
327}
328
329impl fmt::Debug for VersionInformation {
330    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331        dump!(self, f, "VersionInformation", {
332            family_id,
333            model_id,
334            stepping,
335            max_logical_processor_ids,
336            local_logical_processor_id,
337            brand_string,
338            sse3,
339            pclmulqdq,
340            dtes64,
341            monitor,
342            ds_cpl,
343            vmx,
344            smx,
345            eist,
346            tm2,
347            ssse3,
348            cnxt_id,
349            sdbg,
350            fma,
351            cmpxchg16b,
352            xtpr_update_control,
353            pdcm,
354            pcid,
355            dca,
356            sse4_1,
357            sse4_2,
358            x2apic,
359            movbe,
360            popcnt,
361            tsc_deadline,
362            aesni,
363            xsave,
364            osxsave,
365            avx,
366            f16c,
367            rdrand,
368            fpu,
369            vme,
370            de,
371            pse,
372            tsc,
373            msr,
374            pae,
375            mce,
376            cx8,
377            apic,
378            sep,
379            mtrr,
380            pge,
381            mca,
382            cmov,
383            pat,
384            pse_36,
385            psn,
386            clfsh,
387            ds,
388            acpi,
389            mmx,
390            fxsr,
391            sse,
392            sse2,
393            ss,
394            htt,
395            tm,
396            pbe
397        })
398    }
399}
400
401#[derive(Copy,Clone)]
402pub struct ExtendedProcessorSignature {
403    ecx: u32,
404    edx: u32,
405}
406
407impl ExtendedProcessorSignature {
408    fn new() -> ExtendedProcessorSignature {
409        let (_, _, c, d) = cpuid(RequestType::ExtendedProcessorSignature);
410        ExtendedProcessorSignature { ecx: c, edx: d }
411    }
412
413    bit!(ecx, {
414        0 => lahf_sahf_in_64_bit,
415        // 1-4 reserved
416        5 => lzcnt,  // implies Advanced Bit Manipulation (ABM) on AMD
417        6 => sse4a, // AMD only
418        // 7 reserved
419        8 => prefetchw,
420        // 9-20 reserved
421        21 => tbm // AMD only
422        // 22-31 reserved
423    });
424
425    bit!(edx, {
426        // 0-10 reserved
427        11 => syscall_sysret_in_64_bit,
428        // 12-19 reserved
429        20 => execute_disable,
430        // 21-25 reserved
431        26 => gigabyte_pages,
432        27 => rdtscp_and_ia32_tsc_aux,
433        // 28 reserved
434        29 => intel_64_bit_architecture
435        // 30-31 reserved
436    });
437}
438
439impl fmt::Debug for ExtendedProcessorSignature {
440    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
441        dump!(self, f, "ExtendedProcessorSignature", {
442            lahf_sahf_in_64_bit,
443            lzcnt,
444            prefetchw,
445            tbm,
446            sse4a,
447            syscall_sysret_in_64_bit,
448            execute_disable,
449            gigabyte_pages,
450            rdtscp_and_ia32_tsc_aux,
451            intel_64_bit_architecture
452        })
453    }
454}
455
456// 3 calls of 4 registers of 4 bytes
457const BRAND_STRING_LENGTH: usize = 3 * 4 * 4;
458
459pub struct BrandString {
460    bytes: [u8; BRAND_STRING_LENGTH],
461}
462
463impl BrandString {
464    fn new() -> BrandString {
465        fn append_bytes(a: RequestType, bytes: &mut [u8]) {
466            let (a, b, c, d) = cpuid(a);
467
468            let result_bytes =
469                as_bytes(&a).iter()
470                .chain(as_bytes(&b).iter())
471                .chain(as_bytes(&c).iter())
472                .chain(as_bytes(&d).iter());
473
474            for (output, input) in bytes.iter_mut().zip(result_bytes) {
475                *output = *input
476            }
477        }
478
479        let mut brand_string = BrandString { bytes: [0; BRAND_STRING_LENGTH] };
480        append_bytes(RequestType::BrandString1, &mut brand_string.bytes[0..]);
481        append_bytes(RequestType::BrandString2, &mut brand_string.bytes[16..]);
482        append_bytes(RequestType::BrandString3, &mut brand_string.bytes[32..]);
483        brand_string
484    }
485}
486
487impl Clone for BrandString {
488    fn clone(&self) -> Self {
489        let mut bytes = [0; BRAND_STRING_LENGTH];
490        for (d, s) in bytes.iter_mut().zip(self.bytes.iter()) {
491            *d = *s;
492        }
493        BrandString { bytes: bytes }
494    }
495}
496
497impl Deref for BrandString {
498    type Target = str;
499
500    fn deref(&self) -> &str {
501        let nul_terminator = self.bytes.iter().position(|&b| b == 0).unwrap_or(0);
502        let usable_bytes = &self.bytes[..nul_terminator];
503        unsafe { str::from_utf8_unchecked(usable_bytes) }.trim()
504    }
505}
506
507impl fmt::Display for BrandString {
508    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
509        (self as &str).fmt(f)
510    }
511}
512
513impl fmt::Debug for BrandString {
514    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
515        (self as &str).fmt(f)
516    }
517}
518
519#[derive(Copy,Clone)]
520pub struct ThermalPowerManagementInformation {
521    eax: u32,
522    ebx: u32,
523    ecx: u32,
524}
525
526impl ThermalPowerManagementInformation {
527    fn new() -> ThermalPowerManagementInformation {
528        let (a, b, c, _) = cpuid(RequestType::ThermalPowerManagementInformation);
529        ThermalPowerManagementInformation { eax: a, ebx: b, ecx: c }
530    }
531
532    bit!(eax, {
533        0 => digital_temperature_sensor,
534        1 => intel_turbo_boost,
535        2 => arat,
536        // 3 - reserved
537        4 => pln,
538        5 => ecmd,
539        6 => ptm,
540        7 => hwp,
541        8 => hwp_notification,
542        9 => hwp_activity_window,
543        10 => hwp_energy_performance_preference,
544        // 12 - reserved
545        13 => hdc
546    });
547
548    pub fn number_of_interrupt_thresholds(self) -> u32 {
549        bits_of(self.ebx, 0, 3)
550    }
551
552    bit!(ecx, {
553        0 => hardware_coordination_feedback,
554        // 1-2 - reserved
555        3 => performance_energy_bias
556    });
557}
558
559impl fmt::Debug for ThermalPowerManagementInformation {
560    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
561        dump!(self, f, "ThermalPowerManagementInformation", {
562            digital_temperature_sensor,
563            intel_turbo_boost,
564            arat,
565            pln,
566            ecmd,
567            ptm,
568            hwp,
569            hwp_notification,
570            hwp_activity_window,
571            hwp_energy_performance_preference,
572            hdc,
573
574            number_of_interrupt_thresholds,
575
576            hardware_coordination_feedback,
577            performance_energy_bias
578        })
579    }
580}
581
582#[derive(Copy,Clone)]
583pub struct StructuredExtendedInformation {
584    ebx: u32,
585    ecx: u32,
586    edx: u32,
587}
588
589impl StructuredExtendedInformation {
590    fn new() -> StructuredExtendedInformation {
591        let (_, b, c, d) = cpuid(RequestType::StructuredExtendedInformation);
592        StructuredExtendedInformation { ebx: b, ecx: c, edx: d }
593    }
594
595    bit!(ebx, {
596        0 => fsgsbase,
597        1 => ia32_tsc_adjust_msr,
598        // 2 - reserved
599        3 => bmi1,
600        4 => hle,
601        5 => avx2,
602        // 6 - reserved
603        7 => smep,
604        8 => bmi2,
605        9 => enhanced_rep_movsb_stosb,
606        10 => invpcid,
607        11 => rtm,
608        12 => pqm,
609        13 => deprecates_fpu_cs_ds,
610        // 14 - reserved
611        15 => pqe,
612        16 => avx512f,
613        17 => avx512dq,
614        18 => rdseed,
615        19 => adx,
616        20 => smap,
617        21 => avx512_ifma,
618        // 22 - reserved
619        23 => clflushopt,
620        24 => clwb,
621        25 => intel_processor_trace,
622        26 => avx512pf,
623        27 => avx512er,
624        28 => avx512cd,
625        29 => sha,
626        30 => avx512bw,
627        31 => avx512vl
628    });
629
630    bit!(ecx, {
631        0 => prefetchwt1,
632        1 => avx512_vbmi,
633        2 => umip,
634        3 => pku,
635        4 => ospke,
636        // 5 - reserved
637        6 => avx512_vbmi2,
638        // 7 - reserved
639        8 => gfni,
640        9 => vaes,
641        10 => vpclmulqdq,
642        11 => avx512_vnni,
643        12 => avx512_bitalg,
644        // 13 - reserved
645        14 => avx512_vpopcntdq,
646        // 15-16 - reserved
647        // 17-22 - TODO
648        // 23-29 - reserved
649        30 => sgx
650        // 31 - reserved
651    });
652
653    bit!(edx, {
654        // 0-1 - reserved
655        2 => avx512_4vnniw,
656        3 => avx512_4fmaps
657        // 4-31 - reserved
658    });
659}
660
661impl fmt::Debug for StructuredExtendedInformation {
662    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
663        dump!(self, f, "StructuredExtendedInformation", {
664            fsgsbase,
665            ia32_tsc_adjust_msr,
666            bmi1,
667            hle,
668            avx2,
669            smep,
670            bmi2,
671            enhanced_rep_movsb_stosb,
672            invpcid,
673            rtm,
674            pqm,
675            deprecates_fpu_cs_ds,
676            pqe,
677            avx512f,
678            avx512dq,
679            rdseed,
680            adx,
681            smap,
682            avx512_ifma,
683            clflushopt,
684            clwb,
685            intel_processor_trace,
686            avx512pf,
687            avx512er,
688            avx512cd,
689            sha,
690            avx512bw,
691            avx512vl,
692            prefetchwt1,
693            avx512_vbmi,
694            umip,
695            pku,
696            ospke,
697            avx512_vbmi2,
698            gfni,
699            vaes,
700            vpclmulqdq,
701            avx512_vnni,
702            avx512_bitalg,
703            avx512_vpopcntdq,
704            sgx,
705            avx512_4vnniw,
706            avx512_4fmaps
707        })
708    }
709}
710
711#[derive(Clone, Default)]
712pub struct ExtendedTopologyEnumeration {
713    level: u32,
714}
715
716impl ExtendedTopologyEnumeration {
717    pub fn new() -> ExtendedTopologyEnumeration {
718        ExtendedTopologyEnumeration::default()
719    }
720}
721
722impl Iterator for ExtendedTopologyEnumeration {
723    type Item = ExtendedTopologyLeaf;
724
725    fn next(&mut self) -> Option<Self::Item> {
726        let leaf = ExtendedTopologyLeaf::new(self.level);
727
728        self.level += 1;
729
730        leaf
731    }
732}
733
734impl fmt::Debug for ExtendedTopologyEnumeration {
735    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
736        f.debug_list().entries(self.clone()).finish()
737    }
738}
739
740#[repr(u8)]
741#[derive(Copy, Clone, Debug, PartialEq)]
742pub enum TopologyType {
743    Invalid = 0,
744    SMT = 1,
745    Core = 2,
746}
747
748#[derive(Copy, Clone)]
749pub struct ExtendedTopologyLeaf {
750    eax: u32,
751    ebx: u32,
752    ecx: u32,
753    edx: u32,
754}
755
756impl ExtendedTopologyLeaf {
757    fn new(level: u32) -> Option<ExtendedTopologyLeaf> {
758        let (eax, ebx, ecx, edx) = cpuid_ext(RequestType::ExtendedTopologyEnumeration, level);
759
760        // Leaf 0BH exists if EBX[15:0] is not zero.
761        if bits_of(ebx, 0, 15) != 0 {
762            Some(ExtendedTopologyLeaf { eax, ebx, ecx, edx })
763        } else {
764            None
765        }
766    }
767
768    /// Number of bits to shift right on x2APIC ID to get a unique topology ID of the next level type.
769    ///
770    /// All logical processors with the same next level ID share current level.
771    ///
772    /// ### Notes
773    ///
774    /// Software should use this field (EAX[4:0]) to enumerate processor topology of the system.
775    fn shift_right_for_next_apic_id(&self) -> u32 {
776        bits_of(self.eax, 0, 4)
777    }
778
779    /// Get a unique topology ID of the next level type.
780    pub fn next_level_apic_id(&self) -> u32 {
781        self.current_logical_processor_id() >> self.shift_right_for_next_apic_id()
782    }
783
784    /// Number of logical processors at this level type.
785    ///
786    /// The number reflects configuration as shipped by Intel.
787    ///
788    /// ### Notes
789    ///
790    /// Software must not use EBX[15:0] to enumerate processor topology of the system.
791    /// This value in this field (EBX[15:0]) is only intended for display/diagnostic purposes.
792    /// The actual number of logical processors available to BIOS/OS/Applications
793    /// may be different from the value of EBX[15:0],
794    /// depending on software and platform hardware configurations.
795    pub fn logical_processor_count(&self) -> u32 {
796        bits_of(self.ebx, 0, 15)
797    }
798
799    /// Level number.
800    pub fn level_number(&self) -> u8 {
801        self.ecx as u8
802    }
803
804    /// Level type
805    pub fn level_type(&self) -> TopologyType {
806        match self.ecx >> 8 {
807            0 => TopologyType::Invalid,
808            1 => TopologyType::SMT,
809            2 => TopologyType::Core,
810            _ => unreachable!()
811        }
812    }
813
814    /// x2APIC ID the current logical processor.
815    ///
816    /// ### Notes
817    ///
818    /// Unless the program is constrained to run on a single
819    /// processor, multiple consecutive calls to this function can
820    /// return different values.
821    pub fn current_logical_processor_id(&self) -> u32 {
822        self.edx
823    }
824}
825
826impl fmt::Debug for ExtendedTopologyLeaf {
827    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
828        dump!(self, f, "ExtendedTopologyLeaf", {
829            level_number,
830            level_type,
831            logical_processor_count,
832            current_logical_processor_id,
833            next_level_apic_id
834        })
835    }
836}
837
838#[derive(Copy,Clone)]
839pub struct ProcessorExtendedState {
840    eax: u32,
841    ebx: u32,
842    ecx: u32,
843}
844
845impl ProcessorExtendedState {
846    fn new() -> ProcessorExtendedState {
847        let (a, b, c, _) = cpuid(RequestType::ProcessorExtendedState);
848        ProcessorExtendedState { eax: a, ebx: b, ecx: c }
849    }
850
851    bit!(eax, {
852        0 => x87_state,
853        1 => sse_state,
854        2 => avx_state,
855        // 3-4 mpx_state
856        // 5-7 avx_512_state
857        8 => ia32_xss,
858        9 => pkru_state
859        // 10-31 - reserved
860    });
861
862    pub fn mpx_state(self) -> u32 {
863        bits_of(self.eax, 3, 4)
864    }
865
866    pub fn avx_512_state(self) -> u32 {
867        bits_of(self.eax, 5, 7)
868    }
869
870    pub fn maximum_bytes_for_enabled_features(self) -> u32 {
871        self.ebx
872    }
873
874    pub fn maximum_bytes_for_supported_features(self) -> u32 {
875        self.ecx
876    }
877}
878
879impl fmt::Debug for ProcessorExtendedState {
880    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
881        dump!(self, f, "ProcessorExtendedState", {
882            x87_state,
883            sse_state,
884            avx_state,
885            mpx_state,
886            avx_512_state,
887            ia32_xss,
888            pkru_state,
889
890            maximum_bytes_for_enabled_features,
891
892            maximum_bytes_for_supported_features
893        })
894    }
895}
896
897#[derive(Copy,Clone)]
898pub struct ProcessorExtendedStateSecondary {
899    eax: u32,
900    ebx: u32,
901    ecx: u32,
902}
903
904impl ProcessorExtendedStateSecondary {
905    fn new() -> ProcessorExtendedStateSecondary {
906        let (a, b, c, _) = cpuid_ext(RequestType::ProcessorExtendedState, 0x00000001);
907        ProcessorExtendedStateSecondary { eax: a, ebx: b, ecx: c }
908    }
909
910    bit!(eax, {
911        0 => xsaveopt,
912        1 => xsavec_and_xrstor,
913        2 => xgetbv_with_ecx_1,
914        3 => xsaves_xrstors_and_ia32_xss
915        // 4-31 - reserved
916    });
917
918    bit!(ecx, {
919        // 0-7 - used for XCR0
920        8 => pt_state
921        // 9 - used for XCR0
922        // 10 - 31 - reserved
923    });
924
925    pub fn bytes_of_xsave_area_containing_all_states_enabled(self) -> u32 {
926        self.ebx
927    }
928}
929
930impl fmt::Debug for ProcessorExtendedStateSecondary {
931    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
932        dump!(self, f, "ProcessorExtendedStateSecondary", {
933            xsaveopt,
934            xsavec_and_xrstor,
935            xgetbv_with_ecx_1,
936            xsaves_xrstors_and_ia32_xss,
937
938            bytes_of_xsave_area_containing_all_states_enabled,
939
940            pt_state
941        })
942    }
943}
944
945#[derive(Debug, Copy, Clone)]
946pub enum CacheLineAssociativity {
947    Disabled,
948    DirectMapped,
949    TwoWay,
950    FourWay,
951    EightWay,
952    SixteenWay,
953    Full,
954}
955
956#[derive(Copy, Clone)]
957pub struct CacheLine(u32);
958
959impl CacheLine {
960    fn new() -> CacheLine {
961        let (_, _, c, _) = cpuid(RequestType::CacheLine);
962        CacheLine(c)
963    }
964
965    pub fn cache_line_size(self) -> u32 {
966        bits_of(self.0, 0, 7)
967    }
968
969    pub fn l2_associativity(self) -> Option<CacheLineAssociativity> {
970        match bits_of(self.0, 12, 15) {
971            0x00 => Some(CacheLineAssociativity::Disabled),
972            0x01 => Some(CacheLineAssociativity::DirectMapped),
973            0x02 => Some(CacheLineAssociativity::TwoWay),
974            0x04 => Some(CacheLineAssociativity::FourWay),
975            0x06 => Some(CacheLineAssociativity::EightWay),
976            0x08 => Some(CacheLineAssociativity::SixteenWay),
977            0x0F => Some(CacheLineAssociativity::Full),
978            _ => None,
979        }
980    }
981
982    pub fn cache_size(self) -> u32 {
983        bits_of(self.0, 16, 31)
984    }
985}
986
987impl fmt::Debug for CacheLine {
988    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
989        dump!(self, f, "CacheLine", {
990            cache_line_size,
991            l2_associativity,
992            cache_size
993        })
994    }
995}
996
997#[derive(Copy, Clone)]
998pub struct TimeStampCounter {
999    edx: u32,
1000}
1001
1002impl TimeStampCounter {
1003    fn new() -> TimeStampCounter {
1004        let (_, _, _, d) = cpuid(RequestType::TimeStampCounter);
1005        TimeStampCounter { edx: d }
1006    }
1007
1008    bit!(edx, {
1009        // 0-7 - reserved
1010        8 => invariant_tsc
1011        // 9-31 - reserved
1012    });
1013}
1014
1015impl fmt::Debug for TimeStampCounter {
1016    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1017        dump!(self, f, "TimeStampCounter", {
1018            invariant_tsc
1019        })
1020    }
1021}
1022
1023#[derive(Copy,Clone)]
1024pub struct PhysicalAddressSize(u32);
1025
1026impl PhysicalAddressSize {
1027    fn new() -> PhysicalAddressSize {
1028        let (a, _, _, _) = cpuid(RequestType::PhysicalAddressSize);
1029        PhysicalAddressSize(a)
1030    }
1031
1032    pub fn physical_address_bits(self) -> u32 {
1033        bits_of(self.0, 0, 7)
1034    }
1035
1036    pub fn linear_address_bits(self) -> u32 {
1037        bits_of(self.0, 8, 15)
1038    }
1039}
1040
1041impl fmt::Debug for PhysicalAddressSize {
1042    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1043        dump!(self, f, "PhysicalAddressSize", {
1044            physical_address_bits,
1045            linear_address_bits
1046        })
1047    }
1048}
1049
1050/// Information about the currently running processor
1051///
1052/// Feature flags match the feature mnemonic listed in the Intel
1053/// Instruction Set Reference. This struct provides a facade for flags
1054/// so the consumer doesn't need to worry about which particular CPUID
1055/// leaf provides the information.
1056///
1057/// For data beyond simple feature flags, you will need to retrieve
1058/// the nested struct and call the appropriate methods on it.
1059#[derive(Debug,Clone)]
1060pub struct Master {
1061    // TODO: Rename struct
1062    version_information: Option<VersionInformation>,
1063    thermal_power_management_information: Option<ThermalPowerManagementInformation>,
1064    structured_extended_information: Option<StructuredExtendedInformation>,
1065    extended_topology_enumeration: Option<ExtendedTopologyEnumeration>,
1066    processor_extended_state: Option<ProcessorExtendedState>,
1067    processor_extended_state_secondary: Option<ProcessorExtendedStateSecondary>,
1068    extended_processor_signature: Option<ExtendedProcessorSignature>,
1069    brand_string: Option<BrandString>,
1070    cache_line: Option<CacheLine>,
1071    time_stamp_counter: Option<TimeStampCounter>,
1072    physical_address_size: Option<PhysicalAddressSize>,
1073}
1074
1075impl Master {
1076    fn new() -> Master {
1077        fn when_supported<F, T>(max: u32, kind: RequestType, then: F) -> Option<T>
1078            where F: FnOnce() -> T
1079        {
1080            if max >= kind as u32 {
1081                Some(then())
1082            } else {
1083                None
1084            }
1085        }
1086
1087        let (max_value, _, _, _) = cpuid(RequestType::BasicInformation);
1088
1089        let vi = when_supported(max_value, RequestType::VersionInformation, || {
1090            VersionInformation::new()
1091        });
1092        let tpm = when_supported(max_value, RequestType::ThermalPowerManagementInformation, || {
1093            ThermalPowerManagementInformation::new()
1094        });
1095        let sei = when_supported(max_value, RequestType::StructuredExtendedInformation, || {
1096            StructuredExtendedInformation::new()
1097        });
1098        let ete = when_supported(max_value, RequestType::ExtendedTopologyEnumeration, || {
1099            ExtendedTopologyEnumeration::new()
1100        });
1101        let pes = when_supported(max_value, RequestType::ProcessorExtendedState, || {
1102            ProcessorExtendedState::new()
1103        });
1104        let pes_2 = when_supported(max_value, RequestType::ProcessorExtendedState, || {
1105            ProcessorExtendedStateSecondary::new()
1106        });
1107
1108        // Extended information
1109
1110        let (max_value, _, _, _) = cpuid(RequestType::ExtendedFunctionInformation);
1111
1112        let eps = when_supported(max_value, RequestType::ExtendedProcessorSignature, || {
1113            ExtendedProcessorSignature::new()
1114        });
1115        let brand_string = when_supported(max_value, RequestType::BrandString3, || {
1116            BrandString::new()
1117        });
1118        let cache_line = when_supported(max_value, RequestType::CacheLine, || {
1119            CacheLine::new()
1120        });
1121        let tsc = when_supported(max_value, RequestType::TimeStampCounter, || {
1122            TimeStampCounter::new()
1123        });
1124        let pas = when_supported(max_value, RequestType::PhysicalAddressSize, || {
1125            PhysicalAddressSize::new()
1126        });
1127
1128        Master {
1129            version_information: vi,
1130            thermal_power_management_information: tpm,
1131            structured_extended_information: sei,
1132            extended_topology_enumeration: ete,
1133            processor_extended_state: pes,
1134            processor_extended_state_secondary: pes_2,
1135            extended_processor_signature: eps,
1136            brand_string: brand_string,
1137            cache_line: cache_line,
1138            time_stamp_counter: tsc,
1139            physical_address_size: pas,
1140        }
1141    }
1142
1143    master_attr_reader!(version_information, VersionInformation);
1144    master_attr_reader!(thermal_power_management_information, ThermalPowerManagementInformation);
1145    master_attr_reader!(structured_extended_information, StructuredExtendedInformation);
1146    master_attr_reader!(extended_topology_enumeration, ExtendedTopologyEnumeration);
1147    master_attr_reader!(processor_extended_state, ProcessorExtendedState);
1148    master_attr_reader!(processor_extended_state_secondary, ProcessorExtendedStateSecondary);
1149    master_attr_reader!(extended_processor_signature, ExtendedProcessorSignature);
1150    master_attr_reader!(cache_line, CacheLine);
1151    master_attr_reader!(time_stamp_counter, TimeStampCounter);
1152    master_attr_reader!(physical_address_size, PhysicalAddressSize);
1153
1154    pub fn brand_string(&self) -> Option<&str> {
1155        self.brand_string.as_ref().map(|bs| bs as &str).or({
1156            self.version_information.and_then(|vi| vi.brand_string())
1157        })
1158    }
1159
1160    delegate_flag!(version_information, {
1161        sse3,
1162        pclmulqdq,
1163        dtes64,
1164        monitor,
1165        ds_cpl,
1166        vmx,
1167        smx,
1168        eist,
1169        tm2,
1170        ssse3,
1171        cnxt_id,
1172        sdbg,
1173        fma,
1174        cmpxchg16b,
1175        xtpr_update_control,
1176        pdcm,
1177        pcid,
1178        dca,
1179        sse4_1,
1180        sse4_2,
1181        x2apic,
1182        movbe,
1183        popcnt,
1184        tsc_deadline,
1185        aesni,
1186        xsave,
1187        osxsave,
1188        avx,
1189        f16c,
1190        rdrand,
1191        fpu,
1192        vme,
1193        de,
1194        pse,
1195        tsc,
1196        msr,
1197        pae,
1198        mce,
1199        cx8,
1200        apic,
1201        sep,
1202        mtrr,
1203        pge,
1204        mca,
1205        cmov,
1206        pat,
1207        pse_36,
1208        psn,
1209        clfsh,
1210        ds,
1211        acpi,
1212        mmx,
1213        fxsr,
1214        sse,
1215        sse2,
1216        ss,
1217        htt,
1218        tm,
1219        pbe
1220    });
1221
1222    delegate_flag!(thermal_power_management_information, {
1223        digital_temperature_sensor,
1224        intel_turbo_boost,
1225        arat,
1226        pln,
1227        ecmd,
1228        ptm,
1229        hwp,
1230        hwp_notification,
1231        hwp_activity_window,
1232        hwp_energy_performance_preference,
1233        hdc,
1234        hardware_coordination_feedback,
1235        performance_energy_bias
1236    });
1237
1238    delegate_flag!(structured_extended_information, {
1239        fsgsbase,
1240        ia32_tsc_adjust_msr,
1241        bmi1,
1242        hle,
1243        avx2,
1244        smep,
1245        bmi2,
1246        enhanced_rep_movsb_stosb,
1247        invpcid,
1248        rtm,
1249        pqm,
1250        deprecates_fpu_cs_ds,
1251        pqe,
1252        avx512f,
1253        avx512dq,
1254        rdseed,
1255        adx,
1256        smap,
1257        avx512_ifma,
1258        clflushopt,
1259        clwb,
1260        intel_processor_trace,
1261        avx512pf,
1262        avx512er,
1263        avx512cd,
1264        sha,
1265        avx512bw,
1266        avx512vl,
1267        prefetchwt1,
1268        avx512_vbmi,
1269        umip,
1270        pku,
1271        ospke,
1272        avx512_vbmi2,
1273        gfni,
1274        vaes,
1275        vpclmulqdq,
1276        avx512_vnni,
1277        avx512_bitalg,
1278        avx512_vpopcntdq,
1279        sgx,
1280        avx512_4vnniw,
1281        avx512_4fmaps
1282    });
1283
1284    delegate_flag!(processor_extended_state, {
1285        x87_state,
1286        sse_state,
1287        avx_state,
1288        ia32_xss,
1289        pkru_state
1290    });
1291
1292    delegate_flag!(processor_extended_state_secondary, {
1293        xsaveopt,
1294        xsavec_and_xrstor,
1295        xgetbv_with_ecx_1,
1296        xsaves_xrstors_and_ia32_xss
1297    });
1298
1299    delegate_flag!(extended_processor_signature, {
1300        lahf_sahf_in_64_bit,
1301        lzcnt,
1302        prefetchw,
1303        tbm,
1304        sse4a,
1305        syscall_sysret_in_64_bit,
1306        execute_disable,
1307        gigabyte_pages,
1308        rdtscp_and_ia32_tsc_aux,
1309        intel_64_bit_architecture
1310    });
1311
1312    delegate_flag!(time_stamp_counter, {
1313        invariant_tsc
1314    });
1315}
1316
1317#[cfg(all(test, cpuid_available))]
1318mod test {
1319    use super::*;
1320
1321    #[test]
1322    fn brand_string_contains_intel_or_amd() {
1323        let master = master().unwrap();
1324        let brand_string = master.brand_string().unwrap();
1325
1326        // "Intel(R) Core(TM) i7-3615QM CPU @ 2.30GHz"
1327        let is_intel = brand_string.starts_with("Intel(R)");
1328        // Travis sometimes has these.
1329        // "AMD EPYC 7401P 24-Core Processor"
1330        let is_amd = brand_string.starts_with("AMD");
1331
1332        assert!(is_intel || is_amd, "Brand string was {}", brand_string);
1333    }
1334}
1335
1336#[cfg(all(test, not(cpuid_available)))]
1337mod test {
1338    use super::*;
1339
1340    #[test]
1341    fn is_not_available() {
1342        assert!(master().is_none());
1343    }
1344}