Skip to main content

libarch/x86/
cpuid.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::fmt;
6use core::str::Utf8Error;
7
8use bitrs::{bitfield_repr, layout};
9use regio::x86::{Cpuid, CpuidValue, EAX, EBX, ECX, EDX};
10
11use super::Vendor;
12
13/// Leaf/Function 0x0, EAX
14///
15/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
16/// [amd/vol3]: E.3.1, CPUID Fn0000_0000_EAX Largest Standard Function Number.
17pub const MAX_LEAF: CpuidValue<0x0, 0x0, EAX, u32> = CpuidValue::new();
18
19/// Leaf/Function 0x0, EBX
20///
21/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
22/// [amd/vol3]: E.3.1, CPUID Fn0000_0000_E[D,C,B]X Processor Vendor.
23pub const VENDOR_STRING_B: CpuidValue<0x0, 0x0, EBX, u32> = CpuidValue::new();
24
25/// Leaf/Function 0x0, ECX
26///
27/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
28/// [amd/vol3]: E.3.1, CPUID Fn0000_0000_E[D,C,B]X Processor Vendor.
29pub const VENDOR_STRING_C: CpuidValue<0x0, 0x0, ECX, u32> = CpuidValue::new();
30
31/// Leaf/Function 0x0, EDX
32///
33/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
34/// [amd/vol3]: E.3.1, CPUID Fn0000_0000_E[D,C,B]X Processor Vendor.
35pub const VENDOR_STRING_D: CpuidValue<0x0, 0x0, EDX, u32> = CpuidValue::new();
36
37/// A vendor string derived from CPUID.
38#[derive(Clone, Copy, Debug, Eq, PartialEq)]
39pub struct VendorString([u8; 12]);
40
41impl VendorString {
42    /// Intel's vendor string.
43    pub const INTEL: Self = Self(*b"GenuineIntel");
44
45    /// AMD's vendor string.
46    pub const AMD: Self = Self(*b"AuthenticAMD");
47
48    /// Returns the CPUID-based vendor string.
49    pub fn from_cpuid(cpuid: impl Cpuid) -> Self {
50        let ebx = cpuid.read(VENDOR_STRING_B).to_le_bytes();
51        let ecx = cpuid.read(VENDOR_STRING_C).to_le_bytes();
52        let edx = cpuid.read(VENDOR_STRING_D).to_le_bytes();
53        Self([
54            ebx[0], ebx[1], ebx[2], ebx[3], //
55            edx[0], edx[1], edx[2], edx[3], //
56            ecx[0], ecx[1], ecx[2], ecx[3], //
57        ])
58    }
59
60    /// Returns the vendor identified by this string.
61    ///
62    /// Returns [`Vendor::Unknown`] when the vendor string does not correspond
63    /// to a known vendor.
64    pub fn vendor(&self) -> Vendor {
65        match *self {
66            Self::INTEL => Vendor::Intel,
67            Self::AMD => Vendor::Amd,
68            _ => Vendor::Unknown,
69        }
70    }
71
72    pub fn as_str(&self) -> Result<&str, Utf8Error> {
73        str::from_utf8(&self.0)
74    }
75}
76
77/// Leaf/Function 0x1, EBX
78///
79/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
80/// [amd/vol3]: E.3.2  Function 1h—Processor and Processor Feature Identifiers.
81pub const PROCESSOR_INFO: CpuidValue<0x1, 0x0, EBX, ProcessorInfo> = CpuidValue::new();
82
83layout!({
84    /// The layout of EBX in [`PROCESSOR_INFO`].
85    pub struct ProcessorInfo(u32);
86    {
87        let initial_apic_id @ 31..24;
88        let max_logical_processors @ 23..16;
89        let clflush_size @ 15..8;
90        let brand_index @ 7..0;
91    }
92});
93
94impl ProcessorInfo {
95    pub fn cache_line_size_bytes(&self) -> usize {
96        self.clflush_size() as usize * 8
97    }
98}
99
100/// Leaf/Function 0x1, ECX
101///
102/// [intel/vol2]: Table 3-10.  Feature Information Returned in the ECX Register.
103/// [amd/vol3]: E.3.2, CPUID Fn0000_0001_ECX Feature Identifiers.
104pub const FEATURE_FLAGS_C: CpuidValue<0x1, 0x0, ECX, FeatureFlagsC> = CpuidValue::new();
105
106layout!({
107    /// The layout of ECX in [`FEATURE_FLAGS_C`].
108    pub struct FeatureFlagsC(u32);
109    {
110        let hypervisor @ 31;
111        let rdrand @ 30;
112        let f16c @ 29;
113        let avx @ 28;
114        let osxsave @ 27;
115        let xsave @ 26;
116        let aes @ 25;
117        let tsc_deadline @ 24;
118        let popcnt @ 23;
119        let movbe @ 22;
120        let x2apic @ 21;
121        let sse4_2 @ 20;
122        let sse4_1 @ 19;
123        let dca @ 18;
124        let pcid @ 17;
125        let __ @ 16;
126        let pdcm @ 15;
127        let xtpr @ 14;
128        let cmpxchg16b @ 13;
129        let fma @ 12;
130        let sdbg @ 11;
131        let cnxt_id @ 10;
132        let ssse3 @ 9;
133        let tm2 @ 8;
134        let eist @ 7;
135        let smx @ 6;
136        let vmx @ 5;
137        let ds_cpl @ 4;
138        let monitor @ 3;
139        let dtes64 @ 2;
140        let pclmulqdq @ 1;
141        let sse3 @ 0;
142    }
143});
144
145/// Leaf/Function 0x1, EDX
146///
147/// [intel/vol2]: Table 3-11.  More on Feature Information Returned in the EDX Register.
148/// [amd/vol3]: E.3.6  Function 7h—Structured Extended Feature Identifiers.
149pub const FEATURE_FLAGS_D: CpuidValue<0x1, 0x0, EDX, FeatureFlagsD> = CpuidValue::new();
150
151layout!({
152    /// The layout of EDX in [`FEATURE_FLAGS_D`].
153    pub struct FeatureFlagsD(u32);
154    {
155        let pbe @ 31;
156        let __ @ 30;
157        let tm @ 29;
158        let htt @ 28;
159        let ss @ 27;
160        let sse2 @ 26;
161        let sse @ 25;
162        let fxsr @ 24;
163        let mmx @ 23;
164        let acpi @ 22;
165        let ds @ 21;
166        let __ @ 20;
167        let clfsh @ 19;
168        let psn @ 18;
169        let pse36 @ 17;
170        let pat @ 16;
171        let cmov @ 15;
172        let mca @ 14;
173        let pge @ 13;
174        let mtrr @ 12;
175        let sep @ 11;
176        let __ @ 10;
177        let apic @ 9;
178        let cx8 @ 8;
179        let mce @ 7;
180        let pae @ 6;
181        let msr @ 5;
182        let tsc @ 4;
183        let pse @ 3;
184        let de @ 2;
185        let vme @ 1;
186        let fpu @ 0;
187    }
188});
189
190/// Leaf/Function 0x7, EBX
191///
192/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
193/// [amd/vol3]: E.3.6, CPUID Fn0000_0007_EBX_x0 Structured Extended Feature
194/// Identifiers (ECX=0).
195pub const EXTENDED_FEATURES_B: CpuidValue<0x7, 0x0, EBX, ExtendedFeatureFlagsB> = CpuidValue::new();
196
197layout!({
198    /// The layout of EBX in [`EXTENDED_FEATURES_B`].
199    pub struct ExtendedFeatureFlagsB(u32);
200    {
201        let avx512vl @ 31;
202        let avx512bw @ 30;
203        let sha @ 29;
204        let avx512cd @ 28;
205        let avx512er @ 27;
206        let avx512pf @ 26;
207        let intel_pt @ 25;
208        let clwb @ 24;
209        let clflushopt @ 23;
210        let __ @ 22;
211        let avx512_ifma @ 21;
212        let smap @ 20;
213        let adx @ 19;
214        let rdseed @ 18;
215        let avx512dq @ 17;
216        let avx512f @ 16;
217        let rdt_a @ 15;
218        let mpx @ 14;
219        let fpu_cs_ds_deprecated @ 13;
220        let rdt_m @ 12;
221        let rtm @ 11;
222        let invpcid @ 10;
223        let erms @ 9;
224        let bmi2 @ 8;
225        let smep @ 7;
226        let fdp_excptn_only_x87 @ 6;
227        let avx2 @ 5;
228        let hle @ 4;
229        let bmi1 @ 3;
230        let sgx @ 2;
231        let tsc_adjust @ 1;
232        let fsgsbase @ 0;
233    }
234});
235
236/// Leaf/Function 0x7, ECX
237///
238/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
239/// [amd/vol3]: E.3.6, CPUID Fn0000_0007_ECX_x0 Structured Extended Feature
240/// Identifiers (ECX=0).
241pub const EXTENDED_FEATURES_C: CpuidValue<0x7, 0x0, ECX, ExtendedFeatureFlagsC> = CpuidValue::new();
242
243layout!({
244    /// The layout of ECX in [`EXTENDED_FEATURES_C`].
245    pub struct ExtendedFeatureFlagsC(u32);
246    {
247        let pks @ 31;
248        let sgx_lc @ 30;
249        let __ @ 29;
250        let movdir64b @ 28;
251        let movdiri @ 27;
252        let __ @ 26;
253        let cldemote @ 25;
254        let __ @ 24;
255        let kl @ 23;
256        let rdpid @ 22;
257        // Bits [21:17] are 'The value of MAWAU used by the BNDLDX and BNDSTX
258        // instructions in 64-bit mode.'
259        let __ @ 21..17;
260        let la57 @ 16;
261        let __ @ 15;
262        let avx512_vpopcntdq @ 14;
263        let tme_en @ 13;
264        let avx512_bitalg @ 12;
265        let avx512_vnni @ 11;
266        let vpclmulqdq @ 10;
267        let vaes @ 9;
268        let gfni @ 8;
269        let cet_ss @ 7;
270        let avx512_vbmi2 @ 6;
271        let waitpkg @ 5;
272        let ospke @ 4;
273        let pku @ 3;
274        let umip @ 2;
275        let avx512_vbmi @ 1;
276        let prefetchwt1 @ 0;
277    }
278});
279
280/// Leaf/Function 0x7, EDX
281///
282/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
283/// [amd/vol3]: E.3.6, CPUID Fn0000_0007_EDX_x0 Structured Extended Feature
284/// Identifiers (ECX=0).
285pub const EXTENDED_FEATURES_D: CpuidValue<0x7, 0x0, EDX, ExtendedFeatureFlagsD> = CpuidValue::new();
286
287layout!({
288    /// The layout of EDX in [`EXTENDED_FEATURES_D`].
289    pub struct ExtendedFeatureFlagsD(u32);
290    {
291        let ssbd @ 31;
292        let ia32_core_capabilities @ 30;
293        let ia32_arch_capabilities @ 29;
294        let l1d_flush @ 28;
295        let stibp @ 27;
296        let ibrs_ibpb @ 26;
297        let __ @ 25..21;
298        let cet_ibt @ 20;
299        let __ @ 19;
300        let pconfig @ 18;
301        let __ @ 17..16;
302        let hybrid @ 15;
303        let serialize @ 14;
304        let __ @ 13..11;
305        let md_clear @ 10;
306        let __ @ 9;
307        let avx512_vp2intersect @ 8;
308        let __ @ 7..5;
309        let fsrm @ 4;
310        let avx512_4fmaps @ 3;
311        let avx512_4vnniw @ 2;
312        let __ @ 1..0;
313    }
314});
315
316/// Cache type for Cache Topology leaves.
317#[bitfield_repr(u8)]
318#[derive(Clone, Copy)]
319pub enum CacheType {
320    Null = 0,
321    Data = 1,
322    Instruction = 2,
323    Unified = 3,
324}
325
326// TODO(https://github.com/rust-lang/rust/issues/113521): These values should
327// be generic over subleaf.
328
329/// Leaf/Function 0x4, EAX (subleaf 0)
330///
331/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
332pub const INTEL_CACHE_TOPOLOGY_A: CpuidValue<0x4, 0, EAX, CacheTopologyA> = CpuidValue::new();
333
334/// Leaf/Function 0x8000_001d, EAX (subleaf 0)
335///
336/// [amd/vol3]: E.4.15  Function 8000_001Dh—Cache Topology Information.
337pub const AMD_CACHE_TOPOLOGY_A: CpuidValue<0x8000_001d, 0, EAX, CacheTopologyA> = CpuidValue::new();
338
339layout!({
340    /// The layout of EAX in Intel and AMD Cache Topology leaves.
341    pub struct CacheTopologyA(u32);
342    {
343        let max_cores @ 31..26; // Reserved on AMD.
344        let max_sharing_logical_processors @ 25..14;
345        let __ @ 13..10;
346        let fully_associative @ 9;
347        let self_initializing @ 8;
348        let cache_level @ 7..5;
349        let cache_type @ 4..0: CacheType;
350    }
351});
352
353layout!({
354    /// The layout of EBX in Intel and AMD Cache Topology leaves.
355    pub struct CacheTopologyB(u32);
356    {
357        let ways @ 31..22;
358        let physical_line_partitions @ 21..12;
359        let system_coherency_line_size @ 11..0;
360    }
361});
362
363layout!({
364    /// The layout of ECX in Intel and AMD Cache Topology leaves.
365    pub struct CacheTopologyC(u32);
366    {
367        let sets @ 31..0;
368    }
369});
370
371layout!({
372    /// The layout of EDX in Intel and AMD Cache Topology leaves.
373    pub struct CacheTopologyD(u32);
374    {
375        let __ @ 31..3;
376        let complex_cache_indexing @ 2;
377        let inclusive @ 1;
378        let wbinvd @ 0;
379    }
380});
381
382/// Leaf/Function 0x8000_0005, ECX
383///
384/// [amd/vol3]: E.4.4  Function 8000_0005h — L1 Cache and TLB Information.
385pub const AMD_L1_DATA_CACHE_INFO: CpuidValue<0x8000_0005, 0, ECX, AmdL1CacheInformation> =
386    CpuidValue::new();
387
388/// Leaf/Function 0x8000_0005, EDX
389///
390/// [amd/vol3]: E.4.4  Function 8000_0005h — L1 Cache and TLB Information.
391pub const AMD_L1_INSTRUCTION_CACHE_INFO: CpuidValue<0x8000_0005, 0, EDX, AmdL1CacheInformation> =
392    CpuidValue::new();
393
394layout!({
395    /// The layout of [`AMD_L1_DATA_CACHE_INFO`] and
396    /// [`AMD_L1_INSTRUCTION_CACHE_INFO`].
397    pub struct AmdL1CacheInformation(u32);
398    {
399        let size_kb @ 31..24;
400        let assoc @ 23..16;
401        let lines_per_tag @ 15..8;
402        let line_size @ 7..0;
403    }
404});
405
406impl AmdL1CacheInformation {
407    pub const FULLY_ASSOCIATIVE: u8 = 0xff;
408
409    pub fn ways_of_associativity(&self) -> usize {
410        if self.assoc() == Self::FULLY_ASSOCIATIVE { 0 } else { self.assoc() as usize }
411    }
412
413    pub fn fully_associative(&self) -> Option<bool> {
414        match self.assoc() {
415            0 => None,
416            Self::FULLY_ASSOCIATIVE => Some(true),
417            _ => Some(false),
418        }
419    }
420}
421
422/// Associativity encoding for L2 and L3 cache information leaves.
423#[bitfield_repr(u8)]
424#[derive(Clone, Copy)]
425pub enum AmdL2L3Associativity {
426    Disabled = 0x0,
427    DirectMapped = 0x1,
428    Ways2 = 0x2,
429    Ways3 = 0x3,
430    Ways4 = 0x4,
431    Ways6 = 0x5,
432    Ways8 = 0x6,
433    // 0x7 is reserved.
434    Ways16 = 0x8,
435    SeeLeaf0x8000001d = 0x9,
436    Ways32 = 0xa,
437    Ways48 = 0xb,
438    Ways64 = 0xc,
439    Ways96 = 0xd,
440    Ways128 = 0xe,
441    FullyAssociative = 0xf,
442}
443
444impl AmdL2L3Associativity {
445    /// Indeterminate if zero.
446    pub fn ways_of_associativity(&self) -> usize {
447        match self {
448            Self::Disabled | Self::SeeLeaf0x8000001d | Self::FullyAssociative => 0,
449            Self::DirectMapped => 1,
450            Self::Ways2 => 2,
451            Self::Ways3 => 3,
452            Self::Ways4 => 4,
453            Self::Ways6 => 6,
454            Self::Ways8 => 8,
455            Self::Ways16 => 16,
456            Self::Ways32 => 32,
457            Self::Ways48 => 48,
458            Self::Ways64 => 64,
459            Self::Ways96 => 96,
460            Self::Ways128 => 128,
461        }
462    }
463
464    /// Indeterminate if std::nullopt.
465    pub fn fully_associative(&self) -> Option<bool> {
466        match self {
467            Self::Disabled => None,
468            Self::FullyAssociative => Some(true),
469            _ => Some(false),
470        }
471    }
472}
473
474/// Leaf/Function 0x8000_0006, ECX
475///
476/// [amd/vol3]: E.4.5  Function 8000_0006h—L2 Cache and TLB and L3 Cache Information.
477pub const AMD_L2_CACHE_INFO: CpuidValue<0x8000_0006, 0, ECX, AmdL2CacheInformation> =
478    CpuidValue::new();
479
480layout!({
481    /// The layout of ECX in [`AMD_L2_CACHE_INFO`].
482    pub struct AmdL2CacheInformation(u32);
483    {
484        let size_kb @ 31..16;
485        let assoc @ 15..12: AmdL2L3Associativity;
486        let lines_per_tag @ 11..8;
487        let line_size @ 7..0;
488    }
489});
490
491impl AmdL2CacheInformation {
492    pub fn ways_of_associativity(&self) -> usize {
493        self.assoc().ways_of_associativity()
494    }
495
496    pub fn fully_associative(&self) -> Option<bool> {
497        self.assoc().fully_associative()
498    }
499}
500
501/// Leaf/Function 0x8000_0006, EDX
502///
503/// [amd/vol3]: E.4.5  Function 8000_0006h—L2 Cache and TLB and L3 Cache Information.
504pub const AMD_L3_CACHE_INFO: CpuidValue<0x8000_0006, 0, EDX, AmdL3CacheInformation> =
505    CpuidValue::new();
506
507layout!({
508    /// The layout of EDX in [`AMD_L3_CACHE_INFO`].
509    pub struct AmdL3CacheInformation(u32);
510    {
511        let size @ 31..18;
512        let __ @ 17..16;
513        let assoc @ 15..12: AmdL2L3Associativity;
514        let lines_per_tag @ 11..8;
515        let line_size @ 7..0;
516    }
517});
518
519impl AmdL3CacheInformation {
520    pub fn ways_of_associativity(&self) -> usize {
521        self.assoc().ways_of_associativity()
522    }
523
524    pub fn fully_associative(&self) -> Option<bool> {
525        self.assoc().fully_associative()
526    }
527}
528
529/// Topology level type for CPUID topology enumeration leaves.
530#[bitfield_repr(u8)]
531#[derive(Clone, Copy)]
532pub enum TopologyLevelType {
533    Invalid = 0,
534    Smt = 1,
535    Core = 2,
536    Module = 3,
537    Tile = 4,
538    Die = 5,
539}
540
541layout!({
542    /// The layout of EAX in topology enumeration leaves.
543    pub struct TopologyEnumerationA(u32);
544    {
545        let __ @ 31..5;
546        let next_level_apic_id_shift @ 4..0;
547    }
548});
549
550layout!({
551    /// The layout of EBX in topology enumeration leaves.
552    pub struct TopologyEnumerationB(u32);
553    {
554        let __ @ 31..16;
555        let num_logical_processors @ 15..0;
556    }
557});
558
559layout!({
560    /// The layout of ECX in topology enumeration leaves.
561    pub struct TopologyEnumerationC(u32);
562    {
563        let __ @ 31..16;
564        let level_type @ 15..8: TopologyLevelType;
565        let level_number @ 7..0;
566    }
567});
568
569layout!({
570    /// The layout of EDX in topology enumeration leaves.
571    pub struct TopologyEnumerationD(u32);
572    {
573        let x2apic_id @ 31..0;
574    }
575});
576
577/// Leaf/Function 0xb (V1 Topology), EAX (subleaf 0)
578///
579/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
580pub const V1_TOPOLOGY_A: CpuidValue<0xb, 0, EAX, TopologyEnumerationA> = CpuidValue::new();
581
582/// Leaf/Function 0xb (V1 Topology), EBX (subleaf 0)
583///
584/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
585pub const V1_TOPOLOGY_B: CpuidValue<0xb, 0, EBX, TopologyEnumerationB> = CpuidValue::new();
586
587/// Leaf/Function 0xb (V1 Topology), ECX (subleaf 0)
588///
589/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
590pub const V1_TOPOLOGY_C: CpuidValue<0xb, 0, ECX, TopologyEnumerationC> = CpuidValue::new();
591
592/// Leaf/Function 0xb (V1 Topology), EDX (subleaf 0)
593///
594/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
595pub const V1_TOPOLOGY_D: CpuidValue<0xb, 0, EDX, TopologyEnumerationD> = CpuidValue::new();
596
597/// Leaf/Function 0x1f (V2 Topology), EAX (subleaf 0)
598///
599/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
600pub const V2_TOPOLOGY_A: CpuidValue<0x1f, 0, EAX, TopologyEnumerationA> = CpuidValue::new();
601
602/// Leaf/Function 0x1f (V2 Topology), EBX (subleaf 0)
603///
604/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
605pub const V2_TOPOLOGY_B: CpuidValue<0x1f, 0, EBX, TopologyEnumerationB> = CpuidValue::new();
606
607/// Leaf/Function 0x1f (V2 Topology), ECX (subleaf 0)
608///
609/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
610pub const V2_TOPOLOGY_C: CpuidValue<0x1f, 0, ECX, TopologyEnumerationC> = CpuidValue::new();
611
612/// Leaf/Function 0x1f (V2 Topology), EDX (subleaf 0)
613///
614/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
615pub const V2_TOPOLOGY_D: CpuidValue<0x1f, 0, EDX, TopologyEnumerationD> = CpuidValue::new();
616
617/// Performance timestamp counter size.
618#[bitfield_repr(u8)]
619#[derive(Clone, Copy)]
620pub enum PerfTimestampCounterSize {
621    Bits40 = 0b00,
622    Bits48 = 0b01,
623    Bits56 = 0b10,
624    Bits64 = 0b11,
625}
626
627/// Leaf/Function 0x8000_0008, ECX
628///
629/// [amd/vol3]: E.4.7  Function 8000_0008h—Processor Capacity Parameters and
630/// Extended Feature Identification.
631pub const EXTENDED_SIZE_INFO: CpuidValue<0x8000_0008, 0, ECX, ExtendedSizeInfo> = CpuidValue::new();
632
633layout!({
634    /// The layout of ECX in [`EXTENDED_SIZE_INFO`].
635    pub struct ExtendedSizeInfo(u32);
636    {
637        let __ @ 31..18;
638        let perf_tsc_size @ 17..16: PerfTimestampCounterSize;
639        let apic_id_size @ 15..12;
640        let __ @ 11..8;
641        let nc @ 7..0;
642    }
643});
644
645/// Leaf/Function 0x8000_0008, EBX
646///
647/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
648/// [amd/vol3]: E.4.7  Function 8000_0008h-Processor Capacity Parameters and Extended Feature
649/// Identification.
650pub const EXTENDED_AMD_FEATURES_B: CpuidValue<0x8000_0008, 0, EBX, ExtendedAmdFeaturesB> =
651    CpuidValue::new();
652
653layout!({
654    /// Layout for [`EXTENDED_AMD_FEATURES_B`].
655    ///
656    /// [amd/ibc] details bits [18:14] and 12.
657    /// [amd/ssbd] details bits [26:24].
658    pub struct ExtendedAmdFeaturesB(u32);
659    {
660        let __ @ 31..27;
661        let ssb_no @ 26;
662        let virt_ssbd @ 25;
663        let ssbd @ 24;
664        let __ @ 23..19;
665        let prefers_ibrs @ 18;
666        let stibp_always_on @ 17;
667        let ibrs_always_on @ 16;
668        let stibp @ 15;
669        let ibrs @ 14;
670        let __ @ 13;
671        let ibpb @ 12;
672        let __ @ 11..10;
673        let wbnoinvd @ 9;
674        let mcommit @ 8;
675        let __ @ 7..5;
676        let rdpru @ 4;
677        let __ @ 3;
678        let rstr_fp_err_ptrs @ 2;
679        let inst_ret_cnt_msr @ 1;
680        let clzero @ 0;
681    }
682});
683
684/// Leaf/Function 0x8000_001e, EAX
685///
686/// [amd/vol3]: E.4.16  Function 8000_001Eh—Processor Topology Information.
687pub const EXTENDED_APIC_ID: CpuidValue<0x8000_001e, 0, EAX, ExtendedApicId> = CpuidValue::new();
688
689layout!({
690    /// The layout of EAX in [`EXTENDED_APIC_ID`].
691    pub struct ExtendedApicId(u32);
692    {
693        let x2apic_id @ 31..0;
694    }
695});
696
697/// Leaf/Function 0x8000_001e, EBX
698///
699/// [amd/vol3]: E.4.16  Function 8000_001Eh—Processor Topology Information.
700pub const COMPUTE_UNIT_INFO: CpuidValue<0x8000_001e, 0, EBX, ComputeUnitInfo> = CpuidValue::new();
701
702layout!({
703    /// The layout of EBX in [`COMPUTE_UNIT_INFO`].
704    pub struct ComputeUnitInfo(u32);
705    {
706        let __ @ 31..16;
707        let threads_per_compute_unit @ 15..8;
708        let compute_unit_id @ 7..0;
709    }
710});
711
712/// Leaf/Function 0x8000_001e, ECX
713///
714/// [amd/vol3]: E.4.16  Function 8000_001Eh—Processor Topology Information.
715pub const NODE_INFO: CpuidValue<0x8000_001e, 0, ECX, NodeInfo> = CpuidValue::new();
716
717layout!({
718    /// The layout of ECX in [`NODE_INFO`].
719    pub struct NodeInfo(u32);
720    {
721        let __ @ 31..11;
722        let nodes_per_package @ 10..8;
723        let node_id @ 7..0;
724    }
725});
726
727/// Leaf/Function 0x1, EAX.
728///
729/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
730/// [amd/vol3]: E.3.2  Function 1h-Processor and Processor Feature Identifiers
731/// [intel/vol2]: Figure 3-6.  Version Information Returned by CPUID in EAX.
732/// [amd/vol3]: E.3.2, CPUID Fn0000_0001_EAX  Family, Model, Stepping Identifiers.
733pub const VERSION_INFO: CpuidValue<0x1, 0x0, EAX, VersionInfo> = CpuidValue::new();
734
735/// Encoding for intel processor type in [`VersionInfo`].
736///
737/// [intel/vol2]: Table 3-9.  Processor Type Field.
738#[bitfield_repr(u8)]
739#[derive(Clone, Copy)]
740pub enum IntelProcessorType {
741    OriginalOem = 0b00,
742    IntelOverdrive = 0b01,
743    Dual = 0b10,
744    Reserved = 0b11,
745}
746
747layout!({
748    /// The layout of EAX in [`VERSION_INFO`].
749    pub struct VersionInfo(u32);
750    {
751        let __ @ 31..28;
752        let extended_family @ 27..20;
753        let extended_model @ 19..16;
754        let __ @ 15..14;
755        let intel_processor @ 13..12: IntelProcessorType;
756        let base_family @ 11..8;
757        let base_model @ 7..4;
758        let stepping @ 3..0;
759    }
760});
761
762impl VersionInfo {
763    pub fn family(self) -> u8 {
764        if self.base_family() == 0xf {
765            self.base_family() + self.extended_family()
766        } else {
767            self.base_family()
768        }
769    }
770
771    pub fn model(self) -> u8 {
772        if self.base_family() == 0x6 || self.base_family() == 0xf {
773            (self.extended_model() << 4) | self.base_model()
774        } else {
775            self.base_model()
776        }
777    }
778
779    /// Attempts to derives the microarchitecture with the assumption that the
780    /// system relates to a particular vendor.
781    pub fn microarchitecture(self, vendor: Vendor) -> Microarchitecture {
782        // TODO(https://fxbug.dev/42138852): check in a source of truth for this information and
783        // refer to that here.
784        match vendor {
785            Vendor::Intel => {
786                // Table largely from https://en.wikichip.org/wiki/intel/cpuid
787                match self.family() {
788                    0x6 => match self.model() {
789                        // Big cores
790                        0x0f | // Merom
791                        0x16 | // Merom L
792                        0x17 | // Penryn, Wolfdale, Yorkfield, Harpertown, QC
793                        0x1d   // Dunnington
794                            => Microarchitecture::IntelCore2,
795                        0x1a | // Bloomfield, EP, WS
796                        0x1e | // Lynnfield, Clarksfield
797                        0x1f | // Auburndale, Havendale
798                        0x2e   // EX
799                            => Microarchitecture::IntelNehalem,
800                        0x25 | // Arrandale, Clarkdale
801                        0x2c | // Gulftown, EP
802                        0x2f   // EX
803                            => Microarchitecture::IntelWestmere,
804                        0x2a | // M, H
805                        0x2d   // E, EN, EP
806                            => Microarchitecture::IntelSandyBridge,
807                        0x3a | // M, H, Gladden
808                        0x3e   // E, EN, EP, EX
809                            => Microarchitecture::IntelIvyBridge,
810                        0x3c | // S
811                        0x3f | // E, EP, EX
812                        0x45 | // ULT
813                        0x46   // GT3E
814                            => Microarchitecture::IntelHaswell,
815                        0x3d | // U, Y, S
816                        0x47 | // H, C, W
817                        0x56 | // DE, Hewitt Lake
818                        0x4f   // E, EP, EX
819                            => Microarchitecture::IntelBroadwell,
820                        0x4e | // Skylake Y, U
821                        0x5e | // Skylake DT, H, S
822                        0x8e | // Kaby Lake Y, U, Coffee Lake U;
823                               // Whiskey Lake U; Amber Lake Y; Comet Lake U
824                        0x9e | // Kaby Lake T, H, S, X, Coffee Lake S, H, E
825                        0xa5   // Comet Lake S, H
826                            => Microarchitecture::IntelSkylake,
827                        0x55   // Skylake SP, X, DE, W, Cascade Lake SP, X, W; Cooper Lake
828                            => Microarchitecture::IntelSkylakeServer,
829                        0x66   // Cannon Lake U
830                            => Microarchitecture::IntelCannonLake,
831                        0x6a | // Ice Lake Server SP
832                        0x6c | // Ice Lake Server DE
833                        0x7d | // Ice Lake Y
834                        0x7e   // Ice Lake U
835                            => Microarchitecture::IntelIceLake,
836                        0x8c | // Tiger Lake UP
837                        0x8d   // Tiger Lake H
838                            => Microarchitecture::IntelTigerLake,
839                        0x97 | // Alder Lake S
840                        0x9a   // Alder Lake H, P, U
841                            => Microarchitecture::IntelAlderLake,
842                        0xb7   // Raptor Lake S
843                            => Microarchitecture::IntelRaptorLake,
844
845                        // Small cores
846                        0x1c | // Silverthorne, Diamondville, Pineview
847                        0x26   // Lincroft
848                            => Microarchitecture::IntelBonnell,
849                        0x27 | // Penwell
850                        0x35 | // Cloverview
851                        0x36   // Cedarview
852                            => Microarchitecture::IntelSaltwell,
853                        0x37 | // Bay Trail
854                        0x4a | // Tangier
855                        0x4d | // Avoton, Rangeley
856                        0x5a | // Anniedale
857                        0x5d   // SoFIA
858                            => Microarchitecture::IntelSilvermont,
859                        0x4c   // Cherry Trail, Braswell
860                            => Microarchitecture::IntelAirmont,
861                        0x5c | // Apollo Lake, Broxton
862                        0x5f   // Denverton
863                            => Microarchitecture::IntelGoldmont,
864                        0x7a   // Gemini Lake
865                            => Microarchitecture::IntelGoldmontPlus,
866                        0x8a | // Lakefield
867                        0x96 | // Elkhart Lake
868                        0x9c   // Jasper Lake
869                            => Microarchitecture::IntelTremont,
870                        _ => Microarchitecture::Unknown,
871                    },
872                    _ => Microarchitecture::Unknown,
873                }
874            }
875            Vendor::Amd => {
876                // Table largely from https://en.wikichip.org/wiki/amd/cpuid
877                match self.family() {
878                    0x15 // Bulldozer/Piledriver/Steamroller/Excavator
879                        => Microarchitecture::AmdFamilyBulldozer,
880                    0x16 // Jaguar
881                        => Microarchitecture::AmdFamilyJaguar,
882                    0x17 // Zen 1 - 2
883                        => Microarchitecture::AmdFamilyZen,
884                    0x19 // Zen 3 - 4
885                        => Microarchitecture::AmdFamilyZen3,
886                    _ => Microarchitecture::Unknown,
887                }
888            }
889            Vendor::Unknown => Microarchitecture::Unknown,
890        }
891    }
892}
893
894/// The list is not exhaustive and is in chronological order within groupings.
895/// Microarchictectures that use the same processor (and, say, differ only in
896/// performance or SoC composition) are regarded as equivalent.
897#[derive(Clone, Debug, Eq, PartialEq)]
898pub enum Microarchitecture {
899    Unknown,
900
901    // Intel Core family (64-bit, display family 0x6).
902    IntelCore2,
903    IntelNehalem,
904    IntelWestmere,
905    IntelSandyBridge,
906    IntelIvyBridge,
907    IntelHaswell,
908    IntelBroadwell,
909    /// Includes Kaby/Coffee/Whiskey/Amber/Comet Lake.
910    IntelSkylake,
911    /// Includes Cascade/Cooper Lake.
912    IntelSkylakeServer,
913    /// A 10nm prototype only ever released on the Intel Core i3-8121U.
914    IntelCannonLake,
915    IntelIceLake,
916    IntelTigerLake,
917    IntelAlderLake,
918    IntelRaptorLake,
919
920    // Intel Atom family.
921    IntelBonnell,
922    IntelSaltwell,
923    IntelSilvermont,
924    IntelAirmont,
925    IntelGoldmont,
926    IntelGoldmontPlus,
927    IntelTremont,
928
929    // AMD families.
930    /// Bulldozer/Piledriver/Steamroller/Excavator.
931    AmdFamilyBulldozer,
932    /// Jaguar.
933    AmdFamilyJaguar,
934    /// Zen 1, 1+, 2.
935    AmdFamilyZen,
936    /// Zen 3, 4.
937    AmdFamilyZen3,
938}
939
940impl fmt::Display for Microarchitecture {
941    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
942        f.write_str(match self {
943            Self::Unknown => "Unknown",
944            Self::IntelCore2 => "Intel Core 2",
945            Self::IntelNehalem => "Intel Nehalem",
946            Self::IntelWestmere => "Intel Westmere",
947            Self::IntelSandyBridge => "Intel Sandy Bridge",
948            Self::IntelIvyBridge => "Intel Ivy Bridge",
949            Self::IntelBroadwell => "Intel Broadwell",
950            Self::IntelHaswell => "Intel Haswell",
951            Self::IntelSkylake => "Intel Skylake",
952            Self::IntelSkylakeServer => "Intel Skylake (server)",
953            Self::IntelCannonLake => "Intel Cannon Lake",
954            Self::IntelIceLake => "Intel Ice Lake",
955            Self::IntelTigerLake => "Intel Tiger Lake",
956            Self::IntelAlderLake => "Intel Alder Lake",
957            Self::IntelRaptorLake => "Intel Raptor Lake",
958            Self::IntelBonnell => "Intel Bonnell",
959            Self::IntelSaltwell => "Intel Saltwell",
960            Self::IntelSilvermont => "Intel Silvermont",
961            Self::IntelAirmont => "Intel Airmont",
962            Self::IntelGoldmont => "Intel Goldmont",
963            Self::IntelGoldmontPlus => "Intel Goldmont Plus",
964            Self::IntelTremont => "Intel Tremont",
965            Self::AmdFamilyBulldozer => "AMD Bulldozer",
966            Self::AmdFamilyJaguar => "AMD Jaguar",
967            Self::AmdFamilyZen => "AMD Zen 1-2",
968            Self::AmdFamilyZen3 => "AMD Zen 3-4",
969        })
970    }
971}
972
973#[cfg(test)]
974mod tests {
975    use super::*;
976
977    // Basic exercise of the above utilities.
978    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
979    #[test]
980    fn feature_detection() {
981        use regio::x86::DirectCpuid;
982
983        let vendor = VendorString::from_cpuid(DirectCpuid {});
984        println!("Vendor string: {}", vendor.as_str().unwrap_or("invalid vendor string"));
985    }
986
987    #[test]
988    fn amd_vendor_string() {
989        use regio::testing::x86::FakeCpuid;
990
991        // EBX, ECX, EDX copied verbatim from the manual.
992        let mut cpuid = FakeCpuid::new();
993        cpuid
994            .set(VENDOR_STRING_B, 0x6874_7541)
995            .set(VENDOR_STRING_C, 0x444d_4163)
996            .set(VENDOR_STRING_D, 0x6974_6e65);
997        let vendor_str = VendorString::from_cpuid(&cpuid);
998        assert_eq!(vendor_str, VendorString::AMD);
999    }
1000}