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!(
75				"Called sys_clock_gettime for unsupported clock {}",
76				clock_id
77			);
78			-EINVAL
79		}
80	}
81}
82
83/// Sleep a clock for a specified number of nanoseconds.
84///
85/// The requested time (in nanoseconds) must be greater than 0 and less than 1,000,000.
86///
87/// Returns `0` on success, `-EINVAL` otherwise.
88///
89/// Supported clocks:
90/// - `CLOCK_REALTIME`
91/// - `CLOCK_MONOTONIC`
92#[hermit_macro::system]
93#[unsafe(no_mangle)]
94pub unsafe extern "C" fn sys_clock_nanosleep(
95	clock_id: clockid_t,
96	flags: i32,
97	rqtp: *const timespec,
98	_rmtp: *mut timespec,
99) -> i32 {
100	assert!(
101		!rqtp.is_null(),
102		"sys_clock_nanosleep called with a zero rqtp parameter"
103	);
104	let requested_time = unsafe { &*rqtp };
105	if requested_time.tv_sec < 0 || requested_time.tv_nsec > 999_999_999 {
106		debug!("sys_clock_nanosleep called with an invalid requested time, returning -EINVAL");
107		return -EINVAL;
108	}
109
110	match clock_id {
111		CLOCK_REALTIME | CLOCK_MONOTONIC => {
112			let mut microseconds = (requested_time.tv_sec as u64) * 1_000_000
113				+ (requested_time.tv_nsec as u64) / 1_000;
114
115			if flags & TIMER_ABSTIME > 0 {
116				if clock_id == CLOCK_REALTIME {
117					microseconds -= arch::kernel::systemtime::now_micros();
118				} else {
119					microseconds -= arch::processor::get_timer_ticks();
120				}
121			}
122
123			usleep(microseconds);
124			0
125		}
126		_ => -EINVAL,
127	}
128}
129
130#[hermit_macro::system]
131#[unsafe(no_mangle)]
132pub unsafe extern "C" fn sys_clock_settime(_clock_id: clockid_t, _tp: *const timespec) -> i32 {
133	// We don't support setting any clocks yet.
134	debug!("sys_clock_settime is unimplemented, returning -EINVAL");
135	-EINVAL
136}
137
138/// Get the system's clock time.
139///
140/// This function gets the current time based on the wallclock time when booted up, plus current timer ticks.
141/// Returns `0` on success, `-EINVAL` otherwise.
142///
143/// **Parameter `tz` should be set to `0` since tz is obsolete.**
144#[hermit_macro::system]
145#[unsafe(no_mangle)]
146pub unsafe extern "C" fn sys_gettimeofday(tp: *mut timeval, tz: usize) -> i32 {
147	if let Some(result) = unsafe { tp.as_mut() } {
148		// Return the current time based on the wallclock time when we were booted up
149		// plus the current timer ticks.
150		let microseconds = arch::kernel::systemtime::now_micros();
151		*result = timeval::from_usec(microseconds as i64);
152	}
153
154	if tz > 0 {
155		debug!("The tz parameter in sys_gettimeofday is unimplemented, returning -EINVAL");
156		return -EINVAL;
157	}
158
159	0
160}
161
162#[hermit_macro::system]
163#[unsafe(no_mangle)]
164pub unsafe extern "C" fn sys_setitimer(
165	_which: i32,
166	_value: *const itimerval,
167	_ovalue: *mut itimerval,
168) -> i32 {
169	debug!("Called sys_setitimer, which is unimplemented and always returns 0");
170	0
171}