The Pedigree Project 0.1
clock-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/Machine.h"
5#include "pedigree/kernel/machine/Timer.h"
6#include "pedigree/kernel/process/ConditionVariable.h"
7#include "pedigree/kernel/process/Mutex.h"
8#include "pedigree/kernel/process/Thread.h"
9#include "pedigree/kernel/processor/Processor.h"
10#include "pedigree/kernel/processor/ProcessorInformation.h"
11#include "pedigree/kernel/syscallError.h"
12#include "pedigree/kernel/time/Time.h"
13
14#include <limits.h>
15#include <time.h>
16
17#include "PosixSubsystem.h"
18#include "clock-syscalls.h"
19#include "linux-wait-abi.h"
20#include "mqueue-syscalls.h"
21#include "posix-timer-syscalls.h"
22#include "pthread-syscalls.h"
23#include "system-syscalls.h"
24#include "timerfd-syscalls.h"
25#include "user-namespace.h"
26
27#ifndef SG_NOTICE
28#define SG_NOTICE(x)
29#endif
30
31namespace {
32constexpr Time::Timestamp MaximumLinuxSleepNanoseconds = 0x7FFFFFFFFFFFFFFFULL;
33Mutex clockChangeLock;
34ConditionVariable clockChanged;
35uint64_t clockChangeGeneration = 0;
36
37bool supportedSleepClock(clockid_t clockId) {
38 return clockId == CLOCK_REALTIME || clockId == CLOCK_MONOTONIC;
39}
40
41bool supportedReadClock(clockid_t clockId) {
42 return supportedSleepClock(clockId) || clockId == CLOCK_MONOTONIC_RAW;
43}
44
45Time::Timestamp sleepClockNanoseconds(clockid_t clockId) {
46 return clockId == CLOCK_REALTIME ? Time::getTimeNanoseconds() : Time::getTicks();
47}
48
49bool validTimespec(int64_t seconds, int64_t nanoseconds) {
50 return seconds >= 0 && nanoseconds >= 0 && nanoseconds < 1000000000;
51}
52
53Time::Timestamp timespecToNanoseconds(int64_t secondsValue, int64_t nanosecondsValue) {
54 const Time::Timestamp seconds = static_cast<Time::Timestamp>(secondsValue);
55 const Time::Timestamp nanoseconds = static_cast<Time::Timestamp>(nanosecondsValue);
56 if (seconds > (MaximumLinuxSleepNanoseconds - nanoseconds) / Time::Multiplier::Second) {
57 return MaximumLinuxSleepNanoseconds;
58 }
59
60 const Time::Timestamp wholeSeconds = seconds * Time::Multiplier::Second;
61 return wholeSeconds + nanoseconds;
62}
63
64Time::Timestamp nanosleepAlarmDuration(Time::Timestamp requested) {
65 const Time::Timestamp remainder = requested % Time::Multiplier::Microsecond;
66 if (!remainder) {
67 return requested;
68 }
69
70 // The machine timer accepts whole microseconds. Round up so a partial
71 // microsecond request cannot expire before its requested duration.
72 return requested + (Time::Multiplier::Microsecond - remainder);
73}
74
75bool waitForRealtimeSleep(Time::Timestamp deadline) {
76 LockGuard<Mutex> guard(clockChangeLock);
77 for (;;) {
78 const Time::Timestamp now = Time::getTimeNanoseconds();
79 if (now >= deadline) {
80 return true;
81 }
82 Time::Timestamp duration = nanosleepAlarmDuration(deadline - now);
83 ConditionVariable::Error error = ConditionVariable::NoError;
84 if (clockChanged.wait(clockChangeLock, duration, error) ||
85 error == ConditionVariable::TimedOut) {
86 continue;
87 }
88 Thread* thread = Processor::information().getCurrentThread();
89 thread->clearInterruption();
90 return Time::getTimeNanoseconds() >= deadline;
91 }
92}
93
94bool waitForClockSleep(clockid_t clockId, bool absolute, Time::Timestamp requested,
95 Time::Timestamp& remaining) {
96 remaining = 0;
97 if (absolute && clockId == CLOCK_REALTIME) {
98 return waitForRealtimeSleep(requested);
99 }
100 const Time::Timestamp monotonicStart = Time::getTicks();
101
102 while (true) {
103 Time::Timestamp duration = requested;
104 if (absolute) {
105 const Time::Timestamp now = sleepClockNanoseconds(clockId);
106 if (now >= requested) {
107 return true;
108 }
109 duration = requested - now;
110 } else if (!duration) {
111 return true;
112 }
113
114 const bool completed = Time::delay(nanosleepAlarmDuration(duration));
115 Thread* thread = Processor::information().getCurrentThread();
116 if (completed) {
117 if (!absolute || sleepClockNanoseconds(clockId) >= requested) {
118 return true;
119 }
120
121 // A realtime deadline may move backwards while blocked. Re-arm for the
122 // same absolute deadline rather than reporting an early completion.
123 continue;
124 }
125
126 if (thread->getInterruptionReason() != Thread::InterruptedBySignal) {
127 return true;
128 }
129
130 if (absolute) {
131 const bool deadlineReached = sleepClockNanoseconds(clockId) >= requested;
132 thread->clearInterruption();
133 return deadlineReached;
134 }
135
136 const Time::Timestamp elapsed = Time::getTicks() - monotonicStart;
137 thread->clearInterruption();
138 if (elapsed >= requested) {
139 return true;
140 }
141
142 remaining = requested - elapsed;
143 return false;
144 }
145}
146} // namespace
147
148PosixClockSnapshot posix_clock_snapshot() {
149 LockGuard<Mutex> guard(clockChangeLock);
150 return {clockChangeGeneration, Time::getTimeNanoseconds(), Time::getTicks()};
151}
152
153uint64_t posix_clock_change_generation() {
154 LockGuard<Mutex> guard(clockChangeLock);
155 return clockChangeGeneration;
156}
157
158bool posix_clock_step(int64_t nanoseconds) {
159 uint64_t generation;
160 {
161 LockGuard<Mutex> guard(clockChangeLock);
162 const Time::Timestamp now = Time::getTimeNanoseconds();
163 const uint64_t magnitude = nanoseconds < 0 ? 0 - static_cast<uint64_t>(nanoseconds)
164 : static_cast<uint64_t>(nanoseconds);
165 if (now > MaximumLinuxSleepNanoseconds ||
166 (nanoseconds < 0 ? magnitude > now : magnitude > MaximumLinuxSleepNanoseconds - now)) {
167 return false;
168 }
169 const Time::Timestamp target = nanoseconds < 0 ? now - magnitude : now + magnitude;
170 if (!Time::setTimeNanoseconds(target)) {
171 return false;
172 }
173 generation = ++clockChangeGeneration;
174 clockChanged.broadcast();
175 }
176 posix_futex_clock_changed();
177 posix_mqueue_clock_changed();
178 posix_timerfd_clock_changed(generation);
179 posix_timer_clock_changed();
180 return true;
181}
182
183#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
184extern "C" EXPORTED_PUBLIC Time::Timestamp posixNanosleepAlarmDurationForTest(time_t seconds,
185 long nanoseconds) {
186 const struct timespec requested = {seconds, nanoseconds};
187 return nanosleepAlarmDuration(timespecToNanoseconds(requested.tv_sec, requested.tv_nsec));
188}
189#endif
190
191int posix_nanosleep(const struct timespec* rqtp, struct timespec* rmtp, bool time32) {
192 struct timespec requested = {};
193 bool copied;
194 if (time32) {
195 LinuxKernelTimespec32 source = {};
196 copied = PosixSubsystem::copyFromUser(&source, rqtp, sizeof(source));
197 requested.tv_sec = source.tv_sec;
198 requested.tv_nsec = source.tv_nsec;
199 } else {
200 copied = PosixSubsystem::copyFromUser(&requested, rqtp, sizeof(requested));
201 }
202 if (!copied) {
203 SG_NOTICE("nanosleep -> invalid address");
204 SYSCALL_ERROR(BadAddress);
205 return -1;
206 }
207 if (!validTimespec(requested.tv_sec, requested.tv_nsec)) {
208 SYSCALL_ERROR(InvalidArgument);
209 return -1;
210 }
211
212 SG_NOTICE("nanosleep(" << Dec << requested.tv_sec << ":" << requested.tv_nsec << Hex << ") - "
213 << Machine::instance().getTimer()->getTickCount() << ".");
214
215 const Time::Timestamp duration = timespecToNanoseconds(requested.tv_sec, requested.tv_nsec);
216 Time::Timestamp remaining = 0;
217 if (waitForClockSleep(CLOCK_MONOTONIC, false, duration, remaining)) {
218 return 0;
219 }
220
221 const struct timespec result = {static_cast<time_t>(remaining / Time::Multiplier::Second),
222 static_cast<long>(remaining % Time::Multiplier::Second)};
223 if (rmtp) {
224 if (time32) {
225 const LinuxKernelTimespec32 result32 = {static_cast<int32_t>(result.tv_sec),
226 static_cast<int32_t>(result.tv_nsec)};
227 copied = PosixSubsystem::copyToUser(rmtp, &result32, sizeof(result32));
228 } else {
229 copied = PosixSubsystem::copyToUser(rmtp, &result, sizeof(result));
230 }
231 if (!copied) {
232 SYSCALL_ERROR(BadAddress);
233 return -1;
234 }
235 }
236
237 SYSCALL_ERROR(Interrupted);
238 return -1;
239}
240
241int posix_clock_gettime(clockid_t clock_id, struct timespec* tp) {
242 SG_NOTICE("clock_gettime(" << Dec << clock_id << Hex << ")");
243 Time::Timestamp nanoseconds = 0;
244 switch (clock_id) {
245 case CLOCK_REALTIME:
246 nanoseconds = Time::getTimeNanoseconds();
247 break;
248 case CLOCK_MONOTONIC:
249 case CLOCK_MONOTONIC_RAW:
250 // getTicks has no oscillator discipline; RAW shares this unadjusted clock.
251 nanoseconds = Time::getTicks();
252 break;
253 default:
254 SYSCALL_ERROR(InvalidArgument);
255 return -1;
256 }
257
258 const struct timespec result = {static_cast<time_t>(nanoseconds / Time::Multiplier::Second),
259 static_cast<long>(nanoseconds % Time::Multiplier::Second)};
260 if (!PosixSubsystem::copyToUser(tp, &result, sizeof(result))) {
261 SYSCALL_ERROR(BadAddress);
262 return -1;
263 }
264
265 return 0;
266}
267
268int posix_clock_settime(clockid_t clockId, const LinuxKernelTimespec* value) {
269 if (clockId != CLOCK_REALTIME) {
270 SYSCALL_ERROR(InvalidArgument);
271 return -1;
272 }
273 LinuxKernelTimespec requested = {};
274 if (!PosixSubsystem::copyFromUser(&requested, value, sizeof(requested))) {
275 SYSCALL_ERROR(BadAddress);
276 return -1;
277 }
278 if (!validTimespec(requested.tv_sec, requested.tv_nsec) ||
279 static_cast<uint64_t>(requested.tv_sec) >
280 (MaximumLinuxSleepNanoseconds - requested.tv_nsec) / Time::Multiplier::Second) {
281 SYSCALL_ERROR(InvalidArgument);
282 return -1;
283 }
284 Process* process = Processor::information().getCurrentThread()->getParent();
285 if (!posix_global_capable(PosixCapabilities::SysTime)) {
286 SYSCALL_ERROR(NotEnoughPermissions);
287 return -1;
288 }
289 const Time::Timestamp target =
290 static_cast<uint64_t>(requested.tv_sec) * Time::Multiplier::Second + requested.tv_nsec;
291 uint64_t generation;
292 {
293 LockGuard<Mutex> guard(clockChangeLock);
294 if (!Time::setTimeNanoseconds(target)) {
295 SYSCALL_ERROR(InvalidArgument);
296 return -1;
297 }
298 generation = ++clockChangeGeneration;
299 clockChanged.broadcast();
300 }
301 posix_futex_clock_changed();
302 posix_mqueue_clock_changed();
303 posix_timerfd_clock_changed(generation);
304 posix_timer_clock_changed();
305 return 0;
306}
307
308int posix_clock_getres_native(clockid_t clock_id, struct timespec* resolution) {
309 if (!supportedReadClock(clock_id)) {
310 SYSCALL_ERROR(InvalidArgument);
311 return -1;
312 }
313
314 if (!resolution) {
315 return 0;
316 }
317
318 const struct timespec result = {0, 1};
319 if (!PosixSubsystem::copyToUser(resolution, &result, sizeof(result))) {
320 SYSCALL_ERROR(BadAddress);
321 return -1;
322 }
323
324 return 0;
325}
326
327int posix_clock_getres(clockid_t clock_id, LinuxKernelTimespec* resolution) {
328 if (!supportedReadClock(clock_id)) {
329 SYSCALL_ERROR(InvalidArgument);
330 return -1;
331 }
332
333 if (!resolution) {
334 return 0;
335 }
336
337 const LinuxKernelTimespec result = {0, 1};
338 if (!PosixSubsystem::copyToUser(resolution, &result, sizeof(result))) {
339 SYSCALL_ERROR(BadAddress);
340 return -1;
341 }
342
343 return 0;
344}
345
346int posix_clock_nanosleep(clockid_t clock_id, int flags, const LinuxKernelTimespec* request,
347 LinuxKernelTimespec* remainder) {
348 if (!supportedSleepClock(clock_id)) {
349 SYSCALL_ERROR(InvalidArgument);
350 return -1;
351 }
352
353 LinuxKernelTimespec requested = {};
354 if (!PosixSubsystem::copyFromUser(&requested, request, sizeof(requested))) {
355 SYSCALL_ERROR(BadAddress);
356 return -1;
357 }
358 if (!validTimespec(requested.tv_sec, requested.tv_nsec)) {
359 SYSCALL_ERROR(InvalidArgument);
360 return -1;
361 }
362
363 const bool absolute = flags & TIMER_ABSTIME;
364 const Time::Timestamp requestedNanoseconds =
365 timespecToNanoseconds(requested.tv_sec, requested.tv_nsec);
366 Time::Timestamp remainingNanoseconds = 0;
367 if (waitForClockSleep(clock_id, absolute, requestedNanoseconds, remainingNanoseconds)) {
368 return 0;
369 }
370
371 if (!absolute && remainder) {
372 const LinuxKernelTimespec result = {
373 static_cast<int64_t>(remainingNanoseconds / Time::Multiplier::Second),
374 static_cast<int64_t>(remainingNanoseconds % Time::Multiplier::Second)};
375 if (!PosixSubsystem::copyToUser(remainder, &result, sizeof(result))) {
376 SYSCALL_ERROR(BadAddress);
377 return -1;
378 }
379 }
380
381 SYSCALL_ERROR(Interrupted);
382 return -1;
383}
MUST_USE_RESULT bool wait(Mutex &mutex, Time::Timestamp &timeout, Error &error, WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
Definition Mutex.h:56
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124