use crate::backend::Datum;
use static_assertions::const_assert_eq;
use std::arch::x86_64::{CpuidResult, __cpuid, __cpuid_count};
use std::convert::From;
use std::io::{Error, ErrorKind, Result};
use std::mem::size_of;
use std::str::from_utf8;
const AUTHENTIC_AMD: &str = "AuthenticAMD";
const GENUINE_INTEL: &str = "GenuineIntel";
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Vendor {
Amd,
Intel,
}
impl Vendor {
pub fn get() -> Result<Self> {
let res = unsafe { __cpuid_count(0x00000000, 0x00000000) };
let name = [
res.ebx.to_le_bytes(),
res.edx.to_le_bytes(),
res.ecx.to_le_bytes(),
]
.concat();
let name = from_utf8(&name[..]).map_err(|e| {
Error::new(
ErrorKind::Other,
format!("vendor string parse error: {:#?}", e),
)
})?;
match name {
AUTHENTIC_AMD => Ok(Self::Amd),
GENUINE_INTEL => Ok(Self::Intel),
name => Err(Error::new(
ErrorKind::Other,
format!("unsupported vendor: '{}'", name),
)),
}
}
pub const fn id(&self) -> &str {
match self {
Vendor::Amd => AUTHENTIC_AMD,
Vendor::Intel => GENUINE_INTEL,
}
}
}
pub struct CpuId {
pub name: &'static str,
pub leaf: u32,
pub subl: u32,
pub func: fn(CpuidResult) -> (bool, Option<String>),
pub vend: Option<Vendor>,
}
impl CpuId {
pub fn cpu_identifier(
res: CpuidResult,
required_vendor: Option<Vendor>,
) -> (bool, Option<String>) {
let vendor = Vendor::get();
let supported_cpu =
required_vendor.is_none() || required_vendor.as_ref() == vendor.as_ref().ok();
let identifier_fmt = |name: Option<&str>| {
format!(
"{} | {}",
name.unwrap_or("[unknown model]"),
vendor
.map(|v| v.id().to_string())
.unwrap_or_else(|e| format!("[{}]", e))
)
};
if res.eax < 0x80000004 {
return (supported_cpu, Some(identifier_fmt(None)));
}
const LEAVES: [u32; 3] = [0x80000002, 0x80000003, 0x80000004];
const CHUNK_SIZE: usize = size_of::<u32>() * 4;
const MODEL_LEN: usize = LEAVES.len() * CHUNK_SIZE;
const_assert_eq!(MODEL_LEN, 48);
let mut model: [u8; MODEL_LEN] = [0; MODEL_LEN];
for (i, leaf) in LEAVES.into_iter().enumerate() {
let res = unsafe { __cpuid(leaf) };
let string_part = [
res.eax.to_le_bytes(),
res.ebx.to_le_bytes(),
res.ecx.to_le_bytes(),
res.edx.to_le_bytes(),
]
.concat();
let start = i * CHUNK_SIZE;
let destination = &mut model[start..start + CHUNK_SIZE];
destination.clone_from_slice(&string_part);
}
let model_name = String::from_utf8_lossy(model.as_slice());
(
supported_cpu,
Some(identifier_fmt(Some(model_name.trim_matches(char::from(0))))),
)
}
}
impl From<&CpuId> for Datum {
fn from(cpuid: &CpuId) -> Datum {
let datum = Datum {
name: cpuid.name.into(),
pass: false,
info: None,
mesg: None,
};
let this_vendor = match Vendor::get() {
Ok(v) => v,
Err(_) => return datum,
};
let req_vendor = cpuid.vend.unwrap_or(this_vendor);
let (pass, info) = if this_vendor == req_vendor {
(cpuid.func)(unsafe { __cpuid_count(cpuid.leaf, cpuid.subl) })
} else {
(false, None)
};
Datum {
name: datum.name,
pass,
info,
mesg: datum.mesg,
}
}
}