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