- SIGSTOP/SIGTSTP/SIGTTIN/SIGTTOU stop, SIGCONT resumes; the parent gets SIGCHLD and wait4 reports WUNTRACED/WCONTINUED; ps shows T. Ctrl-Z works, and mc no longer hangs waiting for its subshell to stop. - Blocking reads (console, pty, pipe) and sleeps take a pending stop and carry on after SIGCONT; a caught signal without SA_RESTART ends them with EINTR. - nanosleep reads tv_nsec (sleep 0.5 used to return at once), writes the remainder, and clock_nanosleep understands TIMER_ABSTIME. - rt_sigaction uses the k_sigaction layout for Linux-ABI processes: the old action's restorer was written 112 bytes past the caller's struct. Flags are kept; SIGKILL and SIGSTOP cannot be caught. - F1..F12 reach programs, as the Linux console sends them (PS/2, USB, I2C). - TIOCLINUX subcode 6 answers "no modifiers" instead of echoing the 6 back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
248 lines
6.6 KiB
C
248 lines
6.6 KiB
C
#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);
|
|
}
|