Luna/kernel/src/sys/setitimer.cpp

240 lines
7.2 KiB
C++

#define _TIMESPEC_MACROS_INCLUDED
#include "Pledge.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include "thread/Timer.h"
#include <bits/clockid.h>
#include <bits/itimer.h>
#include <bits/sigevent.h>
#include <luna/Common.h>
#include <sys/types.h>
Result<u64> 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 = &current->real_timer;
clock = &g_realtime_clock;
break;
case ITIMER_VIRTUAL:
timer = &current->virtual_timer;
clock = &current->virtual_clock;
break;
case ITIMER_PROF:
timer = &current->profiling_timer;
clock = &current->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<u64> 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<u64> 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<u64> 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<u64> 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;
}