use crate::arch::Architecture;
pub struct CpuInfo {
pub arch: Architecture,
pub id: u64,
pub vendor: &'static str,
pub frequency_hz: u64,
pub cores: u8,
pub physical_cores: u8,
pub logical_cores: u8,
pub threads_per_core: u8,
pub model_name: [u8; 48],
pub model_name_len: u8,
pub l1_cache_kb: u16,
pub l2_cache_kb: u16,
pub l3_cache_kb: u16,
pub has_ht: bool,
}
pub struct ComponentStatus {
pub name: &'static str,
pub available: bool,
pub capacity: usize,
pub commands: &'static [&'static str],
}
fn read_brand_string(out: &mut [u8; 48]) -> u8 {
let mut pos = 0u8;
for leaf in [0x80000002u32, 0x80000003, 0x80000004] {
if let Some((a, b, c, d)) = crate::arch::shim::cpuid_count(leaf, 0) {
for val in [a, b, c, d] {
let bytes = val.to_le_bytes();
for byte in bytes {
if (pos as usize) < 48 {
out[pos as usize] = byte;
pos += 1;
}
}
}
}
}
while pos > 0 && (out[(pos - 1) as usize] == 0 || out[(pos - 1) as usize] == b' ') {
pos -= 1;
}
pos
}
fn parse_cpu_online_count() -> u8 {
let nr = crate::arch::shim::nr_sched_getaffinity();
if nr == crate::common::error::ERR_NOT_IMPLEMENTED {
return 1;
}
let mut mask = [0u64; 16];
let ret = unsafe {
crate::sys::raw_syscall(
nr,
0, 128, mask.as_mut_ptr() as u64,
0,
0,
0,
)
};
if ret <= 0 {
return 1;
}
let bytes_returned = ret as usize;
let words = bytes_returned / 8;
let mut count: u32 = 0;
let mut i = 0;
while i < words && i < 16 {
count += mask[i].count_ones();
i += 1;
}
if count == 0 { 1 } else if count > 255 { 255 } else { count as u8 }
}
fn read_cpu_n_max_freq_hz(n: u8) -> u64 {
static CPU_FREQ_SIG: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
CPU_FREQ_SIG.store(n as usize, core::sync::atomic::Ordering::Release);
0
}
fn arm_part_to_name(part: u16, buf: &mut [u8; 48]) -> u8 {
let name: &[u8] = match part {
0xd03 => b"Cortex-A53",
0xd04 => b"Cortex-A35",
0xd05 => b"Cortex-A55",
0xd06 => b"Cortex-A65",
0xd07 => b"Cortex-A57",
0xd08 => b"Cortex-A72",
0xd09 => b"Cortex-A73",
0xd0a => b"Cortex-A75",
0xd0b => b"Cortex-A76",
0xd0c => b"Neoverse-N1",
0xd0d => b"Cortex-A77",
0xd40 => b"Neoverse-V1",
0xd41 => b"Cortex-A78",
0xd44 => b"Cortex-X1",
0xd46 => b"Cortex-A510",
0xd47 => b"Cortex-A710",
0xd48 => b"Cortex-X2",
0xd4d => b"Cortex-A715",
0xd4e => b"Cortex-X3",
0xd80 => b"Cortex-A520",
0xd81 => b"Cortex-A720",
0xd82 => b"Cortex-X4",
_ => b"ARM",
};
let len = name.len().min(48);
let mut i = 0;
while i < len {
buf[i] = name[i];
i += 1;
}
len as u8
}
pub fn detect_cpu_info() -> Option<CpuInfo> {
if let Some((eax, ebx, ecx, edx)) = crate::arch::shim::cpuid_count(0, 0) {
let max_leaf = eax;
let mut v = [0u8; 12];
v[0..4].copy_from_slice(&ebx.to_le_bytes());
v[4..8].copy_from_slice(&edx.to_le_bytes());
v[8..12].copy_from_slice(&ecx.to_le_bytes());
let vendor = match &v {
b"GenuineIntel" => "Intel",
b"AuthenticAMD" => "AMD",
_ => "x86_64-unknown",
};
let ext_max = crate::arch::shim::cpuid_count(0x80000000, 0)
.map(|(a, _, _, _)| a)
.unwrap_or(0);
let mut model_name = [0u8; 48];
let model_name_len = if ext_max >= 0x80000004 {
read_brand_string(&mut model_name)
} else {
0
};
let (l2_cache_kb, l3_cache_kb) = if ext_max >= 0x80000006 {
if let Some((_, _, c6, _)) = crate::arch::shim::cpuid_count(0x80000006, 0) {
let l2 = (c6 >> 16) as u16;
let l3_512k = if ext_max >= 0x80000006 {
if let Some((_, _, _, d6)) = crate::arch::shim::cpuid_count(0x80000006, 0) {
((d6 >> 18) & 0x3FFF) as u16
} else {
0
}
} else {
0
};
(l2, l3_512k * 512)
} else {
(0, 0)
}
} else {
(0, 0)
};
let l1_cache_kb = if ext_max >= 0x80000005 {
if let Some((_, _, c5, d5)) = crate::arch::shim::cpuid_count(0x80000005, 0) {
let data = (d5 >> 24) as u16;
let inst = (c5 >> 24) as u16;
data + inst
} else {
0
}
} else {
0
};
let freq_hz = if max_leaf >= 0x16 {
if let Some((fbase, _, _, _)) = crate::arch::shim::cpuid_count(0x16, 0) {
if fbase != 0 {
(fbase as u64) * 1_000_000u64
} else {
estimate_frequency()
}
} else {
estimate_frequency()
}
} else {
estimate_frequency()
};
let mut physical_cores: u8 = 1;
let mut logical_cores: u8 = 1;
if vendor == "AMD" && ext_max >= 0x80000008 {
if let Some((_, _, c8, _)) = crate::arch::shim::cpuid_count(0x80000008, 0) {
let nc = (c8 & 0xFF) as u8;
let total_logical = nc + 1;
logical_cores = total_logical;
if ext_max >= 0x8000001E {
if let Some((_, b1e, _, _)) = crate::arch::shim::cpuid_count(0x8000001E, 0) {
let threads_per_unit = ((b1e >> 8) & 0xFF) as u8 + 1;
physical_cores = total_logical / threads_per_unit;
if physical_cores == 0 {
physical_cores = 1;
}
} else {
physical_cores = total_logical;
}
} else {
physical_cores = total_logical;
}
} else if let Some((_, ebx1, _, _)) = crate::arch::shim::cpuid_count(1, 0) {
let lp = ((ebx1 >> 16) & 0xFF) as u8;
if lp > 0 {
logical_cores = lp;
}
physical_cores = logical_cores;
}
} else if vendor == "Intel" {
if max_leaf >= 0x0B {
if let Some((_, ebx_smt, _, _)) = crate::arch::shim::cpuid_count(0x0B, 0) {
let smt_count = (ebx_smt & 0xFFFF) as u8;
if let Some((_, ebx_core, _, _)) = crate::arch::shim::cpuid_count(0x0B, 1) {
let total = (ebx_core & 0xFFFF) as u8;
if total > 0 && smt_count > 0 {
logical_cores = total;
physical_cores = total / smt_count;
if physical_cores == 0 {
physical_cores = 1;
}
}
}
}
} else if max_leaf >= 4 {
if let Some((eax4, _, _, _)) = crate::arch::shim::cpuid_count(4, 0) {
physical_cores = ((eax4 >> 26) & 0x3F) as u8 + 1;
}
if let Some((_, ebx1, _, _)) = crate::arch::shim::cpuid_count(1, 0) {
logical_cores = ((ebx1 >> 16) & 0xFF) as u8;
if logical_cores == 0 {
logical_cores = physical_cores;
}
}
}
} else if max_leaf >= 0x0B {
if let Some((_, ebx_b1, _, _)) = crate::arch::shim::cpuid_count(0x0B, 1) {
let total = (ebx_b1 & 0xFFFF) as u8;
if total > 0 {
logical_cores = total;
}
}
if let Some((_, ebx_b0, _, _)) = crate::arch::shim::cpuid_count(0x0B, 0) {
let smt = (ebx_b0 & 0xFFFF) as u8;
if smt > 0 && logical_cores > 0 {
physical_cores = logical_cores / smt;
if physical_cores == 0 {
physical_cores = 1;
}
}
}
}
let os_count = parse_cpu_online_count();
if os_count > logical_cores {
let ratio = if logical_cores > 0 && physical_cores > 0 {
logical_cores / physical_cores
} else {
1
};
logical_cores = os_count;
physical_cores = if ratio > 0 { os_count / ratio } else { os_count };
if physical_cores == 0 {
physical_cores = 1;
}
}
let has_ht = if let Some((_, _, _, edx1)) = crate::arch::shim::cpuid_count(1, 0) {
(edx1 & (1 << 28)) != 0
} else {
false
};
let threads_per_core = if physical_cores > 0 {
logical_cores / physical_cores
} else {
1
};
return Some(CpuInfo {
arch: Architecture::X86_64,
id: if let Some((a1, b1, c1, _)) = crate::arch::shim::cpuid_count(1, 0) {
((a1 as u64) << 32) | ((b1 as u64) << 16) | (c1 as u64)
} else {
0
},
vendor,
frequency_hz: freq_hz,
cores: physical_cores,
physical_cores,
logical_cores,
threads_per_core: if threads_per_core == 0 {
1
} else {
threads_per_core
},
model_name,
model_name_len,
l1_cache_kb,
l2_cache_kb,
l3_cache_kb,
has_ht,
});
}
if let Some(midr) = crate::arch::shim::read_aarch64_midr() {
let implementer = ((midr >> 24) & 0xff) as u8;
let vendor = match implementer {
0x41 => "ARM",
0x51 => "Qualcomm",
0x43 => "Cavium",
0x4E => "NVIDIA",
0x50 => "APM",
0x56 => "Marvell",
_ => "aarch64-unknown",
};
let core_count = parse_cpu_online_count();
let freq = read_cpu_n_max_freq_hz(0);
let frequency_hz = if freq > 0 { freq } else { 2_000_000_000u64 };
let part = ((midr >> 4) & 0xfff) as u16;
let mut model_name = [0u8; 48];
let model_name_len = arm_part_to_name(part, &mut model_name);
return Some(CpuInfo {
arch: Architecture::AArch64,
id: midr,
vendor,
frequency_hz,
cores: core_count,
physical_cores: core_count,
logical_cores: core_count,
threads_per_core: 1,
model_name,
model_name_len,
l1_cache_kb: 0,
l2_cache_kb: 0,
l3_cache_kb: 0,
has_ht: false,
});
}
None
}
fn estimate_frequency() -> u64 {
if let Some((denom, numer, crystal, _)) = crate::arch::shim::cpuid_count(0x15, 0) {
if denom != 0 && numer != 0 {
let crystal_hz = if crystal != 0 {
crystal as u64
} else {
25_000_000u64
};
let tsc_hz = crystal_hz * (numer as u64) / (denom as u64);
if tsc_hz > 100_000_000 {
return tsc_hz;
}
}
}
if let Some(freq) = parse_frequency_from_brand() {
return freq;
}
calibrate_tsc()
}
fn parse_frequency_from_brand() -> Option<u64> {
let mut brand = [0u8; 48];
let len = read_brand_string(&mut brand);
if len == 0 {
return None;
}
let s = &brand[..len as usize];
let mut i = 0usize;
while i < s.len() {
if s[i] == b'@' {
let mut j = i + 1;
while j < s.len() && s[j] == b' ' {
j += 1;
}
let start = j;
let mut integer_part: u64 = 0;
let mut frac_part: u64 = 0;
let mut frac_digits: u32 = 0;
let mut in_frac = false;
while j < s.len() && (s[j].is_ascii_digit() || s[j] == b'.') {
if s[j] == b'.' {
in_frac = true;
} else if in_frac {
frac_part = frac_part * 10 + (s[j] - b'0') as u64;
frac_digits += 1;
} else {
integer_part = integer_part * 10 + (s[j] - b'0') as u64;
}
j += 1;
}
if j == start {
i += 1;
continue;
}
let mut multiplier: u64 = 1_000_000;
if j + 2 < s.len() && s[j] == b'G' && s[j + 1] == b'H' && s[j + 2] == b'z' {
multiplier = 1_000_000_000;
} else if j + 2 < s.len() && s[j] == b'M' && s[j + 1] == b'H' && s[j + 2] == b'z' {
multiplier = 1_000_000;
}
let mut hz = integer_part * multiplier;
if frac_digits > 0 {
let mut div = 1u64;
let mut d = 0u32;
while d < frac_digits {
div *= 10;
d += 1;
}
hz += frac_part * multiplier / div;
}
if hz > 100_000_000 {
return Some(hz);
}
}
i += 1;
}
None
}
pub fn calibrate_tsc() -> u64 {
0
}
pub fn model_name_str(info: &CpuInfo) -> &str {
let len = info.model_name_len as usize;
if len == 0 {
return "";
}
let slice = &info.model_name[..len];
core::str::from_utf8(slice).unwrap_or("")
}
pub fn fill_cpu_component(cs: &mut ComponentStatus) -> bool {
if let Some(info) = detect_cpu_info() {
cs.name = "cpu";
cs.available = true;
cs.capacity = info.frequency_hz as usize;
cs.commands = &[];
true
} else {
cs.name = "cpu";
cs.available = false;
cs.capacity = 0;
cs.commands = &[];
false
}
}
#[derive(Copy, Clone)]
pub struct CoreInfo {
pub core_id: u32,
pub frequency_hz: u64,
pub raw_temp: Option<u64>,
}
pub fn detect_cores(out: &mut [CoreInfo]) -> usize {
if let Some(info) = detect_cpu_info() {
let total = info.logical_cores as usize;
let base_freq = info.frequency_hz;
let mut count = 0usize;
while count < out.len() && count < total {
out[count] = CoreInfo {
core_id: count as u32,
frequency_hz: base_freq,
raw_temp: crate::arch::shim::read_msr(0x19C),
};
count += 1;
}
return count;
}
if crate::arch::shim::read_aarch64_midr().is_some() {
let core_count = parse_cpu_online_count() as usize;
let mut count = 0usize;
while count < out.len() && count < core_count {
out[count] = CoreInfo {
core_id: count as u32,
frequency_hz: read_cpu_n_max_freq_hz(count as u8),
raw_temp: None,
};
count += 1;
}
return count;
}
0
}
#[derive(Copy, Clone)]
pub struct RamInfo {
pub total_bytes: usize,
pub modules: u8,
pub ecc_enabled: bool,
}
pub fn detect_ram(info: &mut RamInfo) -> bool {
if let Some((start, end)) = crate::memory::phys::allocator::PhysAllocator::region() {
info.total_bytes = end.saturating_sub(start);
info.modules = 1;
info.ecc_enabled = false;
true
} else {
let total = crate::boot::total_usable_ram();
info.total_bytes = total;
info.modules = 1;
info.ecc_enabled = false;
true
}
}
pub fn read_core_temperatures(out: &mut [Option<i32>]) -> usize {
let mut written = 0usize;
let tjmax = crate::arch::shim::read_msr(0x1A2).map(|v| ((v >> 16) & 0xff) as i32);
for out_slot in out.iter_mut() {
let value = crate::arch::shim::read_msr(0x19C);
if let Some(msr) = value {
let digital = ((msr >> 16) & 0x7f) as i32;
if let Some(tj) = tjmax {
let temp = tj - digital;
*out_slot = Some(temp);
} else {
*out_slot = Some(digital);
}
written += 1;
} else {
*out_slot = None;
}
}
written
}