|
2 | 2 | // |
3 | 3 | // Copyright (c) 2022-2023, 2026 SUSE LLC |
4 | 4 | // |
5 | | -// Authors: Joerg Roedel <jroedel@suse.de> |
6 | | -// Carlos López <clopez@suse.de> |
7 | | - |
8 | | -use core::arch::x86_64::CpuidResult; |
9 | | - |
10 | | -use crate::{platform::cpuid, utils::immut_after_init::ImmutAfterInitCell}; |
11 | | - |
12 | | -/// The CPUID output register for a particular feature |
13 | | -#[derive(Clone, Copy, Debug)] |
14 | | -enum CpuidReg { |
15 | | - Eax, |
16 | | - Ebx, |
17 | | - Ecx, |
18 | | - Edx, |
19 | | -} |
20 | | - |
21 | | -/// A discoverable CPU feature. |
22 | | -/// |
23 | | -/// At the moment this type is used for CPUID detection, but it can |
24 | | -/// be expanded to use other sources of information. |
25 | | -#[derive(Debug)] |
26 | | -struct CpuFeat { |
27 | | - /// Raw value |
28 | | - val: ImmutAfterInitCell<u32>, |
29 | | - /// CPUID leaf |
30 | | - leaf: u32, |
31 | | - /// CPUID subleaf |
32 | | - subleaf: u32, |
33 | | - /// CPUID output register |
34 | | - reg: CpuidReg, |
35 | | - /// Bitshift to apply to raw value |
36 | | - shift: u8, |
37 | | - /// Bitmask to apply to raw value |
38 | | - bitsize: u8, |
39 | | - /// Expected value after shift + mask |
40 | | - expected: u32, |
| 5 | +// Author: Joerg Roedel <jroedel@suse.de> |
| 6 | +// Author: Carlos López <clopez@suse.de> |
| 7 | + |
| 8 | +use cpufeature::CpuidFeature; |
| 9 | +use cpufeature::CpuidRegister; |
| 10 | +use cpufeature::leaves::{ |
| 11 | + CET_SS, INVLPGB_MAX_PAGES, PHYS_ADDR_BITS, PTE_CBIT_POS, X86_FEATURE_PGE, X86_FEATURE_SMAP, |
| 12 | + X86_FEATURE_SMEP, X86_FEATURE_UMIP, X86_FEATURE_X2APIC, X86_FEATURE_XMM, X86_FEATURE_XSAVE, |
| 13 | + X86_FEATURE_XSAVEOPT, XCR0_AVX, XCR0_SSE, XCR0_X87, |
| 14 | +}; |
| 15 | + |
| 16 | +use crate::platform::cpuid_value; |
| 17 | + |
| 18 | +/// Hyper-V CPUID interface signature (`"Hv#1"`) on leaf `0x40000001`. |
| 19 | +const HYPERV_INTERFACE: CpuidFeature = CpuidFeature { |
| 20 | + leaf: 0x4000_0001, |
| 21 | + subleaf: 0, |
| 22 | + register: CpuidRegister::Eax, |
| 23 | + shift: 0, |
| 24 | + width: 32, |
| 25 | +}; |
| 26 | + |
| 27 | +/// Discoverable CPU features backed by [`cpufeature`] leaf descriptors. |
| 28 | +#[repr(usize)] |
| 29 | +#[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 30 | +pub enum Feature { |
| 31 | + X2Apic, |
| 32 | + Xsave, |
| 33 | + Pge, |
| 34 | + Sse1, |
| 35 | + Smep, |
| 36 | + Smap, |
| 37 | + Umip, |
| 38 | + CetSS, |
| 39 | + Xcr0X87, |
| 40 | + Xcr0Sse, |
| 41 | + Xcr0Avx, |
| 42 | + XsaveOpt, |
| 43 | + HyperV, |
| 44 | + PhysAddrSizes, |
| 45 | + InvlpgbMax, |
| 46 | + Cbit, |
41 | 47 | } |
42 | 48 |
|
43 | | -impl CpuFeat { |
44 | | - /// Create a new feature, indicated by a single bit in the specified |
45 | | - /// register in the given CPUID leaf. |
46 | | - const fn new_bit(leaf: u32, reg: CpuidReg, bit: u8) -> Self { |
47 | | - Self::new(leaf, reg, bit, 1, 1) |
48 | | - } |
49 | | - |
50 | | - /// Create a new feature, indicated by the full value of a register |
51 | | - /// in the given CPUID leaf. |
52 | | - const fn new_u32(leaf: u32, reg: CpuidReg, expected: u32) -> Self { |
53 | | - Self::new(leaf, reg, 0, u32::BITS as u8, expected) |
54 | | - } |
55 | | - |
56 | | - /// Create a new CPU feature, detected by querying the given CPUID leaf, and applying |
57 | | - /// a bitshift and bitmask on the specified output register to compare it to the given |
58 | | - /// expected value. |
59 | | - const fn new(leaf: u32, reg: CpuidReg, shift: u8, bitsize: u8, expected: u32) -> Self { |
60 | | - // This method is only called from const context, so these assertions have no |
61 | | - // runtime effect. |
62 | | - assert!((shift as u32) < u32::BITS); |
63 | | - assert!((bitsize as u32) <= u32::BITS); |
64 | | - Self { |
65 | | - val: ImmutAfterInitCell::uninit(), |
66 | | - leaf, |
67 | | - subleaf: 0, |
68 | | - reg, |
69 | | - shift, |
70 | | - bitsize, |
71 | | - expected, |
72 | | - } |
73 | | - } |
74 | | - |
75 | | - /// Create a copy of the given CPU feature, but with the specified |
76 | | - /// subleaf. |
77 | | - const fn with_subfn(mut self, subleaf: u32) -> Self { |
78 | | - self.subleaf = subleaf; |
79 | | - self |
80 | | - } |
81 | | - |
82 | | - /// Get the CPUID register that corresponds to this feature |
83 | | - const fn get_reg(&self, cpuid: &CpuidResult) -> u32 { |
84 | | - match self.reg { |
85 | | - CpuidReg::Eax => cpuid.eax, |
86 | | - CpuidReg::Ebx => cpuid.ebx, |
87 | | - CpuidReg::Ecx => cpuid.ecx, |
88 | | - CpuidReg::Edx => cpuid.edx, |
89 | | - } |
90 | | - } |
91 | | - |
92 | | - const fn mask(&self) -> u32 { |
93 | | - ((1u64 << self.bitsize) - 1) as u32 |
94 | | - } |
95 | | - |
96 | | - fn get_or_init(&self) -> u32 { |
97 | | - if let Ok(val) = self.val.try_get_inner() { |
98 | | - return *val; |
| 49 | +impl Feature { |
| 50 | + const fn descriptor(self) -> &'static CpuidFeature { |
| 51 | + match self { |
| 52 | + Self::X2Apic => &X86_FEATURE_X2APIC, |
| 53 | + Self::Xsave => &X86_FEATURE_XSAVE, |
| 54 | + Self::Pge => &X86_FEATURE_PGE, |
| 55 | + Self::Sse1 => &X86_FEATURE_XMM, |
| 56 | + Self::Smep => &X86_FEATURE_SMEP, |
| 57 | + Self::Smap => &X86_FEATURE_SMAP, |
| 58 | + Self::Umip => &X86_FEATURE_UMIP, |
| 59 | + Self::CetSS => &CET_SS, |
| 60 | + Self::Xcr0X87 => &XCR0_X87, |
| 61 | + Self::Xcr0Sse => &XCR0_SSE, |
| 62 | + Self::Xcr0Avx => &XCR0_AVX, |
| 63 | + Self::XsaveOpt => &X86_FEATURE_XSAVEOPT, |
| 64 | + Self::HyperV => &HYPERV_INTERFACE, |
| 65 | + Self::PhysAddrSizes => &PHYS_ADDR_BITS, |
| 66 | + Self::InvlpgbMax => &INVLPGB_MAX_PAGES, |
| 67 | + Self::Cbit => &PTE_CBIT_POS, |
99 | 68 | } |
100 | | - let val = cpuid(self.leaf, self.subleaf).map_or(0, |c| self.get_reg(&c)); |
101 | | - // If init() fails it means the cell got initialized by someone else |
102 | | - // concurrently, which is always benign. |
103 | | - let _ = self.val.init(val); |
104 | | - val |
105 | | - } |
106 | | - |
107 | | - /// Gets the raw value of this feature, lazily querying CPUID if |
108 | | - /// the value is not cached from a previous query |
109 | | - fn get(&self) -> u32 { |
110 | | - (self.get_or_init() >> self.shift) & self.mask() |
111 | | - } |
112 | | - |
113 | | - /// Checks whether this feature is available by comparing it to |
114 | | - /// its expected value, lazily querying CPUID if the value is not |
115 | | - /// cached from a previous query |
116 | | - fn enabled(&self) -> bool { |
117 | | - self.get() == self.expected |
118 | 69 | } |
119 | 70 | } |
120 | 71 |
|
121 | | -/// Macro to generate a CPU feature lookup table |
122 | | -macro_rules! define_cpu_feats { |
123 | | - ( |
124 | | - $( |
125 | | - $variant:ident => $feat_expr:expr |
126 | | - ),* $(,)? |
127 | | - ) => { |
128 | | - |
129 | | - // Feature enum to use as index |
130 | | - #[repr(usize)] |
131 | | - #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
132 | | - pub enum Feature { |
133 | | - $( $variant, )* |
134 | | - } |
135 | | - |
136 | | - /// The number of defined features |
137 | | - const NUM_FEATS: usize = [ $( stringify!($variant) ),* ].len(); |
138 | | - |
139 | | - static CPU_FEATS: [CpuFeat; NUM_FEATS] = [ |
140 | | - $( $feat_expr, )* |
141 | | - ]; |
142 | | - }; |
143 | | -} |
144 | | - |
145 | | -/// Check if the given feature is enabled by comparing it to its expected |
146 | | -/// value. |
| 72 | +/// Check if the given feature is enabled. |
147 | 73 | pub fn cpu_has_feat(feat: Feature) -> bool { |
148 | | - // Because of #[repr(usize)], this cast matches the array index perfectly. |
149 | | - CPU_FEATS[feat as usize].enabled() |
| 74 | + match feat { |
| 75 | + Feature::HyperV => cpuid_value(feat.descriptor()) == Some(0x3123_7648), |
| 76 | + Feature::PhysAddrSizes | Feature::InvlpgbMax | Feature::Cbit => false, |
| 77 | + _ => cpuid_value(feat.descriptor()) == Some(1), |
| 78 | + } |
150 | 79 | } |
151 | 80 |
|
152 | | -/// Gets the raw value of the given feature |
| 81 | +/// Get the raw CPUID field value for the given feature. |
153 | 82 | pub fn cpu_get_feat(feat: Feature) -> u32 { |
154 | | - CPU_FEATS[feat as usize].get() |
155 | | -} |
156 | | - |
157 | | -define_cpu_feats! { |
158 | | - X2Apic => CpuFeat::new_bit(0x0000_0001, CpuidReg::Ecx, 21), |
159 | | - Xsave => CpuFeat::new_bit(0x0000_0001, CpuidReg::Ecx, 26), |
160 | | - Pge => CpuFeat::new_bit(0x0000_0001, CpuidReg::Edx, 13), |
161 | | - Sse1 => CpuFeat::new_bit(0x0000_0001, CpuidReg::Edx, 25), |
162 | | - Smep => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ebx, 7), |
163 | | - Smap => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ebx, 20), |
164 | | - Umip => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ecx, 2), |
165 | | - CetSS => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ecx, 7), |
166 | | - Xcr0X87 => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 0), |
167 | | - Xcr0Sse => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 1), |
168 | | - Xcr0Avx => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 2), |
169 | | - XsaveOpt => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 0).with_subfn(1), |
170 | | - HyperV => CpuFeat::new_u32(0x40000001, CpuidReg::Eax, 0x31237648), |
171 | | - PhysAddrSizes => CpuFeat::new_u32(0x80000008, CpuidReg::Eax, 0), |
172 | | - InvlpgbMax => CpuFeat::new(0x80000008, CpuidReg::Edx, 0, u16::BITS as u8, 0), |
173 | | - Cbit => CpuFeat::new(0x8000001f, CpuidReg::Ebx, 0, 6, 0), |
| 83 | + cpuid_value(feat.descriptor()).unwrap_or(0) |
174 | 84 | } |
0 commit comments