hermit/syscalls/
timer.rs

1use crate::arch;
2use crate::errno::*;
3use crate::syscalls::usleep;
4use crate::time::{itimerval, timespec, timeval};
5
6#[allow(non_camel_case_types)]
7pub type clockid_t = i32;
8
9pub(crate) const CLOCK_REALTIME: clockid_t = 1;
10pub(crate) const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2;
11pub(crate) const CLOCK_THREAD_CPUTIME_ID: clockid_t = 3;
12pub(crate) const CLOCK_MONOTONIC: clockid_t = 4;
13pub(crate) const TIMER_ABSTIME: i32 = 4;
14
15/// Finds the resolution (or precision) of a clock.
16///
17/// This function gets the clock resolution of the clock with `clock_id` and stores it in parameter `res`.
18/// Returns `0` on success, `-EINVAL` otherwise.
19///
20/// Supported clocks:
21/// - `CLOCK_REALTIME`
22/// - `CLOCK_PROCESS_CPUTIME_ID`
23/// - `CLOCK_THREAD_CPUTIME_ID`
24/// - `CLOCK_MONOTONIC`
25#[hermit_macro::system]
26#[unsafe(no_mangle)]
27pub unsafe extern "C" fn sys_clock_getres(clock_id: clockid_t, res: *mut timespec) -> i32 {
28	assert!(
29		!res.is_null(),
30		"sys_clock_getres called with a zero res parameter"
31	);
32	let result = unsafe { &mut *res };
33
34	match clock_id {
35		CLOCK_REALTIME | CLOCK_PROCESS_CPUTIME_ID | CLOCK_THREAD_CPUTIME_ID | CLOCK_MONOTONIC => {
36			// All clocks in Hermit have 1 microsecond resolution.
37			*result = timespec::from_usec(1);
38			0
39		}
40		_ => {
41			debug!("Called sys_clock_getres for unsupported clock {clock_id}");
42			-EINVAL
43		}
44	}
45}
46
47/// Get the current time of a clock.
48///
49/// Get the current time of the clock with `clock_id` and stores result in parameter `res`.
50/// Returns `0` on success, `-EINVAL` otherwise.
51///
52/// Supported clocks:
53/// - `CLOCK_REALTIME`
54/// - `CLOCK_MONOTONIC`
55#[hermit_macro::system]
56#[unsafe(no_mangle)]
57pub unsafe extern "C" fn sys_clock_gettime(clock_id: clockid_t, tp: *mut timespec) -> i32 {
58	assert!(
59		!tp.is_null(),
60		"sys_clock_gettime called with a zero tp parameter"
61	);
62	let result = unsafe { &mut *tp };
63
64	match clock_id {
65		CLOCK_REALTIME => {
66			*result = timespec::from_usec(arch::kernel::systemtime::now_micros() as i64);
67			0
68		}
69		CLOCK_MONOTONIC => {
70			*result = timespec::from_usec(arch::processor::get_timer_ticks() as i64);
71			0
72		}
73		_ => {
74			debug!("Called sys_clock_gettime for unsupported clock {clock_id}");
75			-EINVAL
76		}
77	}
78}
79
80/// Sleep a clock for a specified number of nanoseconds.
81///
82/// The requested time (in nanoseconds) must be greater than 0 and less than 1,000,000.
83///
84/// Returns `0` on success, `-EINVAL` otherwise.
85///
86/// Supported clocks:
87/// - `CLOCK_REALTIME`
88/// - `CLOCK_MONOTONIC`
89#[hermit_macro::system]
90#[unsafe(no_mangle)]
91pub unsafe extern "C" fn sys_clock_nanosleep(
92	clock_id: clockid_t,
93	flags: i32,
94	rqtp: *const timespec,
95	_rmtp: *mut timespec,
96) -> i32 {
97	assert!(
98		!rqtp.is_null(),
99		"sys_clock_nanosleep called with a zero rqtp parameter"
100	);
101	let requested_time = unsafe { &*rqtp };
102	if requested_time.tv_sec < 0 || requested_time.tv_nsec > 999_999_999 {
103		debug!("sys_clock_nanosleep called with an invalid requested time, returning -EINVAL");
104		return -EINVAL;
105	}
106
107	match clock_id {
108		CLOCK_REALTIME | CLOCK_MONOTONIC => {
109			let mut microseconds = (requested_time.tv_sec as u64) * 1_000_000
110				+ (requested_time.tv_nsec as u64) / 1_000;
111
112			if flags & TIMER_ABSTIME > 0 {
113				if clock_id == CLOCK_REALTIME {
114					microseconds -= arch::kernel::systemtime::now_micros();
115				} else {
116					microseconds -= arch::processor::get_timer_ticks();
117				}
118			}
119
120			usleep(microseconds);
121			0
122		}
123		_ => -EINVAL,
124	}
125}
126
127#[hermit_macro::system]
128#[unsafe(no_mangle)]
129pub unsafe extern "C" fn sys_clock_settime(_clock_id: clockid_t, _tp: *const timespec) -> i32 {
130	// We don't support setting any clocks yet.
131	debug!("sys_clock_settime is unimplemented, returning -EINVAL");
132	-EINVAL
133}
134
135/// Get the system's clock time.
136///
137/// This function gets the current time based on the wallclock time when booted up, plus current timer ticks.
138/// Returns `0` on success, `-EINVAL` otherwise.
139///
140/// **Parameter `tz` should be set to `0` since tz is obsolete.**
141#[hermit_macro::system]
142#[unsafe(no_mangle)]
143pub unsafe extern "C" fn sys_gettimeofday(tp: *mut timeval, tz: usize) -> i32 {
144	if let Some(result) = unsafe { tp.as_mut() } {
145		// Return the current time based on the wallclock time when we were booted up
146		// plus the current timer ticks.
147		let microseconds = arch::kernel::systemtime::now_micros();
148		*result = timeval::from_usec(microseconds as i64);
149	}
150
151	if tz > 0 {
152		debug!("The tz parameter in sys_gettimeofday is unimplemented, returning -EINVAL");
153		return -EINVAL;
154	}
155
156	0
157}
158
159#[hermit_macro::system]
160#[unsafe(no_mangle)]
161pub unsafe extern "C" fn sys_setitimer(
162	_which: i32,
163	_value: *const itimerval,
164	_ovalue: *mut itimerval,
165) -> i32 {
166	debug!("Called sys_setitimer, which is unimplemented and always returns 0");
167	0
168}