#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CpuidResult {
pub eax: u32,
pub ebx: u32,
pub ecx: u32,
pub edx: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CpuVendor {
Intel,
Amd,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Avx512Features {
pub avx512f: bool,
pub avx512bw: bool,
pub avx512vl: bool,
pub avx512dq: bool,
}
#[inline]
pub fn cpuid(leaf: u32, subleaf: u32) -> CpuidResult {
#[cfg(target_arch = "x86_64")]
unsafe {
let result = core::arch::x86_64::__cpuid_count(leaf, subleaf);
CpuidResult {
eax: result.eax,
ebx: result.ebx,
ecx: result.ecx,
edx: result.edx,
}
}
#[cfg(target_arch = "x86")]
unsafe {
let result = core::arch::x86::__cpuid_count(leaf, subleaf);
CpuidResult {
eax: result.eax,
ebx: result.ebx,
ecx: result.ecx,
edx: result.edx,
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
{
CpuidResult {
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
}
}
}
#[inline]
pub fn get_vendor_string() -> Option<&'static str> {
let result = cpuid(0, 0);
let vendor_bytes = [
(result.ebx & 0xFF) as u8,
((result.ebx >> 8) & 0xFF) as u8,
((result.ebx >> 16) & 0xFF) as u8,
((result.ebx >> 24) & 0xFF) as u8,
(result.edx & 0xFF) as u8,
((result.edx >> 8) & 0xFF) as u8,
((result.edx >> 16) & 0xFF) as u8,
((result.edx >> 24) & 0xFF) as u8,
(result.ecx & 0xFF) as u8,
((result.ecx >> 8) & 0xFF) as u8,
((result.ecx >> 16) & 0xFF) as u8,
((result.ecx >> 24) & 0xFF) as u8,
];
match &vendor_bytes[0..12] {
b"GenuineIntel" => Some("intel"),
b"AuthenticAMD" => Some("amd"),
b"CentaurHauls" => Some("centaur"),
b" Shanghai " => Some("zhaoxin"),
b"HygonGenuine" => Some("hygon"),
_ => None,
}
}
#[inline]
pub fn get_vendor() -> CpuVendor {
match get_vendor_string() {
Some("intel") => CpuVendor::Intel,
Some("amd") => CpuVendor::Amd,
_ => CpuVendor::Other,
}
}
#[inline]
pub fn get_family_model_stepping() -> Option<(u8, u8, u8)> {
let result = cpuid(1, 0);
let family_id = ((result.eax >> 8) & 0xF) as u8;
let model = ((result.eax >> 4) & 0xF) as u8;
let stepping = (result.eax & 0xF) as u8;
let extended_family = ((result.eax >> 20) & 0xFF) as u8;
let extended_model = ((result.eax >> 16) & 0xF) as u8;
let actual_family = if family_id == 0xF {
family_id + extended_family
} else {
family_id
};
let actual_model = if family_id == 0xF || family_id == 0x6 {
(extended_model << 4) | model
} else {
model
};
Some((actual_family, actual_model, stepping))
}
#[inline]
pub fn has_avx() -> bool {
let result = cpuid(1, 0);
(result.ecx & (1 << 28)) != 0
}
#[inline]
pub fn has_avx2() -> bool {
if !has_avx() {
return false;
}
let result = cpuid(7, 0);
(result.ebx & (1 << 5)) != 0
}
#[inline]
pub fn get_avx512_features() -> Avx512Features {
let result = cpuid(7, 0);
Avx512Features {
avx512f: (result.ebx & (1 << 16)) != 0,
avx512bw: (result.ebx & (1 << 30)) != 0,
avx512vl: (result.ebx & (1 << 31)) != 0,
avx512dq: (result.ebx & (1 << 17)) != 0,
}
}
#[inline]
pub fn has_avx512_essential() -> bool {
let features = get_avx512_features();
features.avx512f && features.avx512bw && features.avx512vl && features.avx512dq
}
#[inline]
pub fn is_virtualized() -> bool {
let result = cpuid(0x40000000, 0);
result.eax >= 0x40000000
}
#[inline]
pub fn get_max_leaf() -> u32 {
cpuid(0, 0).eax
}
#[inline]
pub fn cpuid_supported() -> bool {
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
{
true
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
{
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpuid_basic() {
let result = cpuid(0, 0);
assert!(result.eax > 0 || result.ebx > 0 || result.ecx > 0 || result.edx > 0);
}
#[test]
fn test_get_vendor() {
let vendor = get_vendor();
let _ = vendor;
}
#[test]
fn test_get_family_model_stepping() {
let info = get_family_model_stepping();
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
assert!(info.is_some());
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
assert!(info.is_none());
}
#[test]
fn test_avx_detection() {
let has_avx = has_avx();
let has_avx2 = has_avx2();
if has_avx2 {
assert!(has_avx, "AVX2 requires AVX support");
}
}
#[test]
fn test_avx512_features() {
let features = get_avx512_features();
let has_essential = has_avx512_essential();
if has_essential {
assert!(
features.avx512f && features.avx512bw && features.avx512vl && features.avx512dq
);
}
}
#[test]
fn test_virtualization_detection() {
let _is_virtual = is_virtualized();
}
#[test]
fn test_cpuid_stability() {
let result1 = cpuid(0, 0);
let result2 = cpuid(0, 0);
assert_eq!(result1, result2, "CPUID results should be stable");
}
}