Skip to main content

strat9_kernel/syscall/
time.rs

1//! Time-related syscalls: clock_gettime, nanosleep, clock_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/// Flag for clock_nanosleep: request is an absolute time based on the clock.
18const TIMER_ABSTIME: i32 = 1;
19
20/// Get current monotonic time in nanoseconds since boot.
21///
22/// Uses the scheduler tick counter (100Hz = 10ms per tick).
23#[inline]
24pub fn current_time_ns() -> u64 {
25    crate::process::scheduler::ticks() * 10_000_000 // 10ms = 10,000,000 ns
26}
27
28/// SYS_CLOCK_GETTIME: Get current time for the specified clock.
29///
30/// # Arguments
31/// * `clock_id` - Clock identifier (CLOCK_MONOTONIC or CLOCK_REALTIME)
32/// * `tp_ptr` - Pointer to userspace timespec structure to fill
33///
34/// # Returns
35/// * 0 on success
36/// * -EINVAL if clock_id is invalid
37/// * -EFAULT if tp_ptr is invalid
38///
39/// # POSIX compatibility
40/// This follows the POSIX signature: `int clock_gettime(clockid_t clock_id, struct timespec *tp)`
41pub fn sys_clock_gettime(clock_id: u32, tp_ptr: u64) -> Result<u64, SyscallError> {
42    if tp_ptr == 0 {
43        return Err(SyscallError::Fault);
44    }
45
46    // Currently we only support CLOCK_MONOTONIC and CLOCK_REALTIME (both return same time)
47    // In the future, CLOCK_REALTIME could be backed by an RTC
48    match clock_id {
49        CLOCK_MONOTONIC | CLOCK_REALTIME => {}
50        _ => return Err(SyscallError::InvalidArgument),
51    }
52
53    let now_ns = current_time_ns();
54    let ts = TimeSpec::from_nanos(now_ns);
55
56    let user = UserSliceReadWrite::new(tp_ptr, core::mem::size_of::<TimeSpec>())?;
57    user.write_val(&ts).map_err(|_| SyscallError::Fault)?;
58    Ok(0)
59}
60
61/// SYS_NANOSLEEP: Sleep for a specified duration.
62///
63/// # Arguments
64/// * `req_ptr` - Pointer to timespec structure with requested sleep duration
65/// * `rem_ptr` - Optional pointer to timespec for remaining time (if interrupted)
66///
67/// # Returns
68/// * 0 on success
69/// * -EINTR if interrupted by a signal (remaining time written to rem_ptr)
70/// * -EINVAL if the requested time is invalid
71pub fn sys_nanosleep(req_ptr: u64, rem_ptr: u64) -> Result<u64, SyscallError> {
72    // Read the requested timespec from userspace
73    let req_slice = UserSliceRead::new(req_ptr, core::mem::size_of::<TimeSpec>() as usize)
74        .map_err(|_| SyscallError::Fault)?; // EFAULT
75
76    let req = req_slice
77        .read_val::<TimeSpec>()
78        .map_err(|_| SyscallError::Fault)?;
79
80    // Validate the request
81    if req.tv_sec < 0 || req.tv_nsec < 0 || req.tv_nsec >= 1_000_000_000 {
82        return Err(SyscallError::InvalidArgument); // EINVAL
83    }
84
85    // Handle zero-duration sleep (just yield)
86    if req.tv_sec == 0 && req.tv_nsec == 0 {
87        yield_task();
88        return Ok(0);
89    }
90
91    let sleep_duration_ns = req.to_nanos();
92    let current_ns = current_time_ns();
93    let wake_deadline_ns = current_ns.saturating_add(sleep_duration_ns);
94
95    // Get current task ID
96    let task_id = current_task_id().ok_or_else(|| SyscallError::PermissionDenied)?; // EPERM
97
98    // Set the wake deadline on the task
99    if let Some(task) = crate::process::get_task_by_id(task_id) {
100        task.wake_deadline_ns
101            .store(wake_deadline_ns, Ordering::Relaxed);
102    }
103
104    // Block the current task - it will be woken by:
105    // 1. timer_tick() when the deadline expires (check_wake_deadlines)
106    // 2. A signal (in which case we return EINTR)
107
108    // Check for pending signals before blocking
109    if let Some(task) = crate::process::get_task_by_id(task_id) {
110        let pending = task.pending_signals.get_mask();
111        let blocked = task.blocked_signals.get_mask();
112        let unblocked_pending = pending & !blocked;
113        if unblocked_pending != 0 {
114            // Clear the deadline since we're not sleeping
115            task.wake_deadline_ns.store(0, Ordering::Relaxed);
116            return Err(SyscallError::Interrupted); // EINTR
117        }
118    }
119
120    loop {
121        block_current_task();
122
123        if let Some(task) = crate::process::get_task_by_id(task_id) {
124            let deadline = task.wake_deadline_ns.load(Ordering::Relaxed);
125            let now = current_time_ns();
126
127            if deadline == 0 || now >= deadline {
128                task.wake_deadline_ns.store(0, Ordering::Relaxed);
129                return Ok(0);
130            }
131
132            if crate::process::has_pending_signals() {
133                task.wake_deadline_ns.store(0, Ordering::Relaxed);
134                if rem_ptr != 0 {
135                    let remaining_ns = deadline - now;
136                    let remaining = TimeSpec::from_nanos(remaining_ns);
137                    let rem_slice =
138                        UserSliceReadWrite::new(rem_ptr, core::mem::size_of::<TimeSpec>() as usize)
139                            .map_err(|_| SyscallError::Fault)?;
140                    rem_slice
141                        .write_val(&remaining)
142                        .map_err(|_| SyscallError::Fault)?;
143                }
144                return Err(SyscallError::Interrupted);
145            }
146        } else {
147            return Err(SyscallError::Fault);
148        }
149    }
150}
151
152/// SYS_CLOCK_NANOSLEEP: Sleep with clock selection and absolute/relative mode.
153///
154/// # Arguments
155/// * `clock_id` - Clock identifier (CLOCK_MONOTONIC or CLOCK_REALTIME)
156/// * `flags` - 0 for relative sleep, TIMER_ABSTIME (1) for absolute wake-up time
157/// * `req_ptr` - Pointer to timespec (absolute time if TIMER_ABSTIME, else duration)
158/// * `rem_ptr` - Optional pointer to timespec for remaining time (relative mode only)
159///
160/// # Returns
161/// * 0 on success
162/// * -EINTR if interrupted by a signal
163/// * -EINVAL if clock_id or timespec is invalid
164/// * -EFAULT if req_ptr is invalid
165///
166/// # POSIX compatibility
167/// POSIX signature: `int clock_nanosleep(clockid_t clock_id, int flags,
168///                                       const struct timespec *request,
169///                                       struct timespec *remain)`
170pub fn sys_clock_nanosleep(
171    clock_id: u32,
172    flags: i32,
173    req_ptr: u64,
174    rem_ptr: u64,
175) -> Result<u64, SyscallError> {
176    // Validate clock_id
177    match clock_id {
178        CLOCK_MONOTONIC | CLOCK_REALTIME => {}
179        _ => return Err(SyscallError::InvalidArgument),
180    }
181
182    // Read the request timespec from userspace
183    let req_slice = UserSliceRead::new(req_ptr, core::mem::size_of::<TimeSpec>() as usize)
184        .map_err(|_| SyscallError::Fault)?;
185
186    let req = req_slice
187        .read_val::<TimeSpec>()
188        .map_err(|_| SyscallError::Fault)?;
189
190    // Validate the timespec fields
191    if req.tv_sec < 0 || req.tv_nsec < 0 || req.tv_nsec >= 1_000_000_000 {
192        return Err(SyscallError::InvalidArgument);
193    }
194
195    let current_ns = current_time_ns();
196
197    // Compute the wake deadline
198    let wake_deadline_ns = if (flags & TIMER_ABSTIME) != 0 {
199        // Absolute mode: request value IS the deadline
200        let deadline = req.to_nanos();
201        // If the deadline is in the past, return immediately (success per POSIX)
202        if deadline <= current_ns {
203            return Ok(0);
204        }
205        deadline
206    } else {
207        // Relative mode: deadline = now + request duration
208        let sleep_duration_ns = req.to_nanos();
209
210        // Handle zero-duration sleep (just yield)
211        if sleep_duration_ns == 0 {
212            yield_task();
213            return Ok(0);
214        }
215
216        current_ns.saturating_add(sleep_duration_ns)
217    };
218
219    // Get current task ID
220    let task_id = current_task_id().ok_or_else(|| SyscallError::PermissionDenied)?;
221
222    // Set the wake deadline on the task
223    if let Some(task) = crate::process::get_task_by_id(task_id) {
224        task.wake_deadline_ns
225            .store(wake_deadline_ns, Ordering::Relaxed);
226    }
227
228    // Check for pending signals before blocking
229    if let Some(task) = crate::process::get_task_by_id(task_id) {
230        let pending = task.pending_signals.get_mask();
231        let blocked = task.blocked_signals.get_mask();
232        let unblocked_pending = pending & !blocked;
233        if unblocked_pending != 0 {
234            task.wake_deadline_ns.store(0, Ordering::Relaxed);
235            return Err(SyscallError::Interrupted);
236        }
237    }
238
239    loop {
240        block_current_task();
241
242        if let Some(task) = crate::process::get_task_by_id(task_id) {
243            let deadline = task.wake_deadline_ns.load(Ordering::Relaxed);
244            let now = current_time_ns();
245
246            if deadline == 0 || now >= deadline {
247                task.wake_deadline_ns.store(0, Ordering::Relaxed);
248                return Ok(0);
249            }
250
251            if crate::process::has_pending_signals() {
252                task.wake_deadline_ns.store(0, Ordering::Relaxed);
253
254                // Write remaining time only in relative mode
255                if (flags & TIMER_ABSTIME) == 0 && rem_ptr != 0 {
256                    let remaining_ns = deadline - now;
257                    let remaining = TimeSpec::from_nanos(remaining_ns);
258                    let rem_slice =
259                        UserSliceReadWrite::new(rem_ptr, core::mem::size_of::<TimeSpec>() as usize)
260                            .map_err(|_| SyscallError::Fault)?;
261                    rem_slice
262                        .write_val(&remaining)
263                        .map_err(|_| SyscallError::Fault)?;
264                }
265                return Err(SyscallError::Interrupted);
266            }
267        } else {
268            return Err(SyscallError::Fault);
269        }
270    }
271}