#define _TIMESPEC_MACROS_INCLUDED #include "Pledge.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" #include "thread/Timer.h" #include #include #include #include #include Result sys_setitimer(Registers*, SyscallArgs args) { int which = (int)args[0]; const struct itimerval* new_timer = (const struct itimerval*)args[1]; struct itimerval* old_timer = (struct itimerval*)args[2]; auto* current = Scheduler::current(); TRY(check_pledge(current, Promise::p_stdio)); Timer* timer; Clock* clock; switch (which) { case ITIMER_REAL: timer = ¤t->real_timer; clock = &g_realtime_clock; break; case ITIMER_VIRTUAL: timer = ¤t->virtual_timer; clock = ¤t->virtual_clock; break; case ITIMER_PROF: timer = ¤t->profiling_timer; clock = ¤t->profiling_clock; break; default: return err(EINVAL); } if (old_timer) { if (timer->active_clock) { struct itimerval result; auto total = timer->active_clock->from_ticks(timer->interval_ticks); auto left = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer)); result.it_interval = TIMESPEC_TO_TIMEVAL(total); result.it_value = TIMESPEC_TO_TIMEVAL(left); if (!MemoryManager::copy_to_user_typed(old_timer, &result)) return err(EFAULT); } else { struct itimerval result; memset(&result, 0, sizeof(result)); if (!MemoryManager::copy_to_user_typed(old_timer, &result)) return err(EFAULT); } } if (new_timer) { timer->disarm(); struct itimerval itimer; if (!MemoryManager::copy_from_user_typed(new_timer, &itimer)) return err(EFAULT); timer->signo = SIGALRM; // FIXME: Also use SIGVTALRM or SIGPROF for other timer types. timer->thread = current; if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_usec != 0) { timer->restart = true; timer->interval_ticks = (itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_usec * 1000) / clock->resolution(); } else timer->restart = false; if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_usec != 0) { u64 ticks = (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_usec * 1000) / clock->resolution(); timer->arm(clock, ticks); } } return 0; } Result sys_timer_create(Registers*, SyscallArgs args) { clockid_t clockid = (clockid_t)args[0]; struct sigevent* sevp = (struct sigevent*)args[1]; timer_t* timerid = (timer_t*)args[2]; auto* current = Scheduler::current(); TRY(check_pledge(current, Promise::p_stdio)); struct sigevent ksevp; if (!sevp) { ksevp.sigev_notify = SIGEV_SIGNAL; ksevp.sigev_signo = SIGALRM; } else if (!MemoryManager::copy_from_user_typed(sevp, &ksevp)) return err(EFAULT); Clock* clock; switch (clockid) { case CLOCK_REALTIME: clock = &g_realtime_clock; break; case CLOCK_MONOTONIC: clock = &g_monotonic_clock; break; default: return err(EINVAL); } if (ksevp.sigev_notify != SIGEV_SIGNAL) return err(ENOTSUP); if (ksevp.sigev_signo <= 0 || ksevp.sigev_signo > NSIG) return err(EINVAL); int id = TRY(current->allocate_timerid()); Timer* timer = current->posix_timers[id].value_ptr(); timer->signo = ksevp.sigev_signo; timer->designated_clock = clock; if (!MemoryManager::copy_to_user_typed(timerid, &id)) return err(EFAULT); return 0; } Result sys_timer_settime(Registers*, SyscallArgs args) { timer_t timerid = (timer_t)args[0]; int flags = (int)args[1]; const struct itimerspec* new_value = (const struct itimerspec*)args[2]; struct itimerspec* old_value = (struct itimerspec*)args[3]; if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL); if (flags > 0) return err(ENOTSUP); auto* current = Scheduler::current(); TRY(check_pledge(current, Promise::p_stdio)); if (!current->posix_timers[timerid].has_value()) return err(EINVAL); Timer* timer = current->posix_timers[timerid].value_ptr(); if (old_value) { if (timer->active_clock) { struct itimerspec result; result.it_interval = timer->active_clock->from_ticks(timer->interval_ticks); result.it_value = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer)); if (!MemoryManager::copy_to_user_typed(old_value, &result)) return err(EFAULT); } else { struct itimerspec result; memset(&result, 0, sizeof(result)); if (!MemoryManager::copy_to_user_typed(old_value, &result)) return err(EFAULT); } } struct itimerspec itimer; if (!MemoryManager::copy_from_user_typed(new_value, &itimer)) return err(EFAULT); timer->disarm(); Clock* clock = timer->designated_clock; check(clock); timer->thread = current; if (itimer.it_interval.tv_sec != 0 || itimer.it_interval.tv_nsec != 0) { timer->restart = true; timer->interval_ticks = (itimer.it_interval.tv_sec * 1'000'000'000 + itimer.it_interval.tv_nsec) / clock->resolution(); } else timer->restart = false; if (itimer.it_value.tv_sec != 0 || itimer.it_value.tv_nsec != 0) { u64 ticks = (itimer.it_value.tv_sec * 1'000'000'000 + itimer.it_value.tv_nsec) / clock->resolution(); timer->arm(clock, ticks); } return 0; } Result sys_timer_gettime(Registers*, SyscallArgs args) { timer_t timerid = (timer_t)args[0]; struct itimerspec* value = (struct itimerspec*)args[1]; if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL); auto* current = Scheduler::current(); TRY(check_pledge(current, Promise::p_stdio)); if (!current->posix_timers[timerid].has_value()) return err(EINVAL); Timer* timer = current->posix_timers[timerid].value_ptr(); if (timer->active_clock) { struct itimerspec result; result.it_interval = timer->active_clock->from_ticks(timer->interval_ticks); result.it_value = timer->active_clock->from_ticks(timer->active_clock->ticks_left(timer)); if (!MemoryManager::copy_to_user_typed(value, &result)) return err(EFAULT); } else { struct itimerspec result; memset(&result, 0, sizeof(result)); if (!MemoryManager::copy_to_user_typed(value, &result)) return err(EFAULT); } return 0; } Result sys_timer_delete(Registers*, SyscallArgs args) { timer_t timerid = (timer_t)args[0]; if (timerid < 0 || timerid >= MAX_POSIX_TIMERS) return err(EINVAL); auto* current = Scheduler::current(); TRY(check_pledge(current, Promise::p_stdio)); if (!current->posix_timers[timerid].has_value()) return err(EINVAL); Timer* timer = current->posix_timers[timerid].value_ptr(); timer->disarm(); current->posix_timers[timerid] = {}; return 0; }