240 lines
7.2 KiB
C++
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 = ¤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<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;
|
|
}
|