feat: build out syscall
This commit is contained in:
1 parent
aad9d4ece8
commit
8f295c1d20
1 file changed
+173
+173
@@ -617,6 +617,179 @@ uint64 syscallDispatch(Registers* regs)
|
|||||||
int64 rawPid = (int64)regs->rdi;
|
int64 rawPid = (int64)regs->rdi;
|
||||||
bool ok;
|
bool ok;
|
||||||
if (rawPid > 0)
|
if (rawPid > 0)
|
||||||
|
ok = threadKill((uint32)rawPid);
|
||||||
|
else
|
||||||
|
ok = threadKillGroup(rawPid == 0 ? currentThread()->pgid : (uint32)(-rawPid));
|
||||||
|
regs->rax = ok ? 0 : (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysSigSend: {
|
||||||
|
/* Same pid>0/pid==0/pid<0 convention as sysKill just above, this
|
||||||
|
time through sigRaiseGroup (signal.h) for the two group cases --
|
||||||
|
see that function's own doc comment for why every per-target
|
||||||
|
special case (SIGKILL/SIGSTOP/SIGCONT bypasses, early-wake,
|
||||||
|
SigInfo) still applies exactly as it already does for a plain,
|
||||||
|
single-target send. */
|
||||||
|
int64 rawPid = (int64)regs->rdi;
|
||||||
|
int signum = (int)regs->rsi;
|
||||||
|
uint32 senderPid = currentThreadId();
|
||||||
|
bool isGroup = (rawPid <= 0);
|
||||||
|
uint32 targetPgid = isGroup ? (rawPid == 0 ? currentThread()->pgid : (uint32)(-rawPid)) : 0;
|
||||||
|
|
||||||
|
/* siCodeUser/senderPid: a real userSigSend always has a genuine
|
||||||
|
sending process, unlike sched.c's own kernel-internal raises
|
||||||
|
(SIGALRM/SIGCHLD) or keyboard.c's terminal-generated ones --
|
||||||
|
exactly real Unix's own SI_USER, si_pid the caller's own pid,
|
||||||
|
exactly like real kill(2). */
|
||||||
|
bool ok = isGroup
|
||||||
|
? sigRaiseGroup(targetPgid, signum, siCodeUser, (int)senderPid)
|
||||||
|
: sigRaiseInfo((uint32)rawPid, signum, siCodeUser, (int)senderPid, 0);
|
||||||
|
regs->rax = ok ? 0 : (uint64)-1;
|
||||||
|
|
||||||
|
/* Every OTHER outcome of sigRaise/sigRaiseGroup either changes
|
||||||
|
nothing about the CALLING thread's own state (a signal aimed at
|
||||||
|
someone else) or, for an ordinary catchable signal aimed at
|
||||||
|
self, is already caught by isrDispatch's unconditional
|
||||||
|
deliverPendingSignals call right after this handler returns
|
||||||
|
(see isr.c) -- self-signaling sigUsr1 and having its handler
|
||||||
|
fire before ever reaching userspace again already works today
|
||||||
|
without any special case here. SIGSTOP against the caller's own
|
||||||
|
pid (or a group that includes it) is the one exception:
|
||||||
|
threadStop (called from sigRaise above, once per group member
|
||||||
|
for the group case) already flipped this very thread's state
|
||||||
|
away from Running, but sigRaise/sysSigSend never touch
|
||||||
|
sigPending for it (see signal.h), so deliverPendingSignals'
|
||||||
|
pending-bit scan would find nothing and simply hand `regs`
|
||||||
|
straight back -- returning to ring 3 and continuing to run a
|
||||||
|
thread the scheduler no longer considers Runnable, until the
|
||||||
|
next timer tick eventually notices. Explicit reschedule here
|
||||||
|
closes that gap, the same "flip state, then let schedule() do
|
||||||
|
the actual waiting" pattern sysSleep/sysExit already use for
|
||||||
|
their own self-directed state changes. The group case checks
|
||||||
|
targetPgid against the caller's OWN pgid rather than an exact id
|
||||||
|
match, since the caller itself -- not just some other member --
|
||||||
|
is who this reschedule is actually about. */
|
||||||
|
bool selfTargeted = isGroup ? (targetPgid == currentThread()->pgid) : ((uint32)rawPid == senderPid);
|
||||||
|
if (ok && signum == sigStop && selfTargeted)
|
||||||
|
return schedule((uint64)regs);
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysSigAction:
|
||||||
|
regs->rax = sigSetHandler(currentThread(), (int)regs->rdi, regs->rsi, regs->rdx, regs->r10);
|
||||||
|
return (uint64)regs;
|
||||||
|
case sysSigProcMask:
|
||||||
|
regs->rax = sigProcMask(currentThread(), (int)regs->rdi, regs->rsi);
|
||||||
|
return (uint64)regs;
|
||||||
|
case sysSigReturn:
|
||||||
|
sigOnSigReturn(currentThread(), regs);
|
||||||
|
return (uint64)regs;
|
||||||
|
case sysAlarm:
|
||||||
|
regs->rax = threadSetAlarm(regs->rdi);
|
||||||
|
return (uint64)regs;
|
||||||
|
case sysSetpgid: {
|
||||||
|
/* Real setpgid(2)'s own "0 means self"/"0 means use pid itself as
|
||||||
|
the new group id" resolution -- threadSetPgid (sched.h) takes an
|
||||||
|
already-fully-resolved value, exactly like threadSetBrk/
|
||||||
|
threadSetAlarm above do for THEIR raw syscall arguments. */
|
||||||
|
uint32 pid = (regs->rdi == 0) ? currentThreadId() : (uint32)regs->rdi;
|
||||||
|
uint32 pgid = (regs->rsi == 0) ? pid : (uint32)regs->rsi;
|
||||||
|
regs->rax = threadSetPgid(pid, pgid) ? 0 : (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysGetpgid: {
|
||||||
|
uint32 pid = (regs->rdi == 0) ? currentThreadId() : (uint32)regs->rdi;
|
||||||
|
uint32 pgid = threadPgid(pid);
|
||||||
|
regs->rax = (pgid == threadInvalidId) ? (uint64)-1 : (uint64)pgid;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysNice: {
|
||||||
|
/* Real setpriority(PRIO_PROCESS, ...)'s own "0 means the caller
|
||||||
|
itself" convention, same resolution shape as sysSetpgid's `pid`
|
||||||
|
argument just above -- rdi/pid, rsi/weight, straight through to
|
||||||
|
threadSetPriority (sched.h), which now does the SAME id==0/
|
||||||
|
out-of-range/Dead-or-Unused rejection threadSetPgid already did
|
||||||
|
(see that function's own updated doc comment for why it had to
|
||||||
|
catch up once this syscall made it reachable from ring 3). */
|
||||||
|
uint32 pid = (regs->rdi == 0) ? currentThreadId() : (uint32)regs->rdi;
|
||||||
|
uint32 weight = (uint32)regs->rsi;
|
||||||
|
regs->rax = threadSetPriority(pid, weight) ? 0 : (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysGetPriority: {
|
||||||
|
uint32 pid = (regs->rdi == 0) ? currentThreadId() : (uint32)regs->rdi;
|
||||||
|
uint32 weight = threadPriority(pid);
|
||||||
|
regs->rax = (weight == 0) ? (uint64)-1 : (uint64)weight;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysTime:
|
||||||
|
/* Real time(2)'s job: seconds since the Unix epoch, off the real
|
||||||
|
CMOS RTC (rtc.c) -- the first syscall in this kernel answering
|
||||||
|
"what day is it" instead of "how long since boot" (sysSleep,
|
||||||
|
userAlarm). No arguments, nothing to validate. */
|
||||||
|
regs->rax = (uint64)rtcNow();
|
||||||
|
return (uint64)regs;
|
||||||
|
case sysSetFgPgrp:
|
||||||
|
/* Real tcsetpgrp(3)'s job -- names which process group keyboard.c's
|
||||||
|
real Ctrl-C/Ctrl-Z (SIGINT/SIGTSTP) is scoped to next. No
|
||||||
|
permission check at all (this kernel has no notion of "the
|
||||||
|
controlling terminal of MY session" to check ownership against,
|
||||||
|
the same honest, documented omission threadSetPgid's own doc
|
||||||
|
comment already gives for setpgid's session check) -- any
|
||||||
|
process can claim the foreground for any pgid, including one it
|
||||||
|
is not even a member of. */
|
||||||
|
keyboardSetForegroundPgid((uint32)regs->rdi);
|
||||||
|
regs->rax = 0;
|
||||||
|
return (uint64)regs;
|
||||||
|
case sysGetFgPgrp:
|
||||||
|
regs->rax = (uint64)keyboardForegroundPgid();
|
||||||
|
return (uint64)regs;
|
||||||
|
case sysPoll: {
|
||||||
|
/* Real poll(2): rdi/PollFd array, rsi/nfds, rdx/timeoutMs (signed,
|
||||||
|
-1 = wait forever, 0 = report current readiness without ever
|
||||||
|
blocking, like every real poll(2) implementation). Flat address
|
||||||
|
space (this kernel's honest fork()-less design, see doSpawn's own
|
||||||
|
comment) means the array can be read and written in place, the
|
||||||
|
same way sysStat writes straight into its caller's FbStat*
|
||||||
|
without a separate copy-out step. */
|
||||||
|
uint64 nfds = regs->rsi;
|
||||||
|
int64 timeoutMs = (int64)regs->rdx;
|
||||||
|
if (nfds > maxPollFds || !userRangeOk(regs->rdi, nfds * sizeof(PollFd))) {
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
PollFd* userFds = (PollFd*)regs->rdi;
|
||||||
|
Thread* self = currentThread();
|
||||||
|
|
||||||
|
/* First entry into this call (not a retry re-executing after a
|
||||||
|
park below) picks the deadline once and remembers it in
|
||||||
|
pollDeadline -- a later retry must NOT recompute it from the
|
||||||
|
still-unchanged rdx, or a poll that keeps almost-but-not-quite
|
||||||
|
finding a ready fd could have its effective timeout pushed out
|
||||||
|
indefinitely instead of counting down real elapsed time. */
|
||||||
|
if (!self->pollActive && timeoutMs != 0) {
|
||||||
|
self->pollActive = true;
|
||||||
|
if (timeoutMs < 0) {
|
||||||
|
self->pollDeadline = 0;
|
||||||
|
} else {
|
||||||
|
uint64 ticks = ((uint64)timeoutMs * (uint64)pitFrequency()) / 1000;
|
||||||
|
if (ticks == 0)
|
||||||
|
ticks = 1;
|
||||||
|
self->pollDeadline = pitTicks() + ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64 ready = 0;
|
||||||
|
for (uint64 i = 0; i < nfds; i++) {
|
||||||
|
int16 revents = 0;
|
||||||
|
if (vfsPollReady(userFds[i].fd, userFds[i].events, &revents))
|
||||||
|
ready++;
|
||||||
|
userFds[i].revents = revents;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool expired = self->pollActive && self->pollDeadline != 0 &&
|
||||||
|
pitTicks() >= self->pollDeadline;
|
||||||
|
if (ready > 0 || timeoutMs == 0 || expired) {
|
||||||
|
self->pollActive = false;
|
||||||
default:
|
default:
|
||||||
regs->rax = (uint64)-1;
|
regs->rax = (uint64)-1;
|
||||||
return (uint64)regs;
|
return (uint64)regs;
|
||||||
|
|||||||
Reference in new issue
Block a user