8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/errors.h"
11#include "pedigree/kernel/process/Process.h"
12#include "pedigree/kernel/process/Scheduler.h"
13#include "pedigree/kernel/process/Thread.h"
14#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
15#include "pedigree/kernel/processor/Processor.h"
16#include "pedigree/kernel/processor/VirtualAddressSpace.h"
17#include "pedigree/kernel/time/Time.h"
22#include "modules/subsys/posix/PosixProcess.h"
23#include "modules/subsys/posix/PosixSubsystem.h"
24#include "modules/subsys/posix/system-syscalls.h"
28static_assert(
sizeof(time_t) == 8);
29static_assert(
sizeof(suseconds_t) == 8);
30static_assert(
static_cast<time_t
>(-1) < 0);
31static_assert(
static_cast<suseconds_t
>(-1) < 0);
32static_assert(
sizeof(
struct timeval) == 16);
33static_assert(offsetof(
struct timeval, tv_sec) == 0);
34static_assert(offsetof(
struct timeval, tv_usec) == 8);
35static_assert(
sizeof(
struct timezone) == 8);
36static_assert(offsetof(
struct timezone, tz_minuteswest) == 0);
37static_assert(offsetof(
struct timezone, tz_dsttime) == 4);
38static_assert(
sizeof(
struct itimerval) == 32);
39static_assert(offsetof(
struct itimerval, it_interval) == 0);
40static_assert(offsetof(
struct itimerval, it_value) == 16);
43constexpr int PreservedErrno = 123;
44constexpr Time::Timestamp MaximumLinuxTimerNanoseconds = 0x7FFFFFFFFFFFFFFFULL;
46struct TimeSyscallContext {
47 TimeSyscallContext() : passed(false), returned(0) {}
53struct AlarmThreadContext {
54 explicit AlarmThreadContext(uint32_t seconds)
55 : seconds(seconds), result(~static_cast<size_t>(0)), returned(0) {}
62struct itimerval timerValue(time_t intervalSeconds, suseconds_t intervalMicroseconds,
63 time_t valueSeconds = 0, suseconds_t valueMicroseconds = 0) {
64 struct itimerval result = {};
65 result.it_interval.tv_sec = intervalSeconds;
66 result.it_interval.tv_usec = intervalMicroseconds;
67 result.it_value.tv_sec = valueSeconds;
68 result.it_value.tv_usec = valueMicroseconds;
72bool sameTimer(
const struct itimerval& left,
const struct itimerval& right) {
73 return left.it_interval.tv_sec == right.it_interval.tv_sec &&
74 left.it_interval.tv_usec == right.it_interval.tv_usec &&
75 left.it_value.tv_sec == right.it_value.tv_sec &&
76 left.it_value.tv_usec == right.it_value.tv_usec;
79bool timerIsDisarmed(
const struct itimerval& value) {
80 const struct itimerval zero = {};
81 return sameTimer(value, zero);
84bool timeNotGreater(
const struct timeval& left,
const struct timeval& right) {
85 return left.tv_sec < right.tv_sec ||
86 (left.tv_sec == right.tv_sec && left.tv_usec <= right.tv_usec);
89bool runningTimerMatches(
const struct itimerval& observed,
const struct itimerval& requested) {
90 const bool positive = observed.it_value.tv_sec > 0 || observed.it_value.tv_usec > 0;
91 return observed.it_interval.tv_sec == requested.it_interval.tv_sec &&
92 observed.it_interval.tv_usec == requested.it_interval.tv_usec &&
93 observed.it_value.tv_sec >= 0 && observed.it_value.tv_usec >= 0 &&
94 observed.it_value.tv_usec < 1000000 && positive &&
95 timeNotGreater(observed.it_value, requested.it_value);
98bool cpuTimerReportInterest(
Process* kernelProcess) {
103 auto& virtualTimer = process->getVirtualIntervalTimer();
104 auto& profileTimer = process->getProfileIntervalTimer();
107 process->publishTimeAccountingForHostedTest(100, 200);
108 bool passed = !process->timeAccountingInterestForHostedTest() &&
109 !process->timeAccountingPendingForHostedTest() && process->
getUserTime() == 100 &&
110 process->getKernelTime() == 200;
112 virtualTimer.setIntervalAndValue(0, 30);
113 profileTimer.setIntervalAndValue(50, 80);
114 passed &= process->timeAccountingInterestForHostedTest() == (VirtualInterest | ProfileInterest);
115 process->publishTimeAccountingForHostedTest(11, 7);
116 virtualTimer.disarm();
117 passed &= process->timeAccountingInterestForHostedTest() == ProfileInterest;
118 virtualTimer.setTimerValue(30);
119 process->publishTimeAccountingForHostedTest(31, 0);
120 for (
size_t attempt = 0;
121 (process->timeAccountingInterestForHostedTest() & VirtualInterest) && attempt < 10000;
127 passed &= process->timeAccountingInterestForHostedTest() == ProfileInterest;
128 Time::Timestamp interval = 0, value = 0;
129 profileTimer.getIntervalAndValue(interval, value);
130 passed &= interval == 50 && value == 31;
131 process->publishTimeAccountingForHostedTest(0, 40);
132 profileTimer.getIntervalAndValue(interval, value);
133 passed &= interval == 50 && value == 41 &&
134 process->timeAccountingInterestForHostedTest() == ProfileInterest;
135 profileTimer.disarm();
136 for (
size_t attempt = 0; process->timeAccountingPendingForHostedTest() && attempt < 10000;
140 process->publishTimeAccountingForHostedTest(500, 700);
141 passed &= !process->timeAccountingInterestForHostedTest() &&
142 !process->timeAccountingPendingForHostedTest() && process->
getUserTime() == 642 &&
143 process->getKernelTime() == 947;
145 virtualTimer.setTimerValue(9);
146 virtualTimer.getIntervalAndValue(interval, value);
147 passed &= value == 9;
148 process->publishTimeAccountingForHostedTest(8, 0);
149 virtualTimer.getIntervalAndValue(interval, value);
150 passed &= value == 1;
151 process->publishTimeAccountingForHostedTest(1, 0);
152 for (
size_t attempt = 0; process->timeAccountingInterestForHostedTest() && attempt < 10000;
156 passed &= !process->timeAccountingInterestForHostedTest();
159 NOTICE(
"HOSTED-WAIT-TEST: PASS cpu-timer-report-interest");
161 ERROR(
"HOSTED-WAIT-TEST: FAIL cpu-timer-report-interest");
166int armAlarmAndExit(
void* parameter) {
167 AlarmThreadContext* context =
reinterpret_cast<AlarmThreadContext*
>(parameter);
168 context->result = posix_alarm(context->seconds);
169 context->returned += 1;
173int exerciseTimeSyscalls(
void* parameter) {
174 TimeSyscallContext* context =
reinterpret_cast<TimeSyscallContext*
>(parameter);
177 bool passed = process->getType() == Process::Posix;
180 const size_t mappingLength = pageSize * 3;
181 uintptr_t address = 0;
182 const bool allocated =
183 process->allocateUserRange(Process::UserRegion::Normal, mappingLength, address);
184 uintptr_t mappedAddress = address;
188 mappedAddress, mappingLength, MemoryMappedObject::Read | MemoryMappedObject::Write)
190 if (!mapping || mappedAddress != address) {
195 process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
197 context->passed =
false;
198 context->returned += 1;
202 struct itimerval* input =
reinterpret_cast<struct itimerval*
>(address + 64);
203 struct itimerval* output =
reinterpret_cast<struct itimerval*
>(address + 256);
204 struct itimerval* alias =
reinterpret_cast<struct itimerval*
>(address + 512);
205 struct itimerval* pageEdge =
206 reinterpret_cast<struct itimerval*
>(address + pageSize - (
sizeof(
struct itimerval) / 2));
207 struct itimerval* readOnly =
reinterpret_cast<struct itimerval*
>(address + (pageSize * 2) + 64);
208 struct timeval* wallClock =
reinterpret_cast<struct timeval*
>(address + 768);
209 struct timezone* timezoneOutput =
reinterpret_cast<struct timezone*
>(address + 832);
210 time_t* secondsOutput =
reinterpret_cast<time_t*
>(address + 896);
212 struct itimerval* bad =
reinterpret_cast<struct itimerval*
>(kernelStart);
214 struct timeval observedWallClock = {};
215 struct timezone observedTimezone = {};
217 passed &= posix_gettimeofday(
nullptr,
nullptr) == 0 && thread->
getErrno() == PreservedErrno;
220 posix_gettimeofday(wallClock, timezoneOutput) == 0 && thread->
getErrno() == PreservedErrno &&
223 observedWallClock.tv_sec >= 0 && observedWallClock.tv_usec >= 0 &&
224 observedWallClock.tv_usec < 1000000 && observedTimezone.tz_minuteswest == 0 &&
225 observedTimezone.tz_dsttime == 0;
227 time_t observedSeconds =
static_cast<time_t
>(-1);
229 const time_t returnedSeconds = posix_time(secondsOutput);
231 returnedSeconds !=
static_cast<time_t
>(-1) && thread->
getErrno() == PreservedErrno &&
233 observedSeconds == returnedSeconds;
235 passed &= posix_time(
nullptr) !=
static_cast<time_t
>(-1) && thread->
getErrno() == PreservedErrno;
237 const struct timezone timezoneSentinel = {37, 1};
241 posix_gettimeofday(
reinterpret_cast<struct timeval*
>(kernelStart), timezoneOutput) == -1 &&
242 thread->
getErrno() == Error::BadAddress &&
244 observedTimezone.tz_minuteswest == timezoneSentinel.tz_minuteswest &&
245 observedTimezone.tz_dsttime == timezoneSentinel.tz_dsttime;
247 passed &= posix_gettimeofday(
nullptr,
reinterpret_cast<struct timezone*
>(kernelStart)) == -1 &&
248 thread->
getErrno() == Error::BadAddress;
250 passed &= posix_time(
reinterpret_cast<time_t*
>(kernelStart)) ==
static_cast<time_t
>(-1) &&
251 thread->
getErrno() == Error::BadAddress;
254 posix_settimeofday(
nullptr,
nullptr) == -1 && thread->
getErrno() == Error::Unimplemented;
256 passed &= posix_settimeofday(
reinterpret_cast<struct timeval*
>(kernelStart),
nullptr) == -1 &&
257 thread->
getErrno() == Error::Unimplemented;
259 const int selectors[] = {ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF};
260 for (
size_t i = 0; passed && i <
sizeof(selectors) /
sizeof(selectors[0]); ++i) {
261 const struct itimerval disarmed = {};
262 const struct itimerval requested =
263 timerValue(
static_cast<time_t
>(i + 1),
static_cast<suseconds_t
>(123456 + i),
264 static_cast<time_t
>(60 + i),
static_cast<suseconds_t
>(654321 + i));
265 struct itimerval observed = {};
267 posix_setitimer(selectors[i], input,
nullptr) == 0 &&
268 posix_getitimer(selectors[i], output) == 0 &&
270 timerIsDisarmed(observed) &&
274 posix_setitimer(selectors[i], input,
nullptr) == 0 && thread->
getErrno() == PreservedErrno;
276 passed &= posix_getitimer(selectors[i], output) == 0 && thread->
getErrno() == PreservedErrno &&
278 runningTimerMatches(observed, requested);
281 const struct itimerval spanning = timerValue(4, 654321, 64, 456789);
282 struct itimerval observed = {};
284 posix_setitimer(ITIMER_REAL, pageEdge,
nullptr) == 0 &&
285 posix_getitimer(ITIMER_REAL, pageEdge) == 0 &&
287 runningTimerMatches(observed, spanning);
289 const struct itimerval baseline = timerValue(7, 700007, 67, 700007);
290 const struct itimerval aliasedRequest = timerValue(9, 900009, 69, 900009);
291 struct itimerval aliasResult = {};
293 posix_setitimer(ITIMER_REAL, input,
nullptr) == 0 &&
295 posix_setitimer(ITIMER_REAL, alias, alias) == 0 &&
297 runningTimerMatches(aliasResult, baseline) &&
298 posix_getitimer(ITIMER_REAL, output) == 0 &&
300 runningTimerMatches(observed, aliasedRequest);
302 struct itimerval invalidValues[] = {
303 timerValue(-1, 0), timerValue(0, -1), timerValue(0, 1000000),
304 timerValue(0, 0, -1, 0), timerValue(0, 0, 0, -1), timerValue(0, 0, 0, 1000000),
306 for (
size_t i = 0; passed && i <
sizeof(invalidValues) /
sizeof(invalidValues[0]); ++i) {
309 passed &= posix_setitimer(ITIMER_REAL, input,
nullptr) == -1 &&
310 thread->
getErrno() == Error::InvalidArgument &&
311 posix_getitimer(ITIMER_REAL, output) == 0 &&
313 runningTimerMatches(observed, aliasedRequest);
316 const time_t saturationSeconds =
317 static_cast<time_t
>(MaximumLinuxTimerNanoseconds / Time::Multiplier::Second);
318 const struct itimerval huge = timerValue(saturationSeconds, 0, saturationSeconds, 0);
320 posix_setitimer(ITIMER_VIRTUAL, input,
nullptr) == 0 &&
321 process->getVirtualIntervalTimer().getInterval() == MaximumLinuxTimerNanoseconds &&
322 process->getVirtualIntervalTimer().getValue() == MaximumLinuxTimerNanoseconds;
324 passed &= posix_setitimer(ITIMER_VIRTUAL,
nullptr, output) == 0 &&
325 thread->
getErrno() == PreservedErrno &&
326 process->getVirtualIntervalTimer().getInterval() == 0 &&
327 process->getVirtualIntervalTimer().getValue() == 0 &&
329 observed.it_interval.tv_sec ==
330 static_cast<time_t
>(MaximumLinuxTimerNanoseconds / Time::Multiplier::Second) &&
331 observed.it_interval.tv_usec ==
332 static_cast<suseconds_t
>((MaximumLinuxTimerNanoseconds % Time::Multiplier::Second) /
333 Time::Multiplier::Microsecond) &&
334 posix_getitimer(ITIMER_VIRTUAL, output) == 0 &&
336 timerIsDisarmed(observed);
339 passed &= posix_getitimer(-1, bad) == -1 && thread->
getErrno() == Error::InvalidArgument;
341 passed &= posix_setitimer(-1, bad,
nullptr) == -1 && thread->
getErrno() == Error::BadAddress;
342 const struct itimerval malformed = timerValue(0, 1000000);
346 posix_setitimer(-1, input,
nullptr) == -1 && thread->
getErrno() == Error::InvalidArgument;
349 passed &= posix_setitimer(-1, input, bad) == -1 && thread->
getErrno() == Error::InvalidArgument;
351 passed &= posix_setitimer(-1,
nullptr, bad) == -1 && thread->
getErrno() == Error::InvalidArgument;
353 const struct itimerval beforeFault = timerValue(11, 111111, 71, 111111);
355 posix_setitimer(ITIMER_REAL, input,
nullptr) == 0;
357 passed &= posix_getitimer(ITIMER_REAL,
nullptr) == -1 && thread->
getErrno() == Error::BadAddress;
359 passed &= posix_getitimer(ITIMER_REAL, bad) == -1 && thread->
getErrno() == Error::BadAddress;
361 passed &= posix_setitimer(ITIMER_REAL, bad,
nullptr) == -1 &&
362 thread->
getErrno() == Error::BadAddress && posix_getitimer(ITIMER_REAL, output) == 0 &&
364 runningTimerMatches(observed, beforeFault);
366 const struct itimerval readOnlyRequest = timerValue(12, 121212, 72, 121212);
369 MemoryMappedObject::Read) == 1 &&
370 posix_setitimer(ITIMER_REAL, readOnly,
nullptr) == 0;
372 passed &= posix_getitimer(ITIMER_REAL, readOnly) == -1 && thread->
getErrno() == Error::BadAddress;
374 const struct itimerval lateFaultRequest = timerValue(13, 131313, 73, 131313);
377 passed &= posix_setitimer(ITIMER_REAL, input, readOnly) == -1 &&
378 thread->
getErrno() == Error::BadAddress && posix_getitimer(ITIMER_REAL, output) == 0 &&
380 runningTimerMatches(observed, lateFaultRequest);
382 const struct itimerval alarmReplacementBaseline = timerValue(23, 232323, 600, 999999);
383 const struct itimerval alarmRequest = timerValue(0, 0, 120, 0);
385 sizeof(alarmReplacementBaseline)) &&
386 posix_setitimer(ITIMER_REAL, input,
nullptr) == 0;
388 passed &= posix_alarm(120) == 601 && thread->
getErrno() == PreservedErrno &&
389 posix_getitimer(ITIMER_REAL, output) == 0 &&
391 runningTimerMatches(observed, alarmRequest);
393 const struct itimerval setitimerReplacement = timerValue(5, 555555, 500, 999999);
394 struct itimerval previousAlarm = {};
397 posix_setitimer(ITIMER_REAL, input, alias) == 0 &&
399 runningTimerMatches(previousAlarm, alarmRequest) &&
400 posix_getitimer(ITIMER_REAL, output) == 0 &&
402 runningTimerMatches(observed, setitimerReplacement);
405 passed &= posix_alarm(0) == 501 && thread->
getErrno() == PreservedErrno &&
406 posix_getitimer(ITIMER_REAL, output) == 0 &&
408 timerIsDisarmed(observed);
410 const struct itimerval belowHalfSecond = timerValue(0, 0, 400, 250000);
412 posix_setitimer(ITIMER_REAL, input,
nullptr) == 0 && posix_alarm(0) == 400;
414 const struct itimerval subsecondAlarm = timerValue(0, 0, 0, 999999);
416 posix_setitimer(ITIMER_REAL, input,
nullptr) == 0 && posix_alarm(0) == 1 &&
417 posix_getitimer(ITIMER_REAL, output) == 0 &&
419 timerIsDisarmed(observed);
421 AlarmThreadContext alarmThreadContext(300);
423 new Thread(process, armAlarmAndExit, &alarmThreadContext,
nullptr,
false,
true,
true);
424 alarmThread->setName(
"hosted transient alarm caller");
425 const bool alarmThreadStarted = alarmThread->
start();
426 const bool alarmThreadJoined = alarmThreadStarted && alarmThread->
joinForCompletion();
427 if (!alarmThreadStarted) {
430 passed &= alarmThreadStarted && alarmThreadJoined && alarmThreadContext.returned == 1 &&
431 alarmThreadContext.result == 0 && posix_getitimer(ITIMER_REAL, output) == 0 &&
433 runningTimerMatches(observed, timerValue(0, 0, 300, 0)) && posix_alarm(0) > 0 &&
434 posix_getitimer(ITIMER_REAL, output) == 0 &&
436 timerIsDisarmed(observed);
439 posix_setitimer(ITIMER_REAL, input,
nullptr) == 0;
441 const bool middleRemoved =
443 passed &= middleRemoved;
445 passed &= posix_setitimer(ITIMER_REAL, pageEdge,
nullptr) == -1 &&
446 thread->
getErrno() == Error::BadAddress;
448 passed &= posix_getitimer(ITIMER_REAL, pageEdge) == -1 && thread->
getErrno() == Error::BadAddress;
450 passed &= posix_setitimer(ITIMER_REAL,
reinterpret_cast<struct itimerval*
>(address + pageSize),
452 thread->
getErrno() == Error::BadAddress && posix_getitimer(ITIMER_REAL, output) == 0 &&
454 runningTimerMatches(observed, lateFaultRequest);
456 for (
size_t i = 0; i <
sizeof(selectors) /
sizeof(selectors[0]); ++i) {
457 passed &= posix_setitimer(selectors[i],
nullptr,
nullptr) == 0;
459 Time::Timestamp finalInterval = 1;
460 Time::Timestamp finalValue = 1;
461 process->getRealIntervalTimer().getIntervalAndValue(finalInterval, finalValue);
462 passed &= !finalInterval && !finalValue;
463 process->getVirtualIntervalTimer().getIntervalAndValue(finalInterval, finalValue);
464 passed &= !finalInterval && !finalValue;
465 process->getProfileIntervalTimer().getIntervalAndValue(finalInterval, finalValue);
466 passed &= !finalInterval && !finalValue;
473 process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
476 context->passed = passed;
477 context->returned += 1;
478 return passed ? 0 : 1;
482bool runHostedTimeSyscallRegressions(
Process* kernelProcess) {
483 const bool interestPassed = cpuTimerReportInterest(kernelProcess);
486 TimeSyscallContext context;
487 Thread*
worker =
new Thread(process, exerciseTimeSyscalls, &context,
nullptr,
false,
true,
true);
488 worker->setName(
"hosted interval timer syscall worker");
491 const bool started =
worker->start();
492 const bool joined = started &&
worker->joinForCompletion();
497 interestPassed && started && joined && context.returned == 1 && context.passed;
502 "HOSTED-SYSCALL-TEST: FAIL time-syscall-usercopy: "
503 "wall-clock, interval-timer, or alarm behavior regressed");
506 NOTICE(
"HOSTED-SYSCALL-TEST: PASS time-syscall-usercopy");
Memory-mapped file interface.
@ Virtual
CPU time in user mode only.
@ Profile
CPU time in user and system.
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
size_t remove(uintptr_t base, size_t length)
static MemoryMapManager & instance()
static constexpr size_t getPageSize() PURE
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)
Time::Timestamp getUserTime() const
static ProcessorInformation & information()
static Scheduler & instance()
void setErrno(size_t err)
Process * getParent() const