moto_rt/
time.rs

1use core::{sync::atomic::Ordering, time::Duration};
2
3use crate::RtVdsoVtable;
4
5#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
6#[repr(C)]
7pub struct Instant {
8    ticks: u64, // Currently tsc. Subject to change.
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(C)]
13pub struct SystemTime {
14    nanos: u128, // Note that SystemTime uses nanos vs Instant which uses "ticks".
15}
16
17#[allow(unused)]
18pub const UNIX_EPOCH: SystemTime = SystemTime { nanos: 0u128 };
19const NANOS_IN_SEC: u64 = 1_000_000_000;
20
21impl Instant {
22    pub const fn nan() -> Self {
23        Instant { ticks: 0 }
24    }
25    pub fn is_nan(&self) -> bool {
26        self.ticks == 0
27    }
28
29    pub fn from_u64(val: u64) -> Self {
30        Instant { ticks: val }
31    }
32
33    pub fn as_u64(&self) -> u64 {
34        self.ticks
35    }
36
37    pub fn now() -> Self {
38        let vdso_time_instant_now: extern "C" fn() -> u64 = unsafe {
39            core::mem::transmute(
40                RtVdsoVtable::get().time_instant_now.load(Ordering::Relaxed) as usize as *const ()
41            )
42        };
43
44        Self {
45            ticks: vdso_time_instant_now(),
46        }
47    }
48
49    pub fn duration_since(&self, earlier: Instant) -> Duration {
50        if earlier.ticks > self.ticks {
51            return Duration::ZERO;
52        }
53
54        let ticks_diff = self.ticks - earlier.ticks;
55        if ticks_diff == 0 {
56            return Duration::ZERO;
57        }
58
59        let nanos = ticks_to_nanos(ticks_diff);
60
61        Duration::new(
62            (nanos / (NANOS_IN_SEC as u128)) as u64,
63            (nanos % (NANOS_IN_SEC as u128)) as u32,
64        )
65    }
66
67    pub fn elapsed(&self) -> Duration {
68        Instant::now().duration_since(*self)
69    }
70
71    pub const fn infinite_future() -> Self {
72        Instant { ticks: u64::MAX }
73    }
74
75    pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
76        if *self < *other {
77            return None;
78        }
79
80        let result_ticks = self.ticks - other.ticks;
81        let result_nanos = ticks_to_nanos(result_ticks);
82        if result_nanos > (u64::MAX as u128) {
83            None
84        } else {
85            Some(Duration::from_nanos(result_nanos as u64))
86        }
87    }
88
89    pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
90        let tsc_secs = other.as_secs().checked_mul(ticks_in_sec())?;
91        let tsc_diff = nanos_to_ticks(other.subsec_nanos() as u64).checked_add(tsc_secs)?;
92
93        Some(Instant {
94            ticks: self.ticks.checked_add(tsc_diff)?,
95        })
96    }
97
98    pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
99        let tsc_secs = other.as_secs().checked_mul(ticks_in_sec())?;
100        let tsc_diff = nanos_to_ticks(other.subsec_nanos() as u64).checked_add(tsc_secs)?;
101
102        if tsc_diff > self.ticks {
103            None
104        } else {
105            Some(Instant {
106                ticks: self.ticks - tsc_diff,
107            })
108        }
109    }
110}
111
112impl core::ops::Add<Duration> for Instant {
113    type Output = Instant;
114
115    fn add(self, other: Duration) -> Instant {
116        let tsc_secs = other.as_secs() * ticks_in_sec();
117        let tsc_diff = nanos_to_ticks(other.subsec_nanos() as u64) + tsc_secs;
118
119        Instant {
120            ticks: self.ticks + tsc_diff,
121        }
122    }
123}
124
125impl core::ops::Sub<Duration> for Instant {
126    type Output = Instant;
127
128    fn sub(self, other: Duration) -> Self::Output {
129        let tsc_secs = other.as_secs() * ticks_in_sec();
130        let tsc_diff = nanos_to_ticks(other.subsec_nanos() as u64) + tsc_secs;
131
132        Instant {
133            ticks: self.ticks - tsc_diff,
134        }
135    }
136}
137
138pub fn since_system_start() -> Duration {
139    Instant::now()
140        .checked_sub_instant(&Instant { ticks: 0 })
141        .unwrap()
142}
143
144impl SystemTime {
145    pub const MIN: Self = Self { nanos: u128::MIN };
146    pub const MAX: Self = Self { nanos: u128::MAX };
147
148    pub fn now() -> Self {
149        SystemTime {
150            nanos: abs_ticks_to_nanos(Instant::now().ticks),
151        }
152    }
153
154    pub const fn as_u128(&self) -> u128 {
155        self.nanos
156    }
157
158    pub const fn from_u128(val: u128) -> Self {
159        Self { nanos: val }
160    }
161
162    pub const fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
163        if self.nanos >= other.nanos {
164            let total_nanos = self.nanos - other.nanos;
165            let secs = total_nanos / (NANOS_IN_SEC as u128);
166            let nanos = total_nanos % (NANOS_IN_SEC as u128);
167            Ok(Duration::new(secs as u64, nanos as u32))
168        } else {
169            let total_nanos = other.nanos - self.nanos;
170            let secs = total_nanos / (NANOS_IN_SEC as u128);
171            let nanos = total_nanos % (NANOS_IN_SEC as u128);
172            Err(Duration::new(secs as u64, nanos as u32))
173        }
174    }
175
176    pub const fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
177        match self.nanos.checked_add(other.as_nanos()) {
178            Some(nanos) => Some(Self { nanos }),
179            None => None,
180        }
181    }
182
183    pub const fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
184        match self.nanos.checked_sub(other.as_nanos()) {
185            Some(nanos) => Some(Self { nanos }),
186            None => None,
187        }
188    }
189}
190
191// This is "Duration".
192fn ticks_to_nanos(ticks: u64) -> u128 {
193    let vdso_ticks_to_nanos: extern "C" fn(u64, *mut u64, *mut u64) = unsafe {
194        core::mem::transmute(
195            RtVdsoVtable::get()
196                .time_ticks_to_nanos
197                .load(Ordering::Relaxed) as usize as *const (),
198        )
199    };
200
201    let mut hi = 0_u64;
202    let mut lo = 0_u64;
203
204    vdso_ticks_to_nanos(ticks, &mut hi, &mut lo);
205
206    ((hi as u128) << 64) + (lo as u128)
207}
208
209// This is "system time".
210fn abs_ticks_to_nanos(ticks: u64) -> u128 {
211    let vdso_abs_ticks_to_nanos: extern "C" fn(u64, *mut u64, *mut u64) = unsafe {
212        core::mem::transmute(
213            RtVdsoVtable::get()
214                .time_abs_ticks_to_nanos
215                .load(Ordering::Relaxed) as usize as *const (),
216        )
217    };
218
219    let mut hi = 0_u64;
220    let mut lo = 0_u64;
221
222    vdso_abs_ticks_to_nanos(ticks, &mut hi, &mut lo);
223
224    ((hi as u128) << 64) + (lo as u128)
225}
226
227fn nanos_to_ticks(nanos: u64) -> u64 {
228    let vdso_nanos_to_ticks: extern "C" fn(u64) -> u64 = unsafe {
229        core::mem::transmute(
230            RtVdsoVtable::get()
231                .time_nanos_to_ticks
232                .load(Ordering::Relaxed) as usize as *const (),
233        )
234    };
235
236    vdso_nanos_to_ticks(nanos)
237}
238
239fn ticks_in_sec() -> u64 {
240    RtVdsoVtable::get()
241        .time_ticks_in_sec
242        .load(Ordering::Relaxed)
243}
244
245#[derive(Debug)]
246pub struct UtcDateTime {
247    pub year: u32,
248    pub month: u8, // starts with 1
249    pub day: u8,   // starts with 1
250    pub hour: u8,
251    pub minute: u8,
252    pub second: u8,
253    pub nanosecond: u32,
254}
255
256impl core::fmt::Display for UtcDateTime {
257    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
258        write!(
259            f,
260            "{}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}Z",
261            self.year,
262            self.month,
263            self.day,
264            self.hour,
265            self.minute,
266            self.second,
267            self.nanosecond / (1000 * 1000)
268        )
269    }
270}
271
272impl UtcDateTime {
273    pub fn now() -> Self {
274        // Safe because it is now.
275        unsafe { Self::from_instant(Instant::now()) }
276    }
277
278    /// Converts a value of Instant into UtcDateTime.
279    ///
280    /// # Safety
281    ///
282    /// If the host adjust its date/time while the guest is running,
283    /// the result may be way off, so should only be used in non-critical
284    /// situations such as tests.
285    pub unsafe fn from_instant(ts: Instant) -> Self {
286        Self::from_unix_nanos(abs_ticks_to_nanos(ts.as_u64()))
287    }
288
289    pub fn from_unix_nanos(nanos: u128) -> Self {
290        let st = nanos as u64;
291        let nanosecond = (st % (1000 * 1000 * 1000)) as u32;
292
293        let seconds = (st - (nanosecond as u64)) / (1000 * 1000 * 1000);
294        let time = seconds % (24 * 60 * 60);
295
296        let second = (time % 60) as u8;
297        let minutes = (time - (second as u64)) / 60;
298        let minute = (minutes % 60) as u8;
299        let hour = ((minutes - (minute as u64)) / 60) as u8;
300
301        let mut days = (seconds - time) / (24 * 60 * 60);
302        let mut year: u32 = 1970;
303
304        fn leap_year(year: u32) -> bool {
305            year.is_multiple_of(400) || (year.is_multiple_of(4) && !year.is_multiple_of(100))
306        }
307
308        // Find the year.
309        loop {
310            if leap_year(year) {
311                if days < 366 {
312                    break;
313                }
314                days -= 366; // leap year
315            } else if days < 365 {
316                break;
317            } else {
318                days -= 365; // normal year
319            }
320            year += 1;
321        }
322
323        // Find the month and day.
324        const MONTHS: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
325        let mut month: u8 = 0;
326        loop {
327            if month == 1 {
328                if leap_year(year) {
329                    if days < 29 {
330                        break;
331                    }
332                    days -= 29;
333                    month += 1;
334                    continue;
335                } else if days < 28 {
336                    break;
337                }
338                days -= 28;
339                month += 1;
340                continue;
341            }
342            if days < MONTHS[month as usize] as u64 {
343                break;
344            }
345            days -= MONTHS[month as usize] as u64;
346            month += 1;
347        }
348
349        Self {
350            year,
351            month: month + 1,
352            day: (days + 1) as u8,
353            hour,
354            minute,
355            second,
356            nanosecond,
357        }
358    }
359}