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/// Leaf/Function 0x8000_0008, EAX
618///
619/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
620/// [amd/vol3]: E.4.7  Function 8000_0008h-Processor Capacity Parameters and
621/// Extended Feature Identification.
622pub const ADDRESS_SIZE_INFO: CpuidValue<0x8000_0008, 0, EAX, AddressSizeInfo> = CpuidValue::new();
623
624layout!({
625    /// The layout of EAX in [`ADDRESS_SIZE_INFO`].
626    pub struct AddressSizeInfo(u32);
627    {
628        let __ @ 31..24;
629        let guest_phys_addr_bits @ 23..16;
630        let linear_addr_bits @ 15..8;
631        let phys_addr_bits @ 7..0;
632    }
633});
634
635/// Performance timestamp counter size.
636#[bitfield_repr(u8)]
637#[derive(Clone, Copy)]
638pub enum PerfTimestampCounterSize {
639    Bits40 = 0b00,
640    Bits48 = 0b01,
641    Bits56 = 0b10,
642    Bits64 = 0b11,
643}
644
645/// Leaf/Function 0x8000_0008, ECX
646///
647/// [amd/vol3]: E.4.7  Function 8000_0008h—Processor Capacity Parameters and
648/// Extended Feature Identification.
649pub const EXTENDED_SIZE_INFO: CpuidValue<0x8000_0008, 0, ECX, ExtendedSizeInfo> = CpuidValue::new();
650
651layout!({
652    /// The layout of ECX in [`EXTENDED_SIZE_INFO`].
653    pub struct ExtendedSizeInfo(u32);
654    {
655        let __ @ 31..18;
656        let perf_tsc_size @ 17..16: PerfTimestampCounterSize;
657        let apic_id_size @ 15..12;
658        let __ @ 11..8;
659        let nc @ 7..0;
660    }
661});
662
663/// Leaf/Function 0x8000_0008, EBX
664///
665/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
666/// [amd/vol3]: E.4.7  Function 8000_0008h-Processor Capacity Parameters and Extended Feature
667/// Identification.
668pub const EXTENDED_AMD_FEATURES_B: CpuidValue<0x8000_0008, 0, EBX, ExtendedAmdFeaturesB> =
669    CpuidValue::new();
670
671layout!({
672    /// Layout for [`EXTENDED_AMD_FEATURES_B`].
673    ///
674    /// [amd/ibc] details bits [18:14] and 12.
675    /// [amd/ssbd] details bits [26:24].
676    pub struct ExtendedAmdFeaturesB(u32);
677    {
678        let __ @ 31..27;
679        let ssb_no @ 26;
680        let virt_ssbd @ 25;
681        let ssbd @ 24;
682        let __ @ 23..19;
683        let prefers_ibrs @ 18;
684        let stibp_always_on @ 17;
685        let ibrs_always_on @ 16;
686        let stibp @ 15;
687        let ibrs @ 14;
688        let __ @ 13;
689        let ibpb @ 12;
690        let __ @ 11..10;
691        let wbnoinvd @ 9;
692        let mcommit @ 8;
693        let __ @ 7..5;
694        let rdpru @ 4;
695        let __ @ 3;
696        let rstr_fp_err_ptrs @ 2;
697        let inst_ret_cnt_msr @ 1;
698        let clzero @ 0;
699    }
700});
701
702/// Leaf/Function 0x8000_001e, EAX
703///
704/// [amd/vol3]: E.4.16  Function 8000_001Eh—Processor Topology Information.
705pub const EXTENDED_APIC_ID: CpuidValue<0x8000_001e, 0, EAX, ExtendedApicId> = CpuidValue::new();
706
707layout!({
708    /// The layout of EAX in [`EXTENDED_APIC_ID`].
709    pub struct ExtendedApicId(u32);
710    {
711        let x2apic_id @ 31..0;
712    }
713});
714
715/// Leaf/Function 0x8000_001e, EBX
716///
717/// [amd/vol3]: E.4.16  Function 8000_001Eh—Processor Topology Information.
718pub const COMPUTE_UNIT_INFO: CpuidValue<0x8000_001e, 0, EBX, ComputeUnitInfo> = CpuidValue::new();
719
720layout!({
721    /// The layout of EBX in [`COMPUTE_UNIT_INFO`].
722    pub struct ComputeUnitInfo(u32);
723    {
724        let __ @ 31..16;
725        let threads_per_compute_unit @ 15..8;
726        let compute_unit_id @ 7..0;
727    }
728});
729
730/// Leaf/Function 0x8000_001e, ECX
731///
732/// [amd/vol3]: E.4.16  Function 8000_001Eh—Processor Topology Information.
733pub const NODE_INFO: CpuidValue<0x8000_001e, 0, ECX, NodeInfo> = CpuidValue::new();
734
735layout!({
736    /// The layout of ECX in [`NODE_INFO`].
737    pub struct NodeInfo(u32);
738    {
739        let __ @ 31..11;
740        let nodes_per_package @ 10..8;
741        let node_id @ 7..0;
742    }
743});
744
745/// Leaf/Function 0x1, EAX.
746///
747/// [intel/vol2]: Table 3-8.  Information Returned by CPUID Instruction.
748/// [amd/vol3]: E.3.2  Function 1h-Processor and Processor Feature Identifiers
749/// [intel/vol2]: Figure 3-6.  Version Information Returned by CPUID in EAX.
750/// [amd/vol3]: E.3.2, CPUID Fn0000_0001_EAX  Family, Model, Stepping Identifiers.
751pub const VERSION_INFO: CpuidValue<0x1, 0x0, EAX, VersionInfo> = CpuidValue::new();
752
753/// Encoding for intel processor type in [`VersionInfo`].
754///
755/// [intel/vol2]: Table 3-9.  Processor Type Field.
756#[bitfield_repr(u8)]
757#[derive(Clone, Copy)]
758pub enum IntelProcessorType {
759    OriginalOem = 0b00,
760    IntelOverdrive = 0b01,
761    Dual = 0b10,
762    Reserved = 0b11,
763}
764
765layout!({
766    /// The layout of EAX in [`VERSION_INFO`].
767    pub struct VersionInfo(u32);
768    {
769        let __ @ 31..28;
770        let extended_family @ 27..20;
771        let extended_model @ 19..16;
772        let __ @ 15..14;
773        let intel_processor @ 13..12: IntelProcessorType;
774        let base_family @ 11..8;
775        let base_model @ 7..4;
776        let stepping @ 3..0;
777    }
778});
779
780impl VersionInfo {
781    pub fn family(self) -> u8 {
782        if self.base_family() == 0xf {
783            self.base_family() + self.extended_family()
784        } else {
785            self.base_family()
786        }
787    }
788
789    pub fn model(self) -> u8 {
790        if self.base_family() == 0x6 || self.base_family() == 0xf {
791            (self.extended_model() << 4) | self.base_model()
792        } else {
793            self.base_model()
794        }
795    }
796
797    /// Attempts to derives the microarchitecture with the assumption that the
798    /// system relates to a particular vendor.
799    pub fn microarchitecture(self, vendor: Vendor) -> Microarchitecture {
800        // TODO(https://fxbug.dev/42138852): check in a source of truth for this information and
801        // refer to that here.
802        match vendor {
803            Vendor::Intel => {
804                // Table largely from https://en.wikichip.org/wiki/intel/cpuid
805                match self.family() {
806                    0x6 => match self.model() {
807                        // Big cores
808                        0x0f | // Merom
809                        0x16 | // Merom L
810                        0x17 | // Penryn, Wolfdale, Yorkfield, Harpertown, QC
811                        0x1d   // Dunnington
812                            => Microarchitecture::IntelCore2,
813                        0x1a | // Bloomfield, EP, WS
814                        0x1e | // Lynnfield, Clarksfield
815                        0x1f | // Auburndale, Havendale
816                        0x2e   // EX
817                            => Microarchitecture::IntelNehalem,
818                        0x25 | // Arrandale, Clarkdale
819                        0x2c | // Gulftown, EP
820                        0x2f   // EX
821                            => Microarchitecture::IntelWestmere,
822                        0x2a | // M, H
823                        0x2d   // E, EN, EP
824                            => Microarchitecture::IntelSandyBridge,
825                        0x3a | // M, H, Gladden
826                        0x3e   // E, EN, EP, EX
827                            => Microarchitecture::IntelIvyBridge,
828                        0x3c | // S
829                        0x3f | // E, EP, EX
830                        0x45 | // ULT
831                        0x46   // GT3E
832                            => Microarchitecture::IntelHaswell,
833                        0x3d | // U, Y, S
834                        0x47 | // H, C, W
835                        0x56 | // DE, Hewitt Lake
836                        0x4f   // E, EP, EX
837                            => Microarchitecture::IntelBroadwell,
838                        0x4e | // Skylake Y, U
839                        0x5e | // Skylake DT, H, S
840                        0x8e | // Kaby Lake Y, U, Coffee Lake U;
841                               // Whiskey Lake U; Amber Lake Y; Comet Lake U
842                        0x9e | // Kaby Lake T, H, S, X, Coffee Lake S, H, E
843                        0xa5   // Comet Lake S, H
844                            => Microarchitecture::IntelSkylake,
845                        0x55   // Skylake SP, X, DE, W, Cascade Lake SP, X, W; Cooper Lake
846                            => Microarchitecture::IntelSkylakeServer,
847                        0x66   // Cannon Lake U
848                            => Microarchitecture::IntelCannonLake,
849                        0x6a | // Ice Lake Server SP
850                        0x6c | // Ice Lake Server DE
851                        0x7d | // Ice Lake Y
852                        0x7e   // Ice Lake U
853                            => Microarchitecture::IntelIceLake,
854                        0x8c | // Tiger Lake UP
855                        0x8d   // Tiger Lake H
856                            => Microarchitecture::IntelTigerLake,
857                        0x97 | // Alder Lake S
858                        0x9a   // Alder Lake H, P, U
859                            => Microarchitecture::IntelAlderLake,
860                        0xb7   // Raptor Lake S
861                            => Microarchitecture::IntelRaptorLake,
862
863                        // Small cores
864                        0x1c | // Silverthorne, Diamondville, Pineview
865                        0x26   // Lincroft
866                            => Microarchitecture::IntelBonnell,
867                        0x27 | // Penwell
868                        0x35 | // Cloverview
869                        0x36   // Cedarview
870                            => Microarchitecture::IntelSaltwell,
871                        0x37 | // Bay Trail
872                        0x4a | // Tangier
873                        0x4d | // Avoton, Rangeley
874                        0x5a | // Anniedale
875                        0x5d   // SoFIA
876                            => Microarchitecture::IntelSilvermont,
877                        0x4c   // Cherry Trail, Braswell
878                            => Microarchitecture::IntelAirmont,
879                        0x5c | // Apollo Lake, Broxton
880                        0x5f   // Denverton
881                            => Microarchitecture::IntelGoldmont,
882                        0x7a   // Gemini Lake
883                            => Microarchitecture::IntelGoldmontPlus,
884                        0x8a | // Lakefield
885                        0x96 | // Elkhart Lake
886                        0x9c   // Jasper Lake
887                            => Microarchitecture::IntelTremont,
888                        _ => Microarchitecture::Unknown,
889                    },
890                    _ => Microarchitecture::Unknown,
891                }
892            }
893            Vendor::Amd => {
894                // Table largely from https://en.wikichip.org/wiki/amd/cpuid
895                match self.family() {
896                    0x15 // Bulldozer/Piledriver/Steamroller/Excavator
897                        => Microarchitecture::AmdFamilyBulldozer,
898                    0x16 // Jaguar
899                        => Microarchitecture::AmdFamilyJaguar,
900                    0x17 // Zen 1 - 2
901                        => Microarchitecture::AmdFamilyZen,
902                    0x19 // Zen 3 - 4
903                        => Microarchitecture::AmdFamilyZen3,
904                    _ => Microarchitecture::Unknown,
905                }
906            }
907            Vendor::Unknown => Microarchitecture::Unknown,
908        }
909    }
910}
911
912/// The list is not exhaustive and is in chronological order within groupings.
913/// Microarchictectures that use the same processor (and, say, differ only in
914/// performance or SoC composition) are regarded as equivalent.
915#[derive(Clone, Debug, Eq, PartialEq)]
916pub enum Microarchitecture {
917    Unknown,
918
919    // Intel Core family (64-bit, display family 0x6).
920    IntelCore2,
921    IntelNehalem,
922    IntelWestmere,
923    IntelSandyBridge,
924    IntelIvyBridge,
925    IntelHaswell,
926    IntelBroadwell,
927    /// Includes Kaby/Coffee/Whiskey/Amber/Comet Lake.
928    IntelSkylake,
929    /// Includes Cascade/Cooper Lake.
930    IntelSkylakeServer,
931    /// A 10nm prototype only ever released on the Intel Core i3-8121U.
932    IntelCannonLake,
933    IntelIceLake,
934    IntelTigerLake,
935    IntelAlderLake,
936    IntelRaptorLake,
937
938    // Intel Atom family.
939    IntelBonnell,
940    IntelSaltwell,
941    IntelSilvermont,
942    IntelAirmont,
943    IntelGoldmont,
944    IntelGoldmontPlus,
945    IntelTremont,
946
947    // AMD families.
948    /// Bulldozer/Piledriver/Steamroller/Excavator.
949    AmdFamilyBulldozer,
950    /// Jaguar.
951    AmdFamilyJaguar,
952    /// Zen 1, 1+, 2.
953    AmdFamilyZen,
954    /// Zen 3, 4.
955    AmdFamilyZen3,
956}
957
958impl fmt::Display for Microarchitecture {
959    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
960        f.write_str(match self {
961            Self::Unknown => "Unknown",
962            Self::IntelCore2 => "Intel Core 2",
963            Self::IntelNehalem => "Intel Nehalem",
964            Self::IntelWestmere => "Intel Westmere",
965            Self::IntelSandyBridge => "Intel Sandy Bridge",
966            Self::IntelIvyBridge => "Intel Ivy Bridge",
967            Self::IntelBroadwell => "Intel Broadwell",
968            Self::IntelHaswell => "Intel Haswell",
969            Self::IntelSkylake => "Intel Skylake",
970            Self::IntelSkylakeServer => "Intel Skylake (server)",
971            Self::IntelCannonLake => "Intel Cannon Lake",
972            Self::IntelIceLake => "Intel Ice Lake",
973            Self::IntelTigerLake => "Intel Tiger Lake",
974            Self::IntelAlderLake => "Intel Alder Lake",
975            Self::IntelRaptorLake => "Intel Raptor Lake",
976            Self::IntelBonnell => "Intel Bonnell",
977            Self::IntelSaltwell => "Intel Saltwell",
978            Self::IntelSilvermont => "Intel Silvermont",
979            Self::IntelAirmont => "Intel Airmont",
980            Self::IntelGoldmont => "Intel Goldmont",
981            Self::IntelGoldmontPlus => "Intel Goldmont Plus",
982            Self::IntelTremont => "Intel Tremont",
983            Self::AmdFamilyBulldozer => "AMD Bulldozer",
984            Self::AmdFamilyJaguar => "AMD Jaguar",
985            Self::AmdFamilyZen => "AMD Zen 1-2",
986            Self::AmdFamilyZen3 => "AMD Zen 3-4",
987        })
988    }
989}
990
991#[cfg(test)]
992mod tests {
993    use super::*;
994
995    // Basic exercise of the above utilities.
996    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
997    #[test]
998    fn feature_detection() {
999        use regio::x86::DirectCpuid;
1000
1001        let vendor = VendorString::from_cpuid(DirectCpuid {});
1002        println!("Vendor string: {}", vendor.as_str().unwrap_or("invalid vendor string"));
1003    }
1004
1005    #[test]
1006    fn amd_vendor_string() {
1007        use regio::testing::x86::FakeCpuid;
1008
1009        // EBX, ECX, EDX copied verbatim from the manual.
1010        let mut cpuid = FakeCpuid::new();
1011        cpuid
1012            .set(VENDOR_STRING_B, 0x6874_7541)
1013            .set(VENDOR_STRING_C, 0x444d_4163)
1014            .set(VENDOR_STRING_D, 0x6974_6e65);
1015        let vendor_str = VendorString::from_cpuid(&cpuid);
1016        assert_eq!(vendor_str, VendorString::AMD);
1017    }
1018
1019    #[test]
1020    fn address_size_info() {
1021        let info = AddressSizeInfo::from(0x002b_3027);
1022        assert_eq!(info.phys_addr_bits(), 0x27);
1023        assert_eq!(info.linear_addr_bits(), 0x30);
1024        assert_eq!(info.guest_phys_addr_bits(), 0x2b);
1025    }
1026}