1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (C) 2019-2021 Aleo Systems Inc.
// This file is part of the aleo-std library.

// The aleo-std library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The aleo-std library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with the aleo-std library. If not, see <https://www.gnu.org/licenses/>.

/// Uses Rust's `cpuid` function from the `arch` module.
pub(crate) mod native_cpuid {
    /// Low-level data-structure to store result of cpuid instruction.
    #[derive(Copy, Clone, Eq, PartialEq)]
    #[repr(C)]
    pub struct CpuIdResult {
        /// Return value EAX register
        pub eax: u32,
        /// Return value EBX register
        pub ebx: u32,
        /// Return value ECX register
        pub ecx: u32,
        /// Return value EDX register
        pub edx: u32,
    }

    #[allow(unreachable_code)]
    pub fn cpuid_count(a: u32, c: u32) -> CpuIdResult {
        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
        {
            #[cfg(all(target_arch = "x86", not(target_env = "sgx"), target_feature = "sse"))]
            use core::arch::x86 as arch;
            #[cfg(all(target_arch = "x86_64", not(target_env = "sgx")))]
            use core::arch::x86_64 as arch;

            // Safety: CPUID is supported on all x86_64 CPUs and all x86 CPUs with SSE, but not by SGX.
            let result = unsafe { arch::__cpuid_count(a, c) };
            return CpuIdResult {
                eax: result.eax,
                ebx: result.ebx,
                ecx: result.ecx,
                edx: result.edx,
            };
        }

        CpuIdResult {
            eax: 22,
            ebx: 1970169159,
            ecx: 1818588270,
            edx: 1231384169,
        }
    }
}

///
/// Vendor Info String (LEAF=0x0)
///
/// The vendor info is a 12-byte (96 bit) long string stored in `ebx`, `edx` and
/// `ecx` by the corresponding `cpuid` instruction.
///
#[derive(PartialEq, Eq)]
#[repr(C)]
struct VendorInfo {
    ebx: u32,
    edx: u32,
    ecx: u32,
}

impl VendorInfo {
    /// Return vendor identification as string, such as "AuthenticAMD" or "GenuineIntel".
    fn as_str(&self) -> &str {
        let brand_string_start = self as *const VendorInfo as *const u8;
        let slice = unsafe {
            // Safety: VendorInfo is laid out with repr(C) and exactly
            // 12 byte long without any padding.
            core::slice::from_raw_parts(brand_string_start, core::mem::size_of::<VendorInfo>())
        };
        core::str::from_utf8(slice).unwrap_or("InvalidVendorString")
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Cpu {
    AMD,
    Intel,
    Unknown,
}

///
/// Returns a new Cpu enum.
///
/// The vendor leaf will contain a ASCII readable string such as "GenuineIntel"
/// for Intel CPUs or "AuthenticAMD" for AMD CPUs.
///
#[allow(clippy::absurd_extreme_comparisons)]
pub fn get_cpu() -> Cpu {
    const EAX_VENDOR_INFO: u32 = 0x0;

    // Check if a non extended leaf  (`val`) is supported.
    let vendor_leaf = native_cpuid::cpuid_count(EAX_VENDOR_INFO, 0);
    let is_leaf_supported = EAX_VENDOR_INFO <= vendor_leaf.eax;

    match is_leaf_supported {
        true => {
            let vendor = VendorInfo {
                ebx: vendor_leaf.ebx,
                ecx: vendor_leaf.ecx,
                edx: vendor_leaf.edx,
            };

            match vendor.as_str() {
                "AuthenticAMD" => Cpu::AMD,
                "GenuineIntel" => Cpu::Intel,
                _ => Cpu::Unknown,
            }
        }
        false => Cpu::Unknown,
    }
}

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

    #[test]
    fn test_get_cpu() {
        println!("{:?}", get_cpu());
    }
}