1#[cfg(target_arch = "aarch64")]
8use core::arch::asm;
9
10#[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
11use core::arch::asm;
12
13#[cfg(target_arch = "aarch64")]
31pub fn get_aarch64_counter_freq_hz() -> u64 {
32 #[cfg(feature = "std")]
34 {
35 use std::sync::OnceLock;
36 static VALIDATED_FREQ: OnceLock<u64> = OnceLock::new();
37
38 *VALIDATED_FREQ.get_or_init(|| {
39 let cntfrq: u64;
41 unsafe {
42 asm!("mrs {}, cntfrq_el0", out(reg) cntfrq);
43 }
44
45 if !is_reasonable_aarch64_freq(cntfrq) {
47 eprintln!(
48 "[tacet-core] WARNING: CNTFRQ_EL0 returned suspicious value: {} Hz",
49 cntfrq
50 );
51 eprintln!("[tacet-core] Calibrating ARM64 counter frequency...");
52 return calibrate_aarch64_frequency();
53 }
54
55 let calibrated = calibrate_aarch64_frequency();
58 let ratio = calibrated as f64 / cntfrq as f64;
59
60 if !(0.9..=1.1).contains(&ratio) {
62 eprintln!(
63 "[tacet-core] WARNING: CNTFRQ_EL0 ({} Hz / {:.2} MHz) differs from calibrated frequency ({} Hz / {:.2} MHz) by {:.1}%",
64 cntfrq, cntfrq as f64 / 1e6,
65 calibrated, calibrated as f64 / 1e6,
66 (ratio - 1.0) * 100.0
67 );
68 eprintln!(
69 "[tacet-core] This typically indicates virtualization (VM/CI) with misconfigured timers."
70 );
71 eprintln!(
72 "[tacet-core] Using calibrated frequency: {} Hz ({:.2} MHz)",
73 calibrated, calibrated as f64 / 1e6
74 );
75 return calibrated;
76 }
77
78 cntfrq
80 })
81 }
82
83 #[cfg(not(feature = "std"))]
85 {
86 let freq: u64;
87 unsafe {
88 asm!("mrs {}, cntfrq_el0", out(reg) freq);
89 }
90
91 if is_reasonable_aarch64_freq(freq) {
92 freq
93 } else {
94 24_000_000 }
96 }
97}
98
99#[cfg(target_arch = "aarch64")]
101#[inline]
102fn is_reasonable_aarch64_freq(freq: u64) -> bool {
103 (1_000_000..=10_000_000_000).contains(&freq)
104}
105
106#[cfg(all(feature = "std", target_arch = "aarch64", target_os = "linux"))]
110fn detect_aarch64_platform_freq() -> Option<u64> {
111 let cpuinfo = std::fs::read_to_string("/proc/cpuinfo").ok()?;
112 let cpuinfo_lower = cpuinfo.to_lowercase();
113
114 if cpuinfo_lower.contains("neoverse") || cpuinfo_lower.contains("graviton") {
116 return Some(1_000_000_000); }
118
119 if cpuinfo_lower.contains("raspberry pi 4") || cpuinfo_lower.contains("bcm2711") {
121 return Some(54_000_000); }
123
124 None
125}
126
127#[cfg(all(feature = "std", target_arch = "aarch64"))]
131#[allow(dead_code)]
132fn calibrate_aarch64_frequency() -> u64 {
133 use std::time::{Duration, Instant};
134
135 const SAMPLES: usize = 5;
136 const SLEEP_MS: u64 = 20;
137
138 let mut frequencies = Vec::with_capacity(SAMPLES);
139
140 for _ in 0..SAMPLES {
141 let start_cnt: u64;
142 unsafe {
143 asm!("mrs {}, cntvct_el0", out(reg) start_cnt);
144 }
145 let start_instant = Instant::now();
146
147 std::thread::sleep(Duration::from_millis(SLEEP_MS));
148
149 let end_cnt: u64;
150 unsafe {
151 asm!("mrs {}, cntvct_el0", out(reg) end_cnt);
152 }
153 let elapsed_ns = start_instant.elapsed().as_nanos() as u64;
154
155 let cnt_delta = end_cnt.wrapping_sub(start_cnt);
156 let freq = ((cnt_delta as u128 * 1_000_000_000) / elapsed_ns as u128) as u64;
157
158 if is_reasonable_aarch64_freq(freq) {
159 frequencies.push(freq);
160 }
161 }
162
163 if frequencies.is_empty() {
164 eprintln!("[tacet-core] WARNING: ARM64 calibration failed. Using 24MHz estimate.");
165 return 24_000_000;
166 }
167
168 frequencies.sort_unstable();
170 let median = frequencies[frequencies.len() / 2];
171
172 eprintln!(
173 "[tacet-core] ARM64 counter frequency calibrated to {:.2} MHz",
174 median as f64 / 1_000_000.0
175 );
176
177 median
178}
179
180#[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
195pub fn get_x86_64_tsc_freq_hz() -> u64 {
196 #[cfg(feature = "std")]
198 {
199 use std::sync::OnceLock;
200 static VALIDATED_FREQ: OnceLock<u64> = OnceLock::new();
201
202 *VALIDATED_FREQ.get_or_init(|| {
203 #[cfg(target_os = "linux")]
205 if let Some(freq) = get_tsc_freq_linux() {
206 return freq;
207 }
208
209 #[cfg(target_os = "macos")]
210 if let Some(freq) = get_tsc_freq_macos() {
211 return freq;
212 }
213
214 calibrate_tsc_frequency()
216 })
217 }
218
219 #[cfg(not(feature = "std"))]
221 {
222 3_000_000_000
223 }
224}
225
226#[cfg(all(feature = "std", target_arch = "x86_64", target_os = "linux"))]
228fn get_tsc_freq_linux() -> Option<u64> {
229 if let Ok(content) = std::fs::read_to_string("/sys/devices/system/cpu/cpu0/tsc_freq_khz") {
231 if let Ok(khz) = content.trim().parse::<u64>() {
232 let freq = khz * 1000;
233 if is_reasonable_tsc_freq(freq) {
234 return Some(freq);
235 }
236 }
237 }
238
239 if has_invariant_tsc() {
241 if let Some(freq) = get_cpuid_base_freq() {
242 if is_reasonable_tsc_freq(freq) {
243 return Some(freq);
244 }
245 }
246 }
247
248 None
249}
250
251#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
253fn has_invariant_tsc() -> bool {
254 let result: u32;
256 unsafe {
257 asm!(
258 "push rbx",
259 "mov eax, 0x80000007",
260 "cpuid",
261 "pop rbx",
262 out("edx") result,
263 out("eax") _,
264 out("ecx") _,
265 options(nostack)
266 );
267 }
268 (result & (1 << 8)) != 0
269}
270
271#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
273fn get_cpuid_base_freq() -> Option<u64> {
274 let max_leaf: u32;
276 unsafe {
277 asm!(
278 "push rbx",
279 "mov eax, 0",
280 "cpuid",
281 "pop rbx",
282 out("eax") max_leaf,
283 out("ecx") _,
284 out("edx") _,
285 options(nostack)
286 );
287 }
288
289 if max_leaf < 0x16 {
290 return None;
291 }
292
293 let base_mhz: u32;
296 unsafe {
297 asm!(
298 "push rbx",
299 "mov eax, 0x16",
300 "cpuid",
301 "pop rbx",
302 out("eax") base_mhz,
303 out("ecx") _,
304 out("edx") _,
305 options(nostack)
306 );
307 }
308
309 if base_mhz == 0 {
310 return None;
311 }
312
313 Some(base_mhz as u64 * 1_000_000)
314}
315
316#[cfg(all(feature = "std", target_arch = "x86_64", target_os = "macos"))]
318fn get_tsc_freq_macos() -> Option<u64> {
319 if let Some(freq) = sysctl_read_u64("machdep.tsc.frequency") {
321 if is_reasonable_tsc_freq(freq) {
322 return Some(freq);
323 }
324 }
325
326 if let Some(freq) = sysctl_read_u64("hw.cpufrequency") {
328 if is_reasonable_tsc_freq(freq) {
329 return Some(freq);
330 }
331 }
332
333 None
334}
335
336#[cfg(all(feature = "std", target_arch = "x86_64", target_os = "macos"))]
338fn sysctl_read_u64(name: &str) -> Option<u64> {
339 use std::process::Command;
340
341 let output = Command::new("sysctl").arg("-n").arg(name).output().ok()?;
342
343 if !output.status.success() {
344 return None;
345 }
346
347 let stdout = String::from_utf8_lossy(&output.stdout);
348 stdout.trim().parse::<u64>().ok()
349}
350
351#[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
353#[inline]
354fn is_reasonable_tsc_freq(freq: u64) -> bool {
355 (500_000_000..=10_000_000_000).contains(&freq)
356}
357
358#[cfg(all(
360 feature = "std",
361 target_arch = "x86_64",
362 any(target_os = "linux", target_os = "macos")
363))]
364fn calibrate_tsc_frequency() -> u64 {
365 use std::time::{Duration, Instant};
366
367 eprintln!("[tacet-core] Calibrating TSC frequency (no sysfs/sysctl available)...");
368
369 const SAMPLES: usize = 5;
370 const SLEEP_MS: u64 = 20;
371
372 let mut frequencies = Vec::with_capacity(SAMPLES);
373
374 for _ in 0..SAMPLES {
375 let start_tsc = rdtsc();
376 let start_instant = Instant::now();
377
378 std::thread::sleep(Duration::from_millis(SLEEP_MS));
379
380 let end_tsc = rdtsc();
381 let elapsed_ns = start_instant.elapsed().as_nanos() as u64;
382
383 let tsc_delta = end_tsc.wrapping_sub(start_tsc);
384 let freq = ((tsc_delta as u128 * 1_000_000_000) / elapsed_ns as u128) as u64;
385
386 if is_reasonable_tsc_freq(freq) {
387 frequencies.push(freq);
388 }
389 }
390
391 if frequencies.is_empty() {
392 eprintln!("[tacet-core] WARNING: TSC calibration failed. Using 3GHz estimate.");
393 return 3_000_000_000;
394 }
395
396 frequencies.sort_unstable();
398 let median = frequencies[frequencies.len() / 2];
399
400 eprintln!(
401 "[tacet-core] TSC frequency calibrated to {:.2} GHz",
402 median as f64 / 1_000_000_000.0
403 );
404
405 median
406}
407
408#[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
410#[inline]
411fn rdtsc() -> u64 {
412 let lo: u32;
413 let hi: u32;
414 unsafe {
415 asm!(
416 "rdtsc",
417 out("eax") lo,
418 out("edx") hi,
419 options(nostack, nomem)
420 );
421 }
422 ((hi as u64) << 32) | (lo as u64)
423}
424
425pub fn counter_frequency_hz() -> u64 {
445 #[cfg(target_arch = "aarch64")]
446 {
447 get_aarch64_counter_freq_hz()
448 }
449
450 #[cfg(all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
451 {
452 get_x86_64_tsc_freq_hz()
453 }
454
455 #[cfg(not(any(
456 target_arch = "aarch64",
457 all(target_arch = "x86_64", any(target_os = "linux", target_os = "macos"))
458 )))]
459 {
460 0 }
462}
463
464pub fn timer_resolution_ns() -> f64 {
479 let freq = counter_frequency_hz();
480 if freq == 0 {
481 f64::INFINITY
482 } else {
483 1_000_000_000.0 / freq as f64
484 }
485}