hal_x86_64/time/
tsc.rs
1use crate::cpu::{intrinsics, FeatureNotSupported};
2use core::{
3 sync::atomic::{AtomicU32, Ordering::*},
4 time::Duration,
5};
6use maitake::time::Clock;
7use raw_cpuid::CpuId;
8
9#[derive(Copy, Clone, Debug)]
10pub struct Rdtsc(());
11
12impl Rdtsc {
13 pub fn is_supported() -> bool {
14 CpuId::new()
15 .get_feature_info()
16 .map(|features| features.has_tsc())
17 .unwrap_or(false)
18 }
19
20 pub fn new() -> Result<Self, FeatureNotSupported> {
21 if Self::is_supported() {
22 Ok(Self(()))
23 } else {
24 Err(FeatureNotSupported::new("rdtsc"))
25 }
26 }
27
28 #[inline(always)]
30 #[must_use]
31 pub fn read_timestamp(&self) -> u64 {
32 unsafe {
33 intrinsics::rdtsc()
36 }
37 }
38
39 pub fn into_maitake_clock(self) -> Result<Clock, &'static str> {
42 #![allow(unreachable_code)]
44 return Err("calibration routine doesn't really work yet, sorry!");
45
46 const NOT_YET_CALIBRATED: u32 = u32::MAX;
47
48 const PIT_SLEEP_DURATION: Duration = Duration::from_millis(50);
52
53 static MAITAKE_TICK_SHIFT: AtomicU32 = AtomicU32::new(NOT_YET_CALIBRATED);
54
55 fn now() -> u64 {
56 let rdtsc = unsafe { intrinsics::rdtsc() };
57 let shift = MAITAKE_TICK_SHIFT.load(Relaxed);
58 rdtsc >> shift
59 }
60
61 tracing::info!("calibrating RDTSC Maitake clock from PIT...");
62 let mut pit = super::PIT.lock();
63
64 let t0 = self.read_timestamp();
65 pit.sleep_blocking(PIT_SLEEP_DURATION)
66 .expect("PIT sleep failed for some reason");
67 let t1 = self.read_timestamp();
68
69 let elapsed_cycles = t1 - t0;
70 tracing::debug!(t0, t1, elapsed_cycles, "slept for {PIT_SLEEP_DURATION:?}");
71
72 let mut shift = 0;
73 loop {
74 assert!(
75 shift < 64,
76 "shifted away all the precision in the timestamp counter! \
77 this definitely should never happen..."
78 );
79 let elapsed_ticks = elapsed_cycles >> shift;
80 tracing::debug!(shift, elapsed_ticks, "trying a new tick shift");
81
82 let elapsed_ticks: u32 = elapsed_ticks.try_into().expect(
83 "there is no way that a 50ms sleep duration is more than \
84 u32::MAX rdtsc cycles...right?",
85 );
86
87 let tick_duration = PIT_SLEEP_DURATION / elapsed_ticks;
88 if tick_duration.as_nanos() > 0 {
89 tracing::info!(?tick_duration, shift, "calibrated RDTSC Maitake clock");
91 MAITAKE_TICK_SHIFT
92 .compare_exchange(NOT_YET_CALIBRATED, shift, AcqRel, Acquire)
93 .map_err(|_| "RDTSC Maitake clock has already been calibrated")?;
94 return Ok(Clock::new(tick_duration, now).named("CLOCK_RDTSC"));
95 } else {
96 shift += 1;
97 }
98 }
99 }
100}