#include "types.h" #include "defs.h" #include "param.h" #include "memlayout.h" #include "mmu.h" #include "proc.h" #include "x86.h" #include "syscall.h" #include "posix.h" #include "uaccess.h" #include "errno.h" addr_t sys_fork(void) { return fork(); } addr_t sys_wait(void) { return wait(); } // _exit(status)/exit_group(status): the low 8 bits of the argument are what // wait() reports back through WEXITSTATUS. addr_t sys_exit(void) { int code = 0; argint(0, &code); exit(W_EXITCODE(code, 0)); return 0; } addr_t sys_exit_group(void) { return sys_exit(); } addr_t sys_kill(void) { int pid, sig; argint(0, &pid); argint(1, &sig); // POSIX target selection: // pid > 0 -> that process // pid == 0 -> every process in the caller's own group // pid == -1 -> every process we may signal (approximated as our group) // pid < -1 -> the process group with id -pid if (pid == 0 || pid == -1) return sigsend_pgrp(myproc()->pgid, sig) < 0 ? -ESRCH : 0; if (pid < -1) return sigsend_pgrp(-pid, sig) < 0 ? -ESRCH : 0; // sig == 0: existence check only if (sig == 0) return proc_exists(pid) ? 0 : -ESRCH; // Raise the signal for real: pending bit + wakeup, handler delivery on // the target's next syscall boundary, default action if unhandled. return sigsend(pid, sig) < 0 ? -ESRCH : 0; } addr_t sys_getpid(void) { return myproc()->pid; } addr_t sys_getppid(void) { return myproc()->parent ? myproc()->parent->pid : 1; } addr_t sys_gettid(void) { return myproc()->pid; } // single-threaded: tid == pid addr_t sys_waitpid(void) { int pid, options; addr_t statusp; // status is optional (NULL is standard, valid usage — waitpid(pid, NULL, // WNOHANG) — so this must NOT go through argptr(), which rejects a NULL/ // unmapped pointer outright). if (argint(0, &pid) < 0 || argaddr(1, &statusp) < 0 || argint(2, &options) < 0) return -1; int status; int wpid = waitpid(pid, &status, options); if (wpid > 0 && statusp) put_user(status, statusp); return wpid; } addr_t sys_wait4(void) { int pid, options; addr_t statusp; argint(0, &pid); argaddr(1, &statusp); argint(2, &options); int status; int wpid = waitpid(pid, &status, options); if (wpid <= 0) return wpid; if (statusp) put_user(status, statusp); return wpid; } addr_t sys_clone(void) { int flags; argint(0, &flags); if (flags & (CLONE_VM | CLONE_THREAD | CLONE_SIGHAND)) return -ENOSYS; return fork(); } addr_t sys_sleep(void) { int n; uint ticks0; if (argint(0, &n) < 0) return -1; acquire(&tickslock); ticks0 = ticks; while (ticks - ticks0 < (uint)n) { if (myproc()->killed) { release(&tickslock); return -1; } sleep(&ticks, &tickslock); } release(&tickslock); return 0; } addr_t sys_uptime(void) { uint xticks; acquire(&tickslock); xticks = ticks; release(&tickslock); return xticks; } addr_t sys_reboot(void) { cprintf("System is rebooting...\n"); outb(0xCF9, 0x06); outb(0x64, 0xFE); return 0; } addr_t sys_shutdown(void) { cprintf("System is shutting down...\n"); outw(0x604, 0x2000); outw(0xB004, 0x2000); outw(0x4004, 0x3400); return 0; } // --- Sessions and process groups --------------------------------------- // Job control is defined entirely in terms of these. They used to be stubs // returning the caller's own pid, which made every process look like its own // group leader and left a shell's wait loop unable to make progress. addr_t sys_setsid(void) { return proc_setsid(); } addr_t sys_getsid(void) { int pid; argint(0, &pid); if (pid == 0 || pid == myproc()->pid) return myproc()->sid; return proc_exists(pid) ? (addr_t)myproc()->sid : (addr_t)-ESRCH; } addr_t sys_getpgid(void) { int pid; argint(0, &pid); if (pid == 0 || pid == myproc()->pid) return myproc()->pgid; int g = proc_pgid(pid); return g < 0 ? (addr_t)-ESRCH : (addr_t)g; } addr_t sys_setpgid(void) { int pid, pgid; argint(0, &pid); argint(1, &pgid); return proc_setpgid(pid, pgid); } addr_t sys_getpgrp(void) { return myproc()->pgid; } // --- User and group identity ------------------------------------------- // Tracked for real now. Nothing *enforces* permissions with these yet -- the // on-disk format has no owner field until SchFS -- but they are no longer // invented, so getuid() after setuid() reports what actually happened. addr_t sys_getuid(void) { return myproc()->uid; } addr_t sys_geteuid(void) { return myproc()->euid; } addr_t sys_getgid(void) { return myproc()->gid; } addr_t sys_getegid(void) { return myproc()->egid; } // Privileged (euid 0) callers may set any id; an unprivileged caller may only // switch between its real and saved ids. Standard POSIX rule. addr_t sys_setuid(void) { int uid; argint(0, &uid); if (uid < 0) return -EINVAL; if (myproc()->euid == 0) { myproc()->uid = myproc()->euid = myproc()->suid = (uint)uid; return 0; } if ((uint)uid != myproc()->uid && (uint)uid != myproc()->suid) return -EPERM; myproc()->euid = (uint)uid; return 0; } addr_t sys_setgid(void) { int gid; argint(0, &gid); if (gid < 0) return -EINVAL; if (myproc()->euid == 0) { myproc()->gid = myproc()->egid = myproc()->sgid = (uint)gid; return 0; } if ((uint)gid != myproc()->gid && (uint)gid != myproc()->sgid) return -EPERM; myproc()->egid = (uint)gid; return 0; } addr_t sys_umask(void) { return 022; } addr_t sys_setreuid(void) { int ruid, euid; argint(0, &ruid); argint(1, &euid); if (myproc()->euid != 0) return -EPERM; if (ruid >= 0) myproc()->uid = (uint)ruid; if (euid >= 0) myproc()->euid = (uint)euid; myproc()->suid = myproc()->euid; return 0; } addr_t sys_setregid(void) { int rgid, egid; argint(0, &rgid); argint(1, &egid); if (myproc()->euid != 0) return -EPERM; if (rgid >= 0) myproc()->gid = (uint)rgid; if (egid >= 0) myproc()->egid = (uint)egid; myproc()->sgid = myproc()->egid; return 0; } addr_t sys_setresuid(void) { int r, e, sv; argint(0, &r); argint(1, &e); argint(2, &sv); if (myproc()->euid != 0) return -EPERM; if (r >= 0) myproc()->uid = (uint)r; if (e >= 0) myproc()->euid = (uint)e; if (sv >= 0) myproc()->suid = (uint)sv; return 0; } addr_t sys_setresgid(void) { int r, e, sv; argint(0, &r); argint(1, &e); argint(2, &sv); if (myproc()->euid != 0) return -EPERM; if (r >= 0) myproc()->gid = (uint)r; if (e >= 0) myproc()->egid = (uint)e; if (sv >= 0) myproc()->sgid = (uint)sv; return 0; } // Linux-specific; the fs id follows the effective id here. addr_t sys_setfsuid(void) { return myproc()->euid; } addr_t sys_setfsgid(void) { return myproc()->egid; } addr_t sys_getresuid(void) { addr_t rp, ep, sp; argaddr(0, &rp); argaddr(1, &ep); argaddr(2, &sp); if (rp) put_user(myproc()->uid, rp); if (ep) put_user(myproc()->euid, ep); if (sp) put_user(myproc()->suid, sp); return 0; } addr_t sys_getresgid(void) { addr_t rp, ep, sp; argaddr(0, &rp); argaddr(1, &ep); argaddr(2, &sp); if (rp) put_user(myproc()->gid, rp); if (ep) put_user(myproc()->egid, ep); if (sp) put_user(myproc()->sgid, sp); return 0; } addr_t sys_getgroups(void) { int size; argint(0, &size); return 0; // no supplementary groups } addr_t sys_setgroups(void) { return 0; } // --- Process/scheduling extensions --- addr_t sys_sched_yield(void) { return 0; } addr_t sys_sched_getaffinity(void) { int pid, cpusetsize; addr_t maskp; argint(0, &pid); argint(1, &cpusetsize); argaddr(2, &maskp); if (maskp && cpusetsize > 0) { char *m = (char*)user_ptr(maskp, (uint64)cpusetsize); if (m) { memset(m, 0, cpusetsize); m[0] = 1; } // CPU 0 only } return 0; } addr_t sys_sched_setaffinity(void) { return 0; } addr_t sys_prctl(void) { int op; addr_t arg2; argint(0, &op); argaddr(1, &arg2); if (op == PR_SET_NAME) { char *name; if (argstr(1, &name) < 0) return -1; safestrcpy(myproc()->name, name, sizeof(myproc()->name)); return 0; } if (op == PR_GET_NAME) { char *buf = (char*)user_ptr(arg2, 16); if (!buf) return -EFAULT; safestrcpy(buf, myproc()->name, 16); return 0; } return 0; // all other prctl ops: silently succeed } addr_t sys_tgkill(void) { int tgid, tid, sig; argint(0, &tgid); argint(1, &tid); argint(2, &sig); // In our single-threaded model tgid == tid == pid. if (tgid <= 0) return 0; if (sig == 0) return proc_exists(tgid) ? 0 : -ESRCH; return sigsend(tgid, sig) < 0 ? -ESRCH : 0; } addr_t sys_tkill(void) { int tid, sig; argint(0, &tid); argint(1, &sig); if (tid <= 0) return 0; if (sig == 0) return proc_exists(tid) ? 0 : -ESRCH; return sigsend(tid, sig) < 0 ? -ESRCH : 0; } addr_t sys_pause(void) { // Sleep until next timer tick and return -EINTR (no real signal delivery). acquire(&tickslock); uint t0 = ticks; while (ticks == t0 && !myproc()->killed) sleep(&ticks, &tickslock); release(&tickslock); return -EINTR; } addr_t sys_getrusage(void) { int who; struct rusage *ru; argint(0, &who); if (argptr(1, (char**)&ru, sizeof(*ru)) < 0) return -1; memset(ru, 0, sizeof(*ru)); acquire(&tickslock); uint xt = ticks; release(&tickslock); ru->ru_utime.tv_sec = (long)(xt / 100); ru->ru_utime.tv_usec = (long)((xt % 100) * 10000); return 0; } addr_t sys_times(void) { addr_t bufaddr; argaddr(0, &bufaddr); if (bufaddr) { struct tms *t = (struct tms*)user_ptr(bufaddr, sizeof(*t)); if (t) memset(t, 0, sizeof(*t)); } acquire(&tickslock); uint xt = ticks; release(&tickslock); return (addr_t)xt; // return clock ticks since boot } addr_t sys_sysinfo(void) { addr_t bufaddr; argaddr(0, &bufaddr); struct sysinfo *si = (struct sysinfo*)user_ptr(bufaddr, sizeof(*si)); if (!si) return -EFAULT; memset(si, 0, sizeof(*si)); acquire(&tickslock); uint xt = ticks; release(&tickslock); si->uptime = (long)(xt / 100); // Real values: PHYSTOP is the top of usable physical memory and the page // allocator knows exactly how much of it is still on the free list. These // used to be the constants 256MB/128MB, invented and always wrong. si->totalram = (uint64)PHYSTOP; si->freeram = kalloc_free_bytes(); si->mem_unit = 1; si->procs = (uint16)proc_num(); return 0; } addr_t sys_waitid(void) { // Simplified: wait for any child, ignore idtype/id/options. int wpid = wait(); if (wpid < 0) return -ECHILD; // siginfo_t pointer (arg 2) — zero-fill if provided. addr_t infop; argaddr(2, &infop); if (infop) { char *p = (char*)user_ptr(infop, 128); // sizeof(siginfo_t) if (p) memset(p, 0, 128); } return 0; }