strat9_kernel/arch/x86_64/
cpuid.rs1use crate::sync::SpinLock;
8use alloc::string::String;
9use bitflags::bitflags;
10use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
11
12bitflags! {
13 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
15 pub struct CpuFeatures: u64 {
16 const SSE3 = 1 << 0;
18 const SSSE3 = 1 << 1;
19 const FMA = 1 << 2;
20 const SSE4_1 = 1 << 3;
21 const SSE4_2 = 1 << 4;
22 const POPCNT = 1 << 5;
23 const AES_NI = 1 << 6;
24 const XSAVE = 1 << 7;
25 const AVX = 1 << 8;
26 const F16C = 1 << 9;
27 const VMX = 1 << 10;
28 const X2APIC = 1 << 11;
29 const FPU = 1 << 16;
31 const TSC = 1 << 17;
32 const APIC = 1 << 18;
33 const SSE = 1 << 19;
34 const SSE2 = 1 << 20;
35 const FXSR = 1 << 21;
36 const AVX2 = 1 << 32;
38 const AVX512F = 1 << 33;
39 const AVX512BW = 1 << 34;
40 const AVX512VL = 1 << 35;
41 const SHA = 1 << 36;
42 const NX = 1 << 48;
44 const PAGES_1G = 1 << 49;
45 const RDTSCP = 1 << 50;
46 const LONG_MODE = 1 << 51;
47 const SVM = 1 << 56;
49 }
50}
51
52pub const XCR0_X87: u64 = 1 << 0;
54pub const XCR0_SSE: u64 = 1 << 1;
55pub const XCR0_AVX: u64 = 1 << 2;
56pub const XCR0_OPMASK: u64 = 1 << 5;
57pub const XCR0_ZMM_HI256: u64 = 1 << 6;
58pub const XCR0_HI16_ZMM: u64 = 1 << 7;
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum CpuVendor {
62 Intel,
63 Amd,
64 Unknown,
65}
66
67#[derive(Debug, Clone)]
69pub struct CpuInfo {
70 pub vendor: CpuVendor,
71 pub features: CpuFeatures,
72 pub max_xcr0: u64,
73 pub xsave_size: usize,
74 pub family: u8,
75 pub model: u8,
76 pub stepping: u8,
77 pub model_name: [u8; 48],
78 model_name_len: usize,
79}
80
81impl CpuInfo {
82 pub fn model_name_str(&self) -> &str {
84 let bytes = &self.model_name[..self.model_name_len];
85 core::str::from_utf8(bytes).unwrap_or("Unknown")
86 }
87
88 pub fn vendor_string(&self) -> &'static str {
90 match self.vendor {
91 CpuVendor::Intel => "GenuineIntel",
92 CpuVendor::Amd => "AuthenticAMD",
93 CpuVendor::Unknown => "Unknown",
94 }
95 }
96}
97
98static HOST_CPU: SpinLock<Option<CpuInfo>> = SpinLock::new(None);
99static INITIALIZED: AtomicBool = AtomicBool::new(false);
100
101pub(crate) static HOST_DEFAULT_XCR0_CACHE: AtomicU64 = AtomicU64::new(0);
105
106pub fn init() {
108 let info = detect();
109 log::info!(
110 "[CPUID] {} {} (family={} model={} stepping={})",
111 info.vendor_string(),
112 info.model_name_str(),
113 info.family,
114 info.model,
115 info.stepping,
116 );
117 log::info!(
118 "[CPUID] features={:?}, max_xcr0={:#x}, xsave_size={}",
119 info.features,
120 info.max_xcr0,
121 info.xsave_size,
122 );
123 *HOST_CPU.lock() = Some(info);
124 INITIALIZED.store(true, Ordering::Release);
125 HOST_DEFAULT_XCR0_CACHE.store(host_default_xcr0(), Ordering::Release);
126}
127
128pub fn host() -> CpuInfo {
130 HOST_CPU
131 .lock()
132 .clone()
133 .expect("cpuid::init() not called yet")
134}
135
136pub fn host_uses_xsave() -> bool {
138 INITIALIZED.load(Ordering::Acquire)
139 && HOST_CPU
140 .lock()
141 .as_ref()
142 .map_or(false, |h| h.features.contains(CpuFeatures::XSAVE))
143}
144
145fn detect() -> CpuInfo {
147 let cpuid = super::cpuid;
148
149 let (max_leaf, ebx0, ecx0, edx0) = cpuid(0, 0);
151 let vendor = match (ebx0, edx0, ecx0) {
152 (0x756E_6547, 0x4965_6E69, 0x6C65_746E) => CpuVendor::Intel,
153 (0x6874_7541, 0x6974_6E65, 0x444D_4163) => CpuVendor::Amd,
154 _ => CpuVendor::Unknown,
155 };
156
157 let mut features = CpuFeatures::empty();
158
159 let (eax1, _ebx1, ecx1, edx1) = if max_leaf >= 1 {
161 cpuid(1, 0)
162 } else {
163 (0, 0, 0, 0)
164 };
165
166 let stepping = (eax1 & 0xF) as u8;
167 let base_family = (eax1 >> 8) & 0xF;
168 let base_model = (eax1 >> 4) & 0xF;
169 let ext_model = (eax1 >> 16) & 0xF;
170 let ext_family = (eax1 >> 20) & 0xFF;
171 let mut family_full: u16 = base_family as u16;
172 let mut model: u8 = base_model as u8;
173 if base_family == 6 || base_family == 15 {
174 model |= (ext_model << 4) as u8;
175 }
176 if base_family == 15 {
177 family_full += ext_family as u16;
178 }
179 let family = family_full as u8;
180
181 if ecx1 & (1 << 0) != 0 {
182 features |= CpuFeatures::SSE3;
183 }
184 if ecx1 & (1 << 9) != 0 {
185 features |= CpuFeatures::SSSE3;
186 }
187 if ecx1 & (1 << 12) != 0 {
188 features |= CpuFeatures::FMA;
189 }
190 if ecx1 & (1 << 19) != 0 {
191 features |= CpuFeatures::SSE4_1;
192 }
193 if ecx1 & (1 << 20) != 0 {
194 features |= CpuFeatures::SSE4_2;
195 }
196 if ecx1 & (1 << 23) != 0 {
197 features |= CpuFeatures::POPCNT;
198 }
199 if ecx1 & (1 << 25) != 0 {
200 features |= CpuFeatures::AES_NI;
201 }
202 if ecx1 & (1 << 26) != 0 {
203 features |= CpuFeatures::XSAVE;
204 }
205 if ecx1 & (1 << 28) != 0 {
206 features |= CpuFeatures::AVX;
207 }
208 if ecx1 & (1 << 21) != 0 {
209 features |= CpuFeatures::X2APIC;
210 }
211 if ecx1 & (1 << 29) != 0 {
212 features |= CpuFeatures::F16C;
213 }
214 if ecx1 & (1 << 5) != 0 {
215 features |= CpuFeatures::VMX;
216 }
217
218 if edx1 & (1 << 0) != 0 {
219 features |= CpuFeatures::FPU;
220 }
221 if edx1 & (1 << 4) != 0 {
222 features |= CpuFeatures::TSC;
223 }
224 if edx1 & (1 << 9) != 0 {
225 features |= CpuFeatures::APIC;
226 }
227 if edx1 & (1 << 24) != 0 {
228 features |= CpuFeatures::FXSR;
229 }
230 if edx1 & (1 << 25) != 0 {
231 features |= CpuFeatures::SSE;
232 }
233 if edx1 & (1 << 26) != 0 {
234 features |= CpuFeatures::SSE2;
235 }
236
237 if max_leaf >= 7 {
239 let (_eax7, ebx7, _ecx7, _edx7) = cpuid(7, 0);
240 if ebx7 & (1 << 5) != 0 {
241 features |= CpuFeatures::AVX2;
242 }
243 if ebx7 & (1 << 16) != 0 {
244 features |= CpuFeatures::AVX512F;
245 }
246 if ebx7 & (1 << 29) != 0 {
247 features |= CpuFeatures::SHA;
248 }
249 if ebx7 & (1 << 30) != 0 {
250 features |= CpuFeatures::AVX512BW;
251 }
252 if ebx7 & (1 << 31) != 0 {
253 features |= CpuFeatures::AVX512VL;
254 }
255 }
256
257 let (mut max_xcr0, mut xsave_size) = (XCR0_X87 | XCR0_SSE, 512usize);
259 if features.contains(CpuFeatures::XSAVE) && max_leaf >= 0x0D {
260 let (eax_d, ebx_d, _ecx_d, edx_d) = cpuid(0x0D, 0);
261 max_xcr0 = ((edx_d as u64) << 32) | eax_d as u64;
262 xsave_size = ebx_d as usize;
263 }
264
265 let (max_ext, _, _, _) = cpuid(0x8000_0000, 0);
267 if max_ext >= 0x8000_0001 {
268 let (_eax_e, _ebx_e, ecx_e, edx_e) = cpuid(0x8000_0001, 0);
269 if edx_e & (1 << 20) != 0 {
270 features |= CpuFeatures::NX;
271 }
272 if edx_e & (1 << 26) != 0 {
273 features |= CpuFeatures::PAGES_1G;
274 }
275 if edx_e & (1 << 27) != 0 {
276 features |= CpuFeatures::RDTSCP;
277 }
278 if edx_e & (1 << 29) != 0 {
279 features |= CpuFeatures::LONG_MODE;
280 }
281 if ecx_e & (1 << 2) != 0 {
282 features |= CpuFeatures::SVM;
283 }
284 }
285
286 let mut model_name = [0u8; 48];
288 let mut model_name_len = 0usize;
289 if max_ext >= 0x8000_0004 {
290 for (i, leaf) in (0x8000_0002u32..=0x8000_0004).enumerate() {
291 let (a, b, c, d) = cpuid(leaf, 0);
292 let offset = i * 16;
293 model_name[offset..offset + 4].copy_from_slice(&a.to_le_bytes());
294 model_name[offset + 4..offset + 8].copy_from_slice(&b.to_le_bytes());
295 model_name[offset + 8..offset + 12].copy_from_slice(&c.to_le_bytes());
296 model_name[offset + 12..offset + 16].copy_from_slice(&d.to_le_bytes());
297 }
298 model_name_len = model_name
299 .iter()
300 .rposition(|&b| b != 0 && b != b' ')
301 .map_or(0, |p| p + 1);
302 }
303
304 CpuInfo {
305 vendor,
306 features,
307 max_xcr0,
308 xsave_size,
309 family,
310 model,
311 stepping,
312 model_name,
313 model_name_len,
314 }
315}
316
317pub fn xcr0_for_features(features: CpuFeatures) -> u64 {
320 let mut xcr0 = XCR0_X87 | XCR0_SSE;
321 if features.contains(CpuFeatures::AVX) {
322 xcr0 |= XCR0_AVX;
323 }
324 if features.contains(CpuFeatures::AVX512F) {
325 xcr0 |= XCR0_OPMASK | XCR0_ZMM_HI256 | XCR0_HI16_ZMM;
326 }
327 let h = host();
328 xcr0 & h.max_xcr0
329}
330
331pub fn xsave_size_for_xcr0(xcr0: u64) -> usize {
334 if !host_uses_xsave() {
335 return 512;
336 }
337 let h = host();
338 if xcr0 == h.max_xcr0 {
339 return h.xsave_size;
340 }
341
342 let mut size = 576usize; for comp in 2..63 {
347 if xcr0 & (1u64 << comp) == 0 {
348 continue;
349 }
350 let (eax, ebx, _ecx, _edx) = super::cpuid(0x0D, comp);
351 let comp_size = eax as usize;
352 let comp_offset = ebx as usize;
353 size = size.max(comp_offset + comp_size);
354 }
355 size.min(h.xsave_size)
356}
357
358#[inline]
361pub fn host_default_xcr0_fast() -> u64 {
362 let cached = HOST_DEFAULT_XCR0_CACHE.load(Ordering::Acquire);
363 if cached != 0 {
364 return cached;
365 }
366 host_default_xcr0()
367}
368
369pub fn tsc_frequency_khz() -> Option<u64> {
378 let max_leaf = super::cpuid(0, 0).0;
379 if max_leaf < 0x15 {
380 return None;
382 }
383 let (eax, ebx, ecx, _edx) = super::cpuid(0x15, 0);
384 let core_crystal_hz = ecx as u64;
385
386 if core_crystal_hz != 0 {
388 let denom = eax as u64;
389 let num = ebx as u64;
390 if denom != 0 {
391 return Some(core_crystal_hz * num / denom / 1_000);
393 }
394 }
395 None
396}
397
398pub fn host_default_xcr0() -> u64 {
399 if INITIALIZED.load(Ordering::Acquire) {
400 HOST_CPU.lock().as_ref().map_or(XCR0_X87 | XCR0_SSE, |h| {
401 let mut xcr0 = XCR0_X87 | XCR0_SSE;
404 if h.features.contains(CpuFeatures::AVX) {
405 xcr0 |= XCR0_AVX;
406 }
407 if h.features.contains(CpuFeatures::AVX512F) {
408 xcr0 |= XCR0_OPMASK | XCR0_ZMM_HI256 | XCR0_HI16_ZMM;
409 }
410 xcr0 & h.max_xcr0
412 })
413 } else {
414 XCR0_X87 | XCR0_SSE
415 }
416}
417
418pub fn features_to_flags_string(f: CpuFeatures) -> String {
420 let mut flags = String::new();
421 let table: &[(CpuFeatures, &str)] = &[
422 (CpuFeatures::FPU, "fpu"),
423 (CpuFeatures::TSC, "tsc"),
424 (CpuFeatures::APIC, "apic"),
425 (CpuFeatures::FXSR, "fxsr"),
426 (CpuFeatures::SSE, "sse"),
427 (CpuFeatures::SSE2, "sse2"),
428 (CpuFeatures::SSE3, "sse3"),
429 (CpuFeatures::SSSE3, "ssse3"),
430 (CpuFeatures::SSE4_1, "sse4_1"),
431 (CpuFeatures::SSE4_2, "sse4_2"),
432 (CpuFeatures::POPCNT, "popcnt"),
433 (CpuFeatures::AES_NI, "aes"),
434 (CpuFeatures::XSAVE, "xsave"),
435 (CpuFeatures::AVX, "avx"),
436 (CpuFeatures::F16C, "f16c"),
437 (CpuFeatures::FMA, "fma"),
438 (CpuFeatures::AVX2, "avx2"),
439 (CpuFeatures::AVX512F, "avx512f"),
440 (CpuFeatures::AVX512BW, "avx512bw"),
441 (CpuFeatures::AVX512VL, "avx512vl"),
442 (CpuFeatures::SHA, "sha_ni"),
443 (CpuFeatures::X2APIC, "x2apic"),
444 (CpuFeatures::NX, "nx"),
445 (CpuFeatures::PAGES_1G, "pdpe1gb"),
446 (CpuFeatures::RDTSCP, "rdtscp"),
447 (CpuFeatures::LONG_MODE, "lm"),
448 (CpuFeatures::VMX, "vmx"),
449 (CpuFeatures::SVM, "svm"),
450 ];
451 for &(feat, name) in table {
452 if f.contains(feat) {
453 if !flags.is_empty() {
454 flags.push(' ');
455 }
456 flags.push_str(name);
457 }
458 }
459 flags
460}