1use core::sync::atomic::AtomicU64;
2
3#[cfg(target_arch = "x86_64")]
4pub use x64::X64 as Arch;
5
6#[cfg(target_arch = "aarch64")]
7pub use aarch64::Aarch64 as Arch;
8
9const DEFAULT_ACPI_TIMER_FREQUENCY: u64 = 3579545;
11
12static PERF_FREQUENCY: AtomicU64 = AtomicU64::new(0);
13
14pub trait ArchFunctionality {
15 fn cpu_count() -> u64;
17 fn perf_frequency() -> u64;
19 fn cpu_count_start() -> u64 {
21 0
22 }
23 fn cpu_count_end() -> u64 {
25 u64::MAX
26 }
27}
28
29#[cfg(target_arch = "x86_64")]
30pub(crate) mod x64 {
31 use super::*;
32 use core::{
33 arch::x86_64::{self, CpuidResult},
34 sync::atomic::Ordering,
35 };
36
37 pub struct X64;
38 impl ArchFunctionality for X64 {
39 fn cpu_count() -> u64 {
40 #[cfg(feature = "validate_cpu_features")]
41 {
42 if (unsafe { x86_64::__cpuid(0x01) }.edx & 0x10) != 0x10 {
44 panic!("CPU does not support TSC");
45 }
46 if (unsafe { x86_64::__cpuid(0x80000007) }.edx & 0x100) != 0x100 {
48 panic!("CPU does not support Invariant TSC");
49 }
50 }
51 unsafe { x86_64::_rdtsc() }
52 }
53
54 fn perf_frequency() -> u64 {
55 let cached = PERF_FREQUENCY.load(Ordering::Relaxed);
56 if cached != 0 {
57 return cached;
58 }
59
60 let hypervisor_leaf = unsafe { x86_64::__cpuid(0x1) };
61 let is_vm = (hypervisor_leaf.ecx & (1 << 31)) != 0;
62
63 if is_vm {
64 log::warn!("Running in a VM - CPUID-based frequency may not be reliable.");
65 }
66
67 let CpuidResult {
68 eax, ebx, ecx, ..
72 } = unsafe { x86_64::__cpuid(0x15) };
73
74 if !is_vm && ecx != 0 && eax != 0 && ebx != 0 {
76 let frequency = (ecx as u64 * ebx as u64) / eax as u64;
77 PERF_FREQUENCY.store(frequency, Ordering::Relaxed);
78 log::trace!("Used CPUID leaf 0x15 to determine CPU frequency: {}", frequency);
79 return frequency;
80 }
81
82 let CpuidResult { eax, .. } = unsafe { x86_64::__cpuid(0x16) };
85 if eax != 0 {
86 let frequency = (eax * 1_000_000) as u64;
88 PERF_FREQUENCY.store(frequency, Ordering::Relaxed);
89 log::trace!("Used CPUID leaf 0x16 to determine CPU frequency: {}", frequency);
90 return frequency;
91 }
92
93 log::warn!("Unable to determine CPU frequency using CPUID leaves, using default ACPI timer frequency");
94
95 PERF_FREQUENCY.store(DEFAULT_ACPI_TIMER_FREQUENCY, Ordering::Relaxed);
96 DEFAULT_ACPI_TIMER_FREQUENCY
97 }
98 }
99}
100
101#[cfg(target_arch = "aarch64")]
102pub(crate) mod aarch64 {
103 use super::*;
104 use aarch64_cpu::registers::{self, Readable};
105 pub struct Aarch64;
106 impl ArchFunctionality for Aarch64 {
107 fn cpu_count() -> u64 {
108 registers::CNTPCT_EL0.get()
109 }
110
111 fn perf_frequency() -> u64 {
112 registers::CNTFRQ_EL0.get()
113 }
114 }
115}