1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct CpuidResult {
10 pub eax: u32,
12 pub ebx: u32,
14 pub ecx: u32,
16 pub edx: u32,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum CpuVendor {
23 Intel,
25 Amd,
27 Other,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct Avx512Features {
34 pub avx512f: bool,
36 pub avx512bw: bool,
38 pub avx512vl: bool,
40 pub avx512dq: bool,
42}
43
44#[inline]
57pub fn cpuid(leaf: u32, subleaf: u32) -> CpuidResult {
58 #[cfg(target_arch = "x86_64")]
59 unsafe {
60 let result = core::arch::x86_64::__cpuid_count(leaf, subleaf);
61 CpuidResult {
62 eax: result.eax,
63 ebx: result.ebx,
64 ecx: result.ecx,
65 edx: result.edx,
66 }
67 }
68
69 #[cfg(target_arch = "x86")]
70 unsafe {
71 let result = core::arch::x86::__cpuid_count(leaf, subleaf);
72 CpuidResult {
73 eax: result.eax,
74 ebx: result.ebx,
75 ecx: result.ecx,
76 edx: result.edx,
77 }
78 }
79
80 #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
81 {
82 CpuidResult {
84 eax: 0,
85 ebx: 0,
86 ecx: 0,
87 edx: 0,
88 }
89 }
90}
91
92#[inline]
97pub fn get_vendor_string() -> Option<&'static str> {
98 let result = cpuid(0, 0);
99
100 let vendor_bytes = [
102 (result.ebx & 0xFF) as u8,
103 ((result.ebx >> 8) & 0xFF) as u8,
104 ((result.ebx >> 16) & 0xFF) as u8,
105 ((result.ebx >> 24) & 0xFF) as u8,
106 (result.edx & 0xFF) as u8,
107 ((result.edx >> 8) & 0xFF) as u8,
108 ((result.edx >> 16) & 0xFF) as u8,
109 ((result.edx >> 24) & 0xFF) as u8,
110 (result.ecx & 0xFF) as u8,
111 ((result.ecx >> 8) & 0xFF) as u8,
112 ((result.ecx >> 16) & 0xFF) as u8,
113 ((result.ecx >> 24) & 0xFF) as u8,
114 ];
115
116 match &vendor_bytes[0..12] {
117 b"GenuineIntel" => Some("intel"),
118 b"AuthenticAMD" => Some("amd"),
119 b"CentaurHauls" => Some("centaur"),
120 b" Shanghai " => Some("zhaoxin"),
121 b"HygonGenuine" => Some("hygon"),
122 _ => None,
123 }
124}
125
126#[inline]
131pub fn get_vendor() -> CpuVendor {
132 match get_vendor_string() {
133 Some("intel") => CpuVendor::Intel,
134 Some("amd") => CpuVendor::Amd,
135 _ => CpuVendor::Other,
136 }
137}
138
139#[inline]
144pub fn get_family_model_stepping() -> Option<(u8, u8, u8)> {
145 let result = cpuid(1, 0);
146
147 let family_id = ((result.eax >> 8) & 0xF) as u8;
148 let model = ((result.eax >> 4) & 0xF) as u8;
149 let stepping = (result.eax & 0xF) as u8;
150
151 let extended_family = ((result.eax >> 20) & 0xFF) as u8;
153 let extended_model = ((result.eax >> 16) & 0xF) as u8;
154
155 let actual_family = if family_id == 0xF {
156 family_id + extended_family
157 } else {
158 family_id
159 };
160
161 let actual_model = if family_id == 0xF || family_id == 0x6 {
162 (extended_model << 4) | model
163 } else {
164 model
165 };
166
167 Some((actual_family, actual_model, stepping))
168}
169
170#[inline]
175pub fn has_avx() -> bool {
176 let result = cpuid(1, 0);
177 (result.ecx & (1 << 28)) != 0
178}
179
180#[inline]
185pub fn has_avx2() -> bool {
186 if !has_avx() {
187 return false;
188 }
189
190 let result = cpuid(7, 0);
191 (result.ebx & (1 << 5)) != 0
192}
193
194#[inline]
199pub fn get_avx512_features() -> Avx512Features {
200 let result = cpuid(7, 0);
201
202 Avx512Features {
203 avx512f: (result.ebx & (1 << 16)) != 0,
204 avx512bw: (result.ebx & (1 << 30)) != 0,
205 avx512vl: (result.ebx & (1 << 31)) != 0,
206 avx512dq: (result.ebx & (1 << 17)) != 0,
207 }
208}
209
210#[inline]
215pub fn has_avx512_essential() -> bool {
216 let features = get_avx512_features();
217 features.avx512f && features.avx512bw && features.avx512vl && features.avx512dq
218}
219
220#[inline]
225pub fn is_virtualized() -> bool {
226 let result = cpuid(0x40000000, 0);
228 result.eax >= 0x40000000
229}
230
231#[inline]
236pub fn get_max_leaf() -> u32 {
237 cpuid(0, 0).eax
238}
239
240#[inline]
245pub fn cpuid_supported() -> bool {
246 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
247 {
248 true
251 }
252
253 #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
254 {
255 false
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn test_cpuid_basic() {
265 let result = cpuid(0, 0);
267 assert!(result.eax > 0 || result.ebx > 0 || result.ecx > 0 || result.edx > 0);
268 }
269
270 #[test]
271 fn test_get_vendor() {
272 let vendor = get_vendor();
274 let _ = vendor;
276 }
277
278 #[test]
279 fn test_get_family_model_stepping() {
280 let info = get_family_model_stepping();
282 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
284 assert!(info.is_some());
285 #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
286 assert!(info.is_none());
287 }
288
289 #[test]
290 fn test_avx_detection() {
291 let has_avx = has_avx();
293 let has_avx2 = has_avx2();
294
295 if has_avx2 {
297 assert!(has_avx, "AVX2 requires AVX support");
298 }
299 }
300
301 #[test]
302 fn test_avx512_features() {
303 let features = get_avx512_features();
305 let has_essential = has_avx512_essential();
306
307 if has_essential {
309 assert!(
310 features.avx512f && features.avx512bw && features.avx512vl && features.avx512dq
311 );
312 }
313 }
314
315 #[test]
316 fn test_virtualization_detection() {
317 let _is_virtual = is_virtualized();
319 }
321
322 #[test]
323 fn test_cpuid_stability() {
324 let result1 = cpuid(0, 0);
326 let result2 = cpuid(0, 0);
327 assert_eq!(result1, result2, "CPUID results should be stable");
328 }
329}