#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; } addr_t sys_nanosleep(void) { addr_t tsaddr, raw; if (argaddr(0, &tsaddr) < 0 || fetchaddr(tsaddr, &raw) < 0) return -1; int64 sec = (int64)raw; if (sec > 0) { acquire(&tickslock); uint t0 = ticks; while (ticks - t0 < (uint)(sec * HZ)) { if (myproc()->killed) { release(&tickslock); return -EINTR; } sleep(&ticks, &tickslock); } release(&tickslock); } return 0; } 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) { // Treat the same as nanosleep: arg2 is the timespec, arg1 is flags. // Linux: clock_nanosleep(clockid, flags, rqtp, rmtp) addr_t tsaddr, raw; if (argaddr(2, &tsaddr) < 0 || fetchaddr(tsaddr, &raw) < 0) return -1; int64 sec = (int64)raw; if (sec > 0) { acquire(&tickslock); uint t0 = ticks; while (ticks - t0 < (uint)(sec * HZ)) { if (myproc()->killed) { release(&tickslock); return -EINTR; } sleep(&ticks, &tickslock); } release(&tickslock); } return 0; }