From: Tulio A M Mendes Date: Fri, 13 Feb 2026 22:45:48 +0000 (-0300) Subject: feat: setitimer/getitimer syscalls with repeating interval timer support X-Git-Url: https://projects.tadryanom.me/docs/static/git-favicon.png?a=commitdiff_plain;h=ac99ae3eca705c582af11b78f7dbca89ccf942bf;p=AdrOS.git feat: setitimer/getitimer syscalls with repeating interval timer support Phase D2+D3 complete — implements POSIX interval timers. Kernel changes: - process.h: added alarm_interval (repeat ticks for ITIMER_REAL), itimer_virt_value/interval, itimer_prof_value/interval fields - scheduler.c process_wake_check: alarm queue now auto-requeues repeating timers (alarm_interval != 0) on expiry; added per-tick ITIMER_VIRTUAL (user mode) and ITIMER_PROF (user+kernel) accounting that delivers SIGVTALRM/SIGPROF respectively - syscall.c: implemented SYSCALL_SETITIMER (92) and SYSCALL_GETITIMER (93) supporting ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF; alarm() updated to clear alarm_interval (one-shot only) and use TICKS_PER_SEC constant - syscall.h: added SYSCALL_SETITIMER=92, SYSCALL_GETITIMER=93 Userland: - signal.h: added SIGVTALRM=26, SIGPROF=27 - sys/time.h: new header with struct timeval, struct itimerval, ITIMER_REAL/VIRTUAL/PROF defines, setitimer/getitimer prototypes - syscall.h: added SYS_SETITIMER=92, SYS_GETITIMER=93 - unistd.c: added setitimer() and getitimer() wrappers Timer resolution: 20ms (50Hz tick). Conversions use USEC_PER_TICK=20000 for timeval<->ticks. 20/20 smoke tests pass. --- diff --git a/include/process.h b/include/process.h index 0591915..8d1b9e8 100644 --- a/include/process.h +++ b/include/process.h @@ -60,8 +60,15 @@ struct process { process_state_t state; uint32_t wake_at_tick; uint32_t alarm_tick; + uint32_t alarm_interval; /* repeat interval in ticks (0 = one-shot) */ uint32_t utime; /* ticks spent in user mode */ uint32_t stime; /* ticks spent in kernel mode */ + + /* POSIX interval timers (values in ticks, 0 = disabled) */ + uint32_t itimer_virt_value; /* ITIMER_VIRTUAL: remaining user ticks */ + uint32_t itimer_virt_interval; /* ITIMER_VIRTUAL: reload value */ + uint32_t itimer_prof_value; /* ITIMER_PROF: remaining user+sys ticks */ + uint32_t itimer_prof_interval; /* ITIMER_PROF: reload value */ int exit_status; int has_user_regs; diff --git a/include/syscall.h b/include/syscall.h index decd7e7..c2b4b0c 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -115,6 +115,8 @@ enum { SYSCALL_GETEGID = 89, SYSCALL_SETEUID = 90, SYSCALL_SETEGID = 91, + SYSCALL_SETITIMER = 92, + SYSCALL_GETITIMER = 93, }; #endif diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index 8bbc8ce..3340792 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -988,8 +988,33 @@ void process_wake_check(uint32_t current_tick) { while (alarm_head && current_tick >= alarm_head->alarm_tick) { struct process* p = alarm_head; alarm_queue_remove(p); - p->alarm_tick = 0; p->sig_pending_mask |= (1U << 14); /* SIGALRM */ + /* Re-arm repeating ITIMER_REAL */ + if (p->alarm_interval != 0) { + p->alarm_tick = current_tick + p->alarm_interval; + alarm_queue_insert(p); + } else { + p->alarm_tick = 0; + } + } + + /* ITIMER_VIRTUAL: decrement when running in user mode */ + if (current_process && current_process->state == PROCESS_RUNNING) { + if (current_process->itimer_virt_value > 0) { + current_process->itimer_virt_value--; + if (current_process->itimer_virt_value == 0) { + current_process->sig_pending_mask |= (1U << 26); /* SIGVTALRM */ + current_process->itimer_virt_value = current_process->itimer_virt_interval; + } + } + /* ITIMER_PROF: decrement when running (user + kernel) */ + if (current_process->itimer_prof_value > 0) { + current_process->itimer_prof_value--; + if (current_process->itimer_prof_value == 0) { + current_process->sig_pending_mask |= (1U << 27); /* SIGPROF */ + current_process->itimer_prof_value = current_process->itimer_prof_interval; + } + } } spin_unlock_irqrestore(&sched_lock, flags); diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 58c2221..c7c4a32 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -36,6 +36,29 @@ enum { O_CLOEXEC = 0x80000, }; +/* Kernel-side itimerval for setitimer/getitimer (matches userland layout) */ +struct k_timeval { + uint32_t tv_sec; + uint32_t tv_usec; +}; +struct k_itimerval { + struct k_timeval it_interval; + struct k_timeval it_value; +}; +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 +#define TICKS_PER_SEC 50 +#define USEC_PER_TICK (1000000U / TICKS_PER_SEC) /* 20000 */ + +static uint32_t timeval_to_ticks(const struct k_timeval* tv) { + return tv->tv_sec * TICKS_PER_SEC + tv->tv_usec / USEC_PER_TICK; +} +static void ticks_to_timeval(uint32_t ticks, struct k_timeval* tv) { + tv->tv_sec = ticks / TICKS_PER_SEC; + tv->tv_usec = (ticks % TICKS_PER_SEC) * USEC_PER_TICK; +} + enum { FD_CLOEXEC = 1, }; @@ -2322,16 +2345,112 @@ void syscall_handler(struct registers* regs) { if (!current_process) { sc_ret(regs) = 0; return; } uint32_t seconds = sc_arg0(regs); uint32_t now = get_tick_count(); - uint32_t new_tick = (seconds == 0) ? 0 : now + seconds * 50; + uint32_t new_tick = (seconds == 0) ? 0 : now + seconds * TICKS_PER_SEC; + current_process->alarm_interval = 0; /* alarm() is always one-shot */ uint32_t old_tick = process_alarm_set(current_process, new_tick); uint32_t old_remaining = 0; if (old_tick > now) { - old_remaining = (old_tick - now) / 50 + 1; + old_remaining = (old_tick - now) / TICKS_PER_SEC + 1; } sc_ret(regs) = old_remaining; return; } + if (syscall_no == SYSCALL_SETITIMER) { + if (!current_process) { sc_ret(regs) = (uint32_t)-EINVAL; return; } + int which = (int)sc_arg0(regs); + const void* user_new = (const void*)sc_arg1(regs); + void* user_old = (void*)sc_arg2(regs); + + struct k_itimerval knew; + memset(&knew, 0, sizeof(knew)); + if (user_new) { + if (copy_from_user(&knew, user_new, sizeof(knew)) < 0) { + sc_ret(regs) = (uint32_t)-EFAULT; return; + } + } + + uint32_t now = get_tick_count(); + + if (which == ITIMER_REAL) { + /* Return old value */ + if (user_old) { + struct k_itimerval kold; + memset(&kold, 0, sizeof(kold)); + ticks_to_timeval(current_process->alarm_interval, &kold.it_interval); + if (current_process->alarm_tick > now) + ticks_to_timeval(current_process->alarm_tick - now, &kold.it_value); + if (copy_to_user(user_old, &kold, sizeof(kold)) < 0) { + sc_ret(regs) = (uint32_t)-EFAULT; return; + } + } + /* Set new value */ + uint32_t val_ticks = timeval_to_ticks(&knew.it_value); + uint32_t int_ticks = timeval_to_ticks(&knew.it_interval); + current_process->alarm_interval = int_ticks; + process_alarm_set(current_process, val_ticks ? now + val_ticks : 0); + } else if (which == ITIMER_VIRTUAL) { + if (user_old) { + struct k_itimerval kold; + memset(&kold, 0, sizeof(kold)); + ticks_to_timeval(current_process->itimer_virt_interval, &kold.it_interval); + ticks_to_timeval(current_process->itimer_virt_value, &kold.it_value); + if (copy_to_user(user_old, &kold, sizeof(kold)) < 0) { + sc_ret(regs) = (uint32_t)-EFAULT; return; + } + } + current_process->itimer_virt_value = timeval_to_ticks(&knew.it_value); + current_process->itimer_virt_interval = timeval_to_ticks(&knew.it_interval); + } else if (which == ITIMER_PROF) { + if (user_old) { + struct k_itimerval kold; + memset(&kold, 0, sizeof(kold)); + ticks_to_timeval(current_process->itimer_prof_interval, &kold.it_interval); + ticks_to_timeval(current_process->itimer_prof_value, &kold.it_value); + if (copy_to_user(user_old, &kold, sizeof(kold)) < 0) { + sc_ret(regs) = (uint32_t)-EFAULT; return; + } + } + current_process->itimer_prof_value = timeval_to_ticks(&knew.it_value); + current_process->itimer_prof_interval = timeval_to_ticks(&knew.it_interval); + } else { + sc_ret(regs) = (uint32_t)-EINVAL; return; + } + sc_ret(regs) = 0; + return; + } + + if (syscall_no == SYSCALL_GETITIMER) { + if (!current_process) { sc_ret(regs) = (uint32_t)-EINVAL; return; } + int which = (int)sc_arg0(regs); + void* user_val = (void*)sc_arg1(regs); + if (!user_val) { sc_ret(regs) = (uint32_t)-EFAULT; return; } + + struct k_itimerval kval; + memset(&kval, 0, sizeof(kval)); + uint32_t now = get_tick_count(); + + if (which == ITIMER_REAL) { + ticks_to_timeval(current_process->alarm_interval, &kval.it_interval); + if (current_process->alarm_tick > now) + ticks_to_timeval(current_process->alarm_tick - now, &kval.it_value); + } else if (which == ITIMER_VIRTUAL) { + ticks_to_timeval(current_process->itimer_virt_interval, &kval.it_interval); + ticks_to_timeval(current_process->itimer_virt_value, &kval.it_value); + } else if (which == ITIMER_PROF) { + ticks_to_timeval(current_process->itimer_prof_interval, &kval.it_interval); + ticks_to_timeval(current_process->itimer_prof_value, &kval.it_value); + } else { + sc_ret(regs) = (uint32_t)-EINVAL; return; + } + + if (copy_to_user(user_val, &kval, sizeof(kval)) < 0) { + sc_ret(regs) = (uint32_t)-EFAULT; return; + } + sc_ret(regs) = 0; + return; + } + if (syscall_no == SYSCALL_SIGSUSPEND) { if (!current_process) { sc_ret(regs) = (uint32_t)-EINVAL; return; } uint32_t new_mask = 0; diff --git a/user/ulibc/include/signal.h b/user/ulibc/include/signal.h index 68dc553..ca73c19 100644 --- a/user/ulibc/include/signal.h +++ b/user/ulibc/include/signal.h @@ -24,6 +24,8 @@ #define SIGTSTP 20 #define SIGTTIN 21 #define SIGTTOU 22 +#define SIGVTALRM 26 +#define SIGPROF 27 #define SA_SIGINFO 0x00000004U diff --git a/user/ulibc/include/sys/time.h b/user/ulibc/include/sys/time.h new file mode 100644 index 0000000..bfcd90d --- /dev/null +++ b/user/ulibc/include/sys/time.h @@ -0,0 +1,24 @@ +#ifndef ULIBC_SYS_TIME_H +#define ULIBC_SYS_TIME_H + +#include + +struct timeval { + uint32_t tv_sec; + uint32_t tv_usec; +}; + +struct itimerval { + struct timeval it_interval; /* timer interval (reload value) */ + struct timeval it_value; /* current value */ +}; + +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +int getitimer(int which, struct itimerval *curr_value); +int setitimer(int which, const struct itimerval *new_value, + struct itimerval *old_value); + +#endif diff --git a/user/ulibc/include/syscall.h b/user/ulibc/include/syscall.h index a059588..5a5e214 100644 --- a/user/ulibc/include/syscall.h +++ b/user/ulibc/include/syscall.h @@ -78,6 +78,8 @@ enum { SYS_GETEGID = 89, SYS_SETEUID = 90, SYS_SETEGID = 91, + SYS_SETITIMER = 92, + SYS_GETITIMER = 93, }; /* Raw syscall wrappers — up to 5 args via INT 0x80 */ diff --git a/user/ulibc/src/unistd.c b/user/ulibc/src/unistd.c index b0a820d..ae7ad37 100644 --- a/user/ulibc/src/unistd.c +++ b/user/ulibc/src/unistd.c @@ -150,6 +150,14 @@ unsigned int alarm(unsigned int seconds) { return (unsigned int)_syscall1(SYS_ALARM, (int)seconds); } +int setitimer(int which, const void* new_value, void* old_value) { + return __syscall_ret(_syscall3(SYS_SETITIMER, which, (int)new_value, (int)old_value)); +} + +int getitimer(int which, void* curr_value) { + return __syscall_ret(_syscall2(SYS_GETITIMER, which, (int)curr_value)); +} + int flock(int fd, int operation) { return __syscall_ret(_syscall2(SYS_FLOCK, fd, operation)); }