use core::{sync::atomic::AtomicU64, u64};
#[cfg(target_arch = "x86_64")]
pub use x64::X64 as Arch;
#[cfg(target_arch = "aarch64")]
pub use aarch64::Aarch64 as Arch;
const DEFAULT_ACPI_TIMER_FREQUENCY: u64 = 3579545;
static PERF_FREQUENCY: AtomicU64 = AtomicU64::new(0);
pub trait ArchFunctionality {
fn cpu_count() -> u64;
fn perf_frequency() -> u64;
fn cpu_count_start() -> u64 {
0
}
fn cpu_count_end() -> u64 {
u64::MAX
}
}
#[cfg(target_arch = "x86_64")]
pub(crate) mod x64 {
use super::*;
use core::{
arch::x86_64::{self, CpuidResult},
sync::atomic::Ordering,
};
pub struct X64;
impl ArchFunctionality for X64 {
fn cpu_count() -> u64 {
#[cfg(feature = "validate_cpu_features")]
{
if (unsafe { x86_64::__cpuid(0x01) }.edx & 0x10) != 0x10 {
panic!("CPU does not support TSC");
}
if (unsafe { x86_64::__cpuid(0x80000007) }.edx & 0x100) != 0x100 {
panic!("CPU does not support Invariant TSC");
}
}
unsafe { x86_64::_rdtsc() }
}
fn perf_frequency() -> u64 {
let cached = PERF_FREQUENCY.load(Ordering::Relaxed);
if cached != 0 {
return cached;
}
let hypervisor_leaf = unsafe { x86_64::__cpuid(0x1) };
let is_vm = (hypervisor_leaf.ecx & (1 << 31)) != 0;
if is_vm {
log::warn!("Running in a VM - CPUID-based frequency may not be reliable.");
}
let CpuidResult {
eax, ebx, ecx, ..
} = unsafe { x86_64::__cpuid(0x15) };
if !is_vm && ecx != 0 && eax != 0 && ebx != 0 {
let frequency = (ecx as u64 * ebx as u64) / eax as u64;
PERF_FREQUENCY.store(frequency, Ordering::Relaxed);
log::trace!("Used CPUID leaf 0x15 to determine CPU frequency: {}", frequency);
return frequency;
}
let CpuidResult { eax, .. } = unsafe { x86_64::__cpuid(0x16) };
if eax != 0 {
let frequency = (eax * 1_000_000) as u64;
PERF_FREQUENCY.store(frequency, Ordering::Relaxed);
log::trace!("Used CPUID leaf 0x16 to determine CPU frequency: {}", frequency);
return frequency;
}
log::warn!("Unable to determine CPU frequency using CPUID leaves, using default ACPI timer frequency");
PERF_FREQUENCY.store(DEFAULT_ACPI_TIMER_FREQUENCY, Ordering::Relaxed);
DEFAULT_ACPI_TIMER_FREQUENCY
}
}
}
#[cfg(target_arch = "aarch64")]
pub(crate) mod aarch64 {
use super::*;
use aarch64_cpu::registers::{self, Readable};
pub struct Aarch64;
impl ArchFunctionality for Aarch64 {
fn cpu_count() -> u64 {
registers::CNTPCT_EL0.get()
}
fn perf_frequency() -> u64 {
registers::CNTFRQ_EL0.get()
}
}
}