ostd/arch/x86/cpu/
extension.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! x86 ISA extensions.
4
5use core::arch::x86_64::CpuidResult;
6
7use bitflags::bitflags;
8use spin::Once;
9
10use super::cpuid::cpuid;
11
12/// Detects available x86 ISA extensions.
13pub(in crate::arch) fn init() {
14    let mut global_isa_extensions = IsaExtensions::empty();
15
16    for ext_leaf in EXTENSION_TABLE.iter() {
17        let Some(CpuidResult { ebx, ecx, edx, .. }) = cpuid(ext_leaf.leaf, ext_leaf.subleaf) else {
18            continue;
19        };
20
21        for ext_data in ext_leaf.data.iter() {
22            let bits = match ext_data.reg {
23                Reg::Ebx => ebx,
24                Reg::Ecx => ecx,
25                Reg::Edx => edx,
26            };
27            if bits & (1 << ext_data.bit) != 0 {
28                global_isa_extensions |= ext_data.flag;
29            }
30        }
31    }
32
33    log::info!("Detected ISA extensions: {:?}", global_isa_extensions);
34
35    GLOBAL_ISA_EXTENSIONS.call_once(|| global_isa_extensions);
36}
37
38/// Checks if the specified set of ISA extensions are available.
39pub fn has_extensions(required: IsaExtensions) -> bool {
40    GLOBAL_ISA_EXTENSIONS.get().unwrap().contains(required)
41}
42
43static GLOBAL_ISA_EXTENSIONS: Once<IsaExtensions> = Once::new();
44
45macro_rules! define_isa_extensions {
46    { $(leaf $leaf:literal, subleaf $subleaf:literal => {
47        $($name:ident, $reg:ident ($bit:literal), $doc:literal; )*
48    })* } => {
49        define_isa_extension_type! {
50            $($($name, $doc;)*)*
51        }
52
53        const EXTENSION_TABLE: &[ExtensionLeaf] = &[
54            $(ExtensionLeaf {
55                leaf: $leaf,
56                subleaf: $subleaf,
57                data: &[
58                    $(ExtensionData {
59                        reg: Reg::$reg,
60                        bit: $bit,
61                        flag: IsaExtensions::$name,
62                    },)*
63                ]
64            },)*
65        ];
66    };
67}
68
69macro_rules! define_isa_extension_type {
70    { $($name:ident, $doc:literal;)* } => {
71        bitflags! {
72            /// x86 ISA extensions.
73            pub struct IsaExtensions: u128 {
74                $(
75                    #[doc = $doc]
76                    const $name = 1u128 << ${index()};
77                )*
78            }
79        }
80    };
81}
82
83/// Extensions that describe in a CPUID leaf.
84struct ExtensionLeaf {
85    leaf: u32,
86    subleaf: u32,
87    data: &'static [ExtensionData],
88}
89
90/// An extension and its position (i.e., the register and the bit) in the CPUID result.
91struct ExtensionData {
92    reg: Reg,
93    bit: u32,
94    flag: IsaExtensions,
95}
96
97enum Reg {
98    Ebx,
99    Ecx,
100    Edx,
101}
102
103define_isa_extensions! {
104    leaf 1, subleaf 0 => {
105        X2APIC,       Ecx(21), "The processor supports x2APIC feature.";
106        TSC_DEADLINE, Ecx(24), "The processor's local APIC timer supports \
107                                one-shot operation using a TSC deadline value.";
108        XSAVE,        Ecx(26), "The processor supports the XSAVE/XRSTOR \
109                                processor extended states feature, \
110                                the XSETBV/XGETBV instructions, and XCR0.";
111        AVX,          Ecx(28), "The processor supports the AVX instruction extensions.";
112        RDRAND,       Ecx(30), "The processor supports RDRAND instruction.";
113
114        XAPIC,        Edx( 9), "APIC On-Chip.";
115    }
116
117    leaf 7, subleaf 0 => {
118        FSGSBASE,     Ebx( 0), "Supports RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE.";
119        AVX512F,      Ebx(16), "Supports the AVX512F instruction extensions.";
120    }
121}