Skip to main content

strat9_kernel/syscall/
time.rs

1//! Time-related syscalls: clock_gettime, nanosleep
2
3use core::sync::atomic::Ordering;
4
5use crate::{
6    memory::userslice::{UserSliceRead, UserSliceReadWrite},
7    process::{block_current_task, current_task_id, yield_task},
8    syscall::error::SyscallError,
9};
10
11pub use strat9_abi::data::TimeSpec;
12
13/// Clock IDs for clock_gettime (POSIX-compatible subset)
14pub const CLOCK_MONOTONIC: u32 = 1;
15pub const CLOCK_REALTIME: u32 = 0;
16
17/// Get current monotonic time in nanoseconds since boot.
18///
19/// Uses the scheduler tick counter (100Hz = 10ms per tick).
20#[inline]
21pub fn current_time_ns() -> u64 {
22    crate::process::scheduler::ticks() * 10_000_000 // 10ms = 10,000,000 ns
23}
24
25/// SYS_CLOCK_GETTIME: Get current time for the specified clock.
26///
27/// # Arguments
28/// * `clock_id` - Clock identifier (CLOCK_MONOTONIC or CLOCK_REALTIME)
29/// * `tp_ptr` - Pointer to userspace timespec structure to fill
30///
31/// # Returns
32/// * 0 on success
33/// * -EINVAL if clock_id is invalid
34/// * -EFAULT if tp_ptr is invalid
35///
36/// # POSIX compatibility
37/// This follows the POSIX signature: `int clock_gettime(clockid_t clock_id, struct timespec *tp)`
38pub fn sys_clock_gettime(clock_id: u32, tp_ptr: u64) -> Result<u64, SyscallError> {
39    if tp_ptr == 0 {
40        return Err(SyscallError::Fault);
41    }
42
43    // Currently we only support CLOCK_MONOTONIC and CLOCK_REALTIME (both return same time)
44    // In the future, CLOCK_REALTIME could be backed by an RTC
45    match clock_id {
46        CLOCK_MONOTONIC | CLOCK_REALTIME => {}
47        _ => return Err(SyscallError::InvalidArgument),
48    }
49
50    let now_ns = current_time_ns();
51    let ts = TimeSpec::from_nanos(now_ns);
52
53    let user = UserSliceReadWrite::new(tp_ptr, core::mem::size_of::<TimeSpec>())?;
54    user.write_val(&ts).map_err(|_| SyscallError::Fault)?;
55    Ok(0)
56}
57
58/// SYS_NANOSLEEP: Sleep for a specified duration.
59///
60/// # Arguments
61/// * `req_ptr` - Pointer to timespec structure with requested sleep duration
62/// * `rem_ptr` - Optional pointer to timespec for remaining time (if interrupted)
63///
64/// # Returns
65/// * 0 on success
66/// * -EINTR if interrupted by a signal (remaining time written to rem_ptr)
67/// * -EINVAL if the requested time is invalid
68pub fn sys_nanosleep(req_ptr: u64, rem_ptr: u64) -> Result<u64, SyscallError> {
69    // Read the requested timespec from userspace
70    let req_slice = UserSliceRead::new(req_ptr, core::mem::size_of::<TimeSpec>() as usize)
71        .map_err(|_| SyscallError::Fault)?; // EFAULT
72
73    let req = req_slice
74        .read_val::<TimeSpec>()
75        .map_err(|_| SyscallError::Fault)?;
76
77    // Validate the request
78    if req.tv_sec < 0 || req.tv_nsec < 0 || req.tv_nsec >= 1_000_000_000 {
79        return Err(SyscallError::InvalidArgument); // EINVAL
80    }
81
82    // Handle zero-duration sleep (just yield)
83    if req.tv_sec == 0 && req.tv_nsec == 0 {
84        yield_task();
85        return Ok(0);
86    }
87
88    let sleep_duration_ns = req.to_nanos();
89    let current_ns = current_time_ns();
90    let wake_deadline_ns = current_ns.saturating_add(sleep_duration_ns);
91
92    // Get current task ID
93    let task_id = current_task_id().ok_or_else(|| SyscallError::PermissionDenied)?; // EPERM
94
95    // Set the wake deadline on the task
96    if let Some(task) = crate::process::get_task_by_id(task_id) {
97        task.wake_deadline_ns
98            .store(wake_deadline_ns, Ordering::Relaxed);
99    }
100
101    // Block the current task - it will be woken by:
102    // 1. timer_tick() when the deadline expires (check_wake_deadlines)
103    // 2. A signal (in which case we return EINTR)
104
105    // Check for pending signals before blocking
106    if let Some(task) = crate::process::get_task_by_id(task_id) {
107        let pending = task.pending_signals.get_mask();
108        let blocked = task.blocked_signals.get_mask();
109        let unblocked_pending = pending & !blocked;
110        if unblocked_pending != 0 {
111            // Clear the deadline since we're not sleeping
112            task.wake_deadline_ns.store(0, Ordering::Relaxed);
113            return Err(SyscallError::Interrupted); // EINTR
114        }
115    }
116
117    loop {
118        block_current_task();
119
120        if let Some(task) = crate::process::get_task_by_id(task_id) {
121            let deadline = task.wake_deadline_ns.load(Ordering::Relaxed);
122            let now = current_time_ns();
123
124            if deadline == 0 || now >= deadline {
125                task.wake_deadline_ns.store(0, Ordering::Relaxed);
126                return Ok(0);
127            }
128
129            if crate::process::has_pending_signals() {
130                task.wake_deadline_ns.store(0, Ordering::Relaxed);
131                if rem_ptr != 0 {
132                    let remaining_ns = deadline - now;
133                    let remaining = TimeSpec::from_nanos(remaining_ns);
134                    let rem_slice =
135                        UserSliceReadWrite::new(rem_ptr, core::mem::size_of::<TimeSpec>() as usize)
136                            .map_err(|_| SyscallError::Fault)?;
137                    rem_slice
138                        .write_val(&remaining)
139                        .map_err(|_| SyscallError::Fault)?;
140                }
141                return Err(SyscallError::Interrupted);
142            }
143        } else {
144            return Err(SyscallError::Fault);
145        }
146    }
147}