use super::sys::{
ERROR_SUCCESS, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, RRF_RT_REG_QWORD, RegCloseKey, RegGetValueW,
RegOpenKeyExW,
};
use crate::cache::Features;
use crate::features::Feature;
const CPU0_SUBKEY: &str = r"HARDWARE\DESCRIPTION\System\CentralProcessor\0";
#[allow(
dead_code,
reason = "fields populated for future decoder expansion / diagnostics"
)]
#[derive(Default, Debug, Copy, Clone)]
pub struct IdRegisters {
pub midr_el1: Option<u64>,
pub aa64pfr0: Option<u64>,
pub aa64pfr1: Option<u64>,
pub aa64isar0: Option<u64>,
pub aa64isar1: Option<u64>,
pub aa64isar2: Option<u64>,
pub aa64mmfr0: Option<u64>,
pub aa64mmfr1: Option<u64>,
pub aa64mmfr2: Option<u64>,
pub aa64mmfr3: Option<u64>,
}
impl IdRegisters {
pub fn read() -> Self {
let Some(hk) = open_cpu0() else {
return Self::default();
};
let r = Self {
midr_el1: read_qword(hk, "CP 4000"),
aa64pfr0: read_qword(hk, "CP 4020"),
aa64pfr1: read_qword(hk, "CP 4021"),
aa64isar0: read_qword(hk, "CP 4030"),
aa64isar1: read_qword(hk, "CP 4031"),
aa64isar2: read_qword(hk, "CP 4032"),
aa64mmfr0: read_qword(hk, "CP 4038"),
aa64mmfr1: read_qword(hk, "CP 4039"),
aa64mmfr2: read_qword(hk, "CP 403A"),
aa64mmfr3: read_qword(hk, "CP 403B"),
};
close(hk);
r
}
}
pub(crate) fn fill(f: &mut Features) {
let r = IdRegisters::read();
if let Some(isar0) = r.aa64isar0 {
isar0_decode(isar0, f);
}
if let Some(isar1) = r.aa64isar1 {
isar1_decode(isar1, f);
}
if let Some(isar2) = r.aa64isar2 {
isar2_decode(isar2, f);
}
if let Some(pfr0) = r.aa64pfr0 {
pfr0_decode(pfr0, f);
}
if let Some(pfr1) = r.aa64pfr1 {
pfr1_decode(pfr1, f);
}
if let Some(mmfr2) = r.aa64mmfr2 {
mmfr2_decode(mmfr2, f);
}
}
fn isar0_decode(isar0: u64, f: &mut Features) {
if field(isar0, 28, 4) >= 1 {
*f = f.with(Feature::Rdm);
}
if field(isar0, 32, 4) >= 1 {
*f = f.with(Feature::Sha3);
}
if field(isar0, 40, 4) >= 1 {
*f = f.with(Feature::Sm4);
}
if field(isar0, 48, 4) >= 1 {
*f = f.with(Feature::Fhm);
}
let ts = field(isar0, 52, 4);
if ts >= 1 {
*f = f.with(Feature::FlagM);
}
if ts >= 2 {
*f = f.with(Feature::FlagM2);
}
if field(isar0, 60, 4) >= 1 {
*f = f.with(Feature::Rand);
}
}
fn isar1_decode(isar1: u64, f: &mut Features) {
let dpb = field(isar1, 0, 4);
if dpb >= 1 {
*f = f.with(Feature::Dpb);
}
if dpb >= 2 {
*f = f.with(Feature::Dpb2);
}
if field(isar1, 4, 4) >= 1 || field(isar1, 8, 4) >= 1 {
*f = f.with(Feature::Paca).with(Feature::Pacg);
}
if field(isar1, 16, 4) >= 1 {
*f = f.with(Feature::Fcma);
}
let lrcpc = field(isar1, 20, 4);
if lrcpc >= 2 {
*f = f.with(Feature::Rcpc2);
}
if field(isar1, 32, 4) >= 1 {
*f = f.with(Feature::FrintTs);
}
if field(isar1, 36, 4) >= 1 {
*f = f.with(Feature::Sb);
}
if field(isar1, 44, 4) >= 1 {
*f = f.with(Feature::Bf16);
}
if field(isar1, 52, 4) >= 1 {
*f = f.with(Feature::I8mm);
}
}
fn isar2_decode(isar2: u64, f: &mut Features) {
if field(isar2, 4, 4) >= 1 {
*f = f.with(Feature::WfxT);
}
if field(isar2, 16, 4) >= 1 {
*f = f.with(Feature::Mops);
}
if field(isar2, 20, 4) >= 1 {
*f = f.with(Feature::Hbc);
}
if field(isar2, 52, 4) >= 1 {
*f = f.with(Feature::Cssc);
}
}
fn pfr0_decode(pfr0: u64, f: &mut Features) {
let advsimd = field(pfr0, 20, 4);
if advsimd == 1 {
*f = f.with(Feature::Fp16);
}
if field(pfr0, 48, 4) >= 1 {
*f = f.with(Feature::Dit);
}
}
fn mmfr2_decode(mmfr2: u64, f: &mut Features) {
if field(mmfr2, 32, 4) >= 1 {
*f = f.with(Feature::Lse2);
}
}
fn pfr1_decode(pfr1: u64, f: &mut Features) {
if field(pfr1, 0, 4) >= 1 {
*f = f.with(Feature::Bti);
}
if field(pfr1, 4, 4) >= 1 {
*f = f.with(Feature::Ssbs);
}
if field(pfr1, 8, 4) >= 1 {
*f = f.with(Feature::Mte);
}
if field(pfr1, 24, 4) >= 1 {
*f = f.with(Feature::Sme);
}
}
#[inline]
const fn field(reg: u64, shift: u32, bits: u32) -> u64 {
(reg >> shift) & ((1u64 << bits) - 1)
}
#[expect(unsafe_code, reason = "Win32 registry FFI entry points")]
fn open_cpu0() -> Option<HKEY> {
let wide = wide_null(CPU0_SUBKEY);
let mut hk: HKEY = core::ptr::null_mut();
let status = unsafe {
RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
wide.as_ptr(),
0,
KEY_READ,
&mut hk as *mut _,
)
};
if status == ERROR_SUCCESS {
Some(hk)
} else {
None
}
}
#[expect(unsafe_code, reason = "Win32 registry FFI entry points")]
fn read_qword(hk: HKEY, value: &str) -> Option<u64> {
let wide = wide_null(value);
let mut data: u64 = 0;
let mut cb: u32 = core::mem::size_of::<u64>() as u32;
let status = unsafe {
RegGetValueW(
hk,
core::ptr::null(),
wide.as_ptr(),
RRF_RT_REG_QWORD,
core::ptr::null_mut(),
&mut data as *mut u64 as *mut _,
&mut cb as *mut u32,
)
};
(status == ERROR_SUCCESS).then_some(data)
}
#[expect(unsafe_code, reason = "Win32 registry FFI entry points")]
fn close(hk: HKEY) {
let _ = unsafe { RegCloseKey(hk) };
}
fn wide_null(s: &str) -> Vec<u16> {
let mut v: Vec<u16> = s.encode_utf16().collect();
v.push(0);
v
}