clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! CPUID interface for safe CPU feature detection
//!
//! This module provides a safe, structured interface to the CPUID instruction
//! for detecting CPU features and capabilities. It encapsulates the low-level
//! x86/x86_64 intrinsics with proper error handling and abstraction.

/// CPUID result structure containing the four registers returned by CPUID
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CpuidResult {
    /// EAX register
    pub eax: u32,
    /// EBX register
    pub ebx: u32,
    /// ECX register
    pub ecx: u32,
    /// EDX register
    pub edx: u32,
}

/// CPU vendor information
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CpuVendor {
    /// Intel CPU
    Intel,
    /// AMD CPU
    Amd,
    /// Other/Unknown vendor
    Other,
}

/// CPU feature flags for AVX-512 detection
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Avx512Features {
    /// AVX-512 Foundation
    pub avx512f: bool,
    /// AVX-512 Byte and Word
    pub avx512bw: bool,
    /// AVX-512 Vector Length
    pub avx512vl: bool,
    /// AVX-512 Doubleword and Quadword
    pub avx512dq: bool,
}

/// Execute CPUID instruction with the specified leaf and subleaf
///
/// # Arguments
/// * `leaf` - The CPUID leaf (EAX input)
/// * `subleaf` - The CPUID subleaf (ECX input), defaults to 0
///
/// # Returns
/// The CPUID result containing EAX, EBX, ECX, EDX registers
///
/// # Safety
/// This function is safe as it only reads CPU state and doesn't modify anything.
/// However, CPUID behavior can vary between CPU models and virtualization environments.
#[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")))]
    {
        // Return zero for unsupported architectures
        CpuidResult {
            eax: 0,
            ebx: 0,
            ecx: 0,
            edx: 0,
        }
    }
}

/// Get CPU vendor string
///
/// # Returns
/// CPU vendor as a string slice, or None if unsupported
#[inline]
pub fn get_vendor_string() -> Option<&'static str> {
    let result = cpuid(0, 0);

    // Extract vendor string from EBX, EDX, ECX registers
    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,
    }
}

/// Get CPU vendor as an enum
///
/// # Returns
/// CPU vendor enum value
#[inline]
pub fn get_vendor() -> CpuVendor {
    match get_vendor_string() {
        Some("intel") => CpuVendor::Intel,
        Some("amd") => CpuVendor::Amd,
        _ => CpuVendor::Other,
    }
}

/// Get CPU family, model, and stepping information
///
/// # Returns
/// Tuple of (family, model, stepping) or None if unsupported
#[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;

    // Handle extended family/model for newer CPUs
    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))
}

/// Check if AVX is supported
///
/// # Returns
/// true if AVX is supported, false otherwise
#[inline]
pub fn has_avx() -> bool {
    let result = cpuid(1, 0);
    (result.ecx & (1 << 28)) != 0
}

/// Check if AVX2 is supported
///
/// # Returns
/// true if AVX2 is supported, false otherwise
#[inline]
pub fn has_avx2() -> bool {
    if !has_avx() {
        return false;
    }

    let result = cpuid(7, 0);
    (result.ebx & (1 << 5)) != 0
}

/// Get AVX-512 feature support information
///
/// # Returns
/// Avx512Features struct with individual feature flags
#[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,
    }
}

/// Check if all essential AVX-512 features are supported
///
/// # Returns
/// true if AVX-512 F, BW, VL, and DQ are all supported, false otherwise
#[inline]
pub fn has_avx512_essential() -> bool {
    let features = get_avx512_features();
    features.avx512f && features.avx512bw && features.avx512vl && features.avx512dq
}

/// Check if the system is likely running in a virtualized environment
///
/// # Returns
/// true if virtualization is detected, false otherwise
#[inline]
pub fn is_virtualized() -> bool {
    // Check for hypervisor presence (CPUID leaf 0x40000000)
    let result = cpuid(0x40000000, 0);
    result.eax >= 0x40000000
}

/// Get maximum supported CPUID leaf
///
/// # Returns
/// Maximum CPUID leaf supported by this CPU
#[inline]
pub fn get_max_leaf() -> u32 {
    cpuid(0, 0).eax
}

/// Check if CPUID is supported (for very old CPUs)
///
/// # Returns
/// true if CPUID is supported, false for very old CPUs
#[inline]
pub fn cpuid_supported() -> bool {
    #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
    {
        // On x86/x86_64, we assume CPUID is supported
        // (true for all CPUs since Pentium and later)
        true
    }

    #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
    {
        false
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_cpuid_basic() {
        // Test that CPUID doesn't panic
        let result = cpuid(0, 0);
        assert!(result.eax > 0 || result.ebx > 0 || result.ecx > 0 || result.edx > 0);
    }

    #[test]
    fn test_get_vendor() {
        // Test that vendor detection works
        let vendor = get_vendor();
        // Vendor should be one of the known values
        let _ = vendor;
    }

    #[test]
    fn test_get_family_model_stepping() {
        // Test that family/model/stepping detection works
        let info = get_family_model_stepping();
        // Should return Some on x86/x86_64, None on other architectures
        #[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() {
        // Test AVX detection
        let has_avx = has_avx();
        let has_avx2 = has_avx2();

        // AVX2 requires AVX
        if has_avx2 {
            assert!(has_avx, "AVX2 requires AVX support");
        }
    }

    #[test]
    fn test_avx512_features() {
        // Test AVX-512 feature detection
        let features = get_avx512_features();
        let has_essential = has_avx512_essential();

        // Essential features should all be true if has_essential is true
        if has_essential {
            assert!(
                features.avx512f && features.avx512bw && features.avx512vl && features.avx512dq
            );
        }
    }

    #[test]
    fn test_virtualization_detection() {
        // Test virtualization detection
        let _is_virtual = is_virtualized();
        // This can be true or false depending on the environment
    }

    #[test]
    fn test_cpuid_stability() {
        // Test that CPUID results are stable across multiple calls
        let result1 = cpuid(0, 0);
        let result2 = cpuid(0, 0);
        assert_eq!(result1, result2, "CPUID results should be stable");
    }
}