The Pedigree Project 0.1
clock-adjust-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "clock-adjust-syscalls.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/process/Mutex.h"
5#include "pedigree/kernel/process/Process.h"
6#include "pedigree/kernel/process/Thread.h"
7#include "pedigree/kernel/processor/Processor.h"
8#include "pedigree/kernel/processor/ProcessorInformation.h"
9#include "pedigree/kernel/syscallError.h"
10
11#include <stddef.h>
12#include <time.h>
13
14#include "PosixSubsystem.h"
15#include "clock-syscalls.h"
16#include "user-namespace.h"
17
18namespace {
19struct LinuxTimex {
20 uint32_t modes, modesPadding;
21 int64_t offset, frequency, maximumError, estimatedError;
22 int32_t status, statusPadding;
23 int64_t constant, precision, tolerance;
24 int64_t seconds, fraction;
25 int64_t tick, ppsFrequency, jitter;
26 int32_t shift, shiftPadding;
27 int64_t stability, jitterCount, calibrationCount, errorCount, stabilityCount;
28 int32_t tai, padding[11];
29};
30static_assert(sizeof(LinuxTimex) == 208, "Linux amd64 timex layout");
31static_assert(offsetof(LinuxTimex, offset) == 8, "Linux timex offset field");
32static_assert(offsetof(LinuxTimex, status) == 40, "Linux timex status field");
33static_assert(offsetof(LinuxTimex, seconds) == 72, "Linux timex timeval field");
34static_assert(offsetof(LinuxTimex, tai) == 160, "Linux timex TAI field");
35
36constexpr uint32_t AdjustSetOffset = 0x0100;
37constexpr uint32_t AdjustMicro = 0x1000;
38constexpr uint32_t AdjustNano = 0x2000;
39constexpr uint32_t AdjustSingleShot = 0x8001;
40constexpr uint32_t AdjustSingleShotRead = 0xA001;
41constexpr uint32_t KnownModes = 0xF1BF;
42constexpr uint32_t SupportedModes = AdjustSetOffset | AdjustMicro | AdjustNano;
43constexpr int32_t StatusUnsynchronized = 0x0040;
44constexpr int32_t StatusNano = 0x2000;
45constexpr int TimeError = 5;
46constexpr int64_t MaximumAdjustment = 0x7FFFFFFFFFFFFFFFLL;
47
48Mutex adjustmentLock;
49bool nanosecondUnits = false;
50
51bool decodeAdjustment(const LinuxTimex& value, int64_t& nanoseconds) {
52 const int64_t units = value.modes & AdjustNano ? 1000000000 : 1000000;
53 if (value.fraction < 0 || value.fraction >= units) {
54 return false;
55 }
56 const int64_t fraction = value.fraction * (1000000000 / units);
57 if (value.seconds >= 0) {
58 if (value.seconds > (MaximumAdjustment - fraction) / 1000000000) {
59 return false;
60 }
61 nanoseconds = value.seconds * 1000000000 + fraction;
62 return true;
63 }
64 // Normalize a negative seconds field before multiplying: {-1, 500000}
65 // denotes minus half a second, and the input may contain INT64_MIN.
66 const uint64_t seconds = 0 - static_cast<uint64_t>(value.seconds + 1);
67 const uint64_t tail = 1000000000 - fraction;
68 if (seconds > (static_cast<uint64_t>(MaximumAdjustment) - tail) / 1000000000) {
69 return false;
70 }
71 nanoseconds = -static_cast<int64_t>(seconds * 1000000000 + tail);
72 return true;
73}
74} // namespace
75
76int posix_clock_adjtime(int clockId, void* value) {
77 LinuxTimex requested = {};
78 if (!PosixSubsystem::copyFromUser(&requested, value, sizeof(requested))) {
79 SYSCALL_ERROR(BadAddress);
80 return -1;
81 }
82 if (clockId != CLOCK_REALTIME) {
83 if (clockId == CLOCK_MONOTONIC) {
84 SYSCALL_ERROR(OperationNotSupported);
85 } else {
86 SYSCALL_ERROR(InvalidArgument);
87 }
88 return -1;
89 }
90 const bool readOnly = requested.modes == 0 || requested.modes == AdjustSingleShotRead;
91 if (!readOnly && !posix_global_capable(PosixCapabilities::SysTime)) {
92 SYSCALL_ERROR(NotEnoughPermissions);
93 return -1;
94 }
95 if ((requested.modes & ~KnownModes) ||
96 ((requested.modes & 0x8000) && requested.modes != AdjustSingleShot &&
97 requested.modes != AdjustSingleShotRead)) {
98 SYSCALL_ERROR(InvalidArgument);
99 return -1;
100 }
101 if (!readOnly && (requested.modes & ~SupportedModes)) {
102 SYSCALL_ERROR(OperationNotSupported);
103 return -1;
104 }
105 int64_t adjustment = 0;
106 if ((requested.modes & AdjustSetOffset) && !decodeAdjustment(requested, adjustment)) {
107 SYSCALL_ERROR(InvalidArgument);
108 return -1;
109 }
110
111 LinuxTimex result = {};
112 {
113 LockGuard<Mutex> guard(adjustmentLock);
114 if ((requested.modes & AdjustSetOffset) && !posix_clock_step(adjustment)) {
115 SYSCALL_ERROR(InvalidArgument);
116 return -1;
117 }
118 if (!readOnly) {
119 if (requested.modes & AdjustNano)
120 nanosecondUnits = true;
121 if (requested.modes & AdjustMicro)
122 nanosecondUnits = false;
123 }
124 const Time::Timestamp now = Time::getTimeNanoseconds();
125 result.modes = requested.modes;
126 result.status = StatusUnsynchronized | (nanosecondUnits ? StatusNano : 0);
127 // There is no oscillator discipline or measured error bound. Do not report
128 // synchronization, a fictitious tuning range, or zero clock uncertainty.
129 result.maximumError = result.estimatedError = MaximumAdjustment;
130 result.precision = 1;
131 result.seconds = now / Time::Multiplier::Second;
132 result.fraction =
133 (now % Time::Multiplier::Second) / (nanosecondUnits ? 1 : Time::Multiplier::Microsecond);
134 }
135 if (!PosixSubsystem::copyToUser(value, &result, sizeof(result))) {
136 SYSCALL_ERROR(BadAddress);
137 return -1;
138 }
139 return TimeError;
140}
141
142int posix_adjtimex(void* value) {
143 return posix_clock_adjtime(CLOCK_REALTIME, value);
144}
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)