#include "core/types.h" #include "core/defs.h" #include "core/param.h" #include "core/date.h" #include "core/spinlock.h" #include "core/proc.h" #include "arch/x86.h" #include "core/syscall.h" #include "uapi/posix.h" #include "core/uaccess.h" #include "uapi/errno.h" // Timer values in ticks, rounding a nonzero time up: POSIX says a timer set // to 1 us goes off, not that it is disarmed. static uint tv_ticks(struct timeval *tv) { if (tv->tv_sec < 0 || tv->tv_usec < 0) return 0; uint64 t = (uint64)tv->tv_sec * HZ + ((uint64)tv->tv_usec + USEC_PER_TICK - 1) / USEC_PER_TICK; return t > 0x7fffffff ? 0x7fffffff : (uint)t; } static void ticks_tv(uint t, struct timeval *tv) { tv->tv_sec = t / HZ; tv->tv_usec = (t % HZ) * USEC_PER_TICK; } // The seconds left on the previous alarm, rounded up, as POSIX asks. addr_t sys_alarm(void) { int seconds; if (argint(0, &seconds) < 0) return -1; struct proc *p = myproc(); uint left = alarm_left(p); alarm_arm(p, seconds > 0 ? (uint)seconds * HZ : 0, 0); return (left + HZ - 1) / HZ; } addr_t sys_gettimeofday(void) { struct timeval *tv; if (argptr(0, (char**)&tv, sizeof(*tv)) < 0) return -1; if (tv) { // Real time, from the hardware clock read at boot plus uptime -- not // uptime alone, which is what this used to return and why every machine // thought it was 1970. See kernel/core/time.c. uint64 sec, usec; wallclock(&sec, &usec); tv->tv_sec = (long)sec; tv->tv_usec = (long)usec; } return 0; } // Sleep for a timespec, in ticks, rounded up. // // Only tv_sec was ever read: `sleep 0.5` returned at once and every // sub-second sleep -- cmatrix's frame delay, a game loop's -- was no sleep at // all. And a signal could not end one: Ctrl-Z on `sleep 100` did nothing. static addr_t sleep_ticks(uint64 want, addr_t remaddr) { struct proc *p = myproc(); acquire(&tickslock); uint t0 = ticks; while ((uint64)(uint)(ticks - t0) < want) { if (p->killed || sig_attention(p)) { release(&tickslock); int e = p->killed ? -EINTR : sig_blocking_check(); if (e) { if (remaddr) { uint64 left = want - (uint)(ticks - t0); struct timespec r = { (long)(left / HZ), (long)((left % HZ) * (1000000000 / HZ)) }; copy_to_user(remaddr, &r, sizeof(r)); } return e; } acquire(&tickslock); continue; } sleep(&ticks, &tickslock); } release(&tickslock); return 0; } static int timespec_ticks(const struct timespec *ts, uint64 *out) { if (ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000) return -EINVAL; uint64 sec = (uint64)ts->tv_sec; if (sec > 0xFFFFFFFFULL / HZ) sec = 0xFFFFFFFFULL / HZ; // a few years: forever enough *out = sec * HZ + ((uint64)ts->tv_nsec * HZ + 999999999) / 1000000000; return 0; } addr_t sys_nanosleep(void) { addr_t tsaddr, remaddr; struct timespec ts; uint64 want; argaddr(0, &tsaddr); argaddr(1, &remaddr); if (copy_from_user(&ts, tsaddr, sizeof(ts)) < 0) return -EFAULT; int e = timespec_ticks(&ts, &want); if (e) return e; return sleep_ticks(want, remaddr); } addr_t sys_clock_gettime(void) { int clkid; struct timespec *tp; argint(0, &clkid); if (argptr(1, (char**)&tp, sizeof(*tp)) < 0) return -1; // CLOCK_MONOTONIC (1) counts from boot; everything else is treated as a // realtime clock and counts from the epoch. Returning boot-relative values // for CLOCK_REALTIME is what made timestamps land in 1970. uint64 sec, usec; wallclock(&sec, &usec); if (clkid == 1) sec -= boot_epoch; tp->tv_sec = (long)sec; tp->tv_nsec = (long)(usec * 1000); return 0; } // ITIMER_REAL only. ITIMER_VIRTUAL and ITIMER_PROF count CPU time, which // nothing here measures per tick, and are refused rather than accepted and // never fired -- which is what all three used to be. static int itimer_get(struct proc *p, struct itimerval *iv) { ticks_tv(p->alarm_every, &iv->it_interval); ticks_tv(alarm_left(p), &iv->it_value); return 0; } addr_t sys_getitimer(void) { int which; addr_t curp; argint(0, &which); argaddr(1, &curp); if (which != 0) return -EINVAL; struct itimerval iv; itimer_get(myproc(), &iv); if (copy_to_user(curp, &iv, sizeof(iv)) != 0) return -EFAULT; return 0; } addr_t sys_setitimer(void) { int which; addr_t newp, oldp; argint(0, &which); argaddr(1, &newp); argaddr(2, &oldp); if (which != 0) return -EINVAL; struct proc *p = myproc(); struct itimerval iv; if (oldp) { itimer_get(p, &iv); if (copy_to_user(oldp, &iv, sizeof(iv)) != 0) return -EFAULT; } if (newp == 0) return 0; if (copy_from_user(&iv, newp, sizeof(iv)) != 0) return -EFAULT; alarm_arm(p, tv_ticks(&iv.it_value), tv_ticks(&iv.it_interval)); return 0; } addr_t sys_time(void) { addr_t tloc; argaddr(0, &tloc); uint64 sec; wallclock(&sec, 0); long t = (long)sec; if (tloc) put_user(t, tloc); return (addr_t)t; } addr_t sys_clock_getres(void) { struct timespec *tp; if (argptr(1, (char**)&tp, sizeof(*tp)) < 0) return 0; if (tp) { tp->tv_sec = 0; tp->tv_nsec = 10000000L; } // 10ms resolution return 0; } addr_t sys_clock_nanosleep(void) { // clock_nanosleep(clockid, flags, rqtp, rmtp). TIMER_ABSTIME (1) makes // rqtp a time on that clock rather than an interval. int clkid, flags; addr_t tsaddr, remaddr; struct timespec ts; uint64 want; argint(0, &clkid); argint(1, &flags); argaddr(2, &tsaddr); argaddr(3, &remaddr); if (copy_from_user(&ts, tsaddr, sizeof(ts)) < 0) return -EFAULT; if (flags & 1) { uint64 sec, usec; wallclock(&sec, &usec); if (clkid == 1) sec -= boot_epoch; long dsec = ts.tv_sec - (long)sec; long dns = ts.tv_nsec - (long)(usec * 1000); if (dns < 0) { dns += 1000000000; dsec--; } if (dsec < 0) return 0; // already past ts.tv_sec = dsec; ts.tv_nsec = dns; remaddr = 0; // never written for an absolute sleep } int e = timespec_ticks(&ts, &want); if (e) return e; return sleep_ticks(want, remaddr); }