feat: grow syscall
This commit is contained in:
1 parent
8f295c1d20
commit
68fa3aca17
1 file changed
+174
+174
@@ -790,6 +790,180 @@ uint64 syscallDispatch(Registers* regs)
|
|||||||
pitTicks() >= self->pollDeadline;
|
pitTicks() >= self->pollDeadline;
|
||||||
if (ready > 0 || timeoutMs == 0 || expired) {
|
if (ready > 0 || timeoutMs == 0 || expired) {
|
||||||
self->pollActive = false;
|
self->pollActive = false;
|
||||||
|
self->pollDeadline = 0;
|
||||||
|
regs->rax = (uint64)ready;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* None ready yet, timeout not yet reached: park on the one shared
|
||||||
|
channel every poller blocks on (schedPollChannel, sched.h) and
|
||||||
|
rewind rip so the whole syscall re-executes -- the same
|
||||||
|
restart-the-syscall trick sysRead/sysWrite/sysWait already use,
|
||||||
|
except schedule()'s own expiry scan (sched.c) is what guarantees
|
||||||
|
this retry eventually happens even if no fd ever becomes ready. */
|
||||||
|
self->sigInRetryableSyscall = true;
|
||||||
|
schedBlockCurrent(schedPollChannel());
|
||||||
|
regs->rip -= 2;
|
||||||
|
return schedule((uint64)regs);
|
||||||
|
}
|
||||||
|
case sysMkfifo: {
|
||||||
|
char path[256];
|
||||||
|
if (!copyUserPath(regs->rdi, path, sizeof(path)))
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
else
|
||||||
|
regs->rax = (uint64)(int64)vfsMkfifo(path);
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysExec: {
|
||||||
|
char path[256];
|
||||||
|
char argvStorage[elfMaxArgs][execArgMaxLen];
|
||||||
|
const char* argvPtrs[elfMaxArgs];
|
||||||
|
int argc;
|
||||||
|
if (!copyUserPath(regs->rdi, path, sizeof(path)) ||
|
||||||
|
!copyUserArgv(regs->rsi, argvStorage, argvPtrs, &argc) ||
|
||||||
|
!doExec(path, argvPtrs, argc, regs))
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysSpawn: {
|
||||||
|
char path[256];
|
||||||
|
char argvStorage[elfMaxArgs][execArgMaxLen];
|
||||||
|
const char* argvPtrs[elfMaxArgs];
|
||||||
|
int argc;
|
||||||
|
if (!copyUserPath(regs->rdi, path, sizeof(path)) ||
|
||||||
|
!copyUserArgv(regs->rsi, argvStorage, argvPtrs, &argc))
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
else
|
||||||
|
regs->rax = (uint64)doSpawn(path, argvPtrs, argc);
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysWait: {
|
||||||
|
uint32 id = (uint32)regs->rdi;
|
||||||
|
if (id == waitAnyPid) {
|
||||||
|
/* waitpid(-1)-style "wait for ANY child" -- see
|
||||||
|
threadJoinAnyChild's own doc comment (sched.h) for the full
|
||||||
|
contract. regs->rsi is an optional user `long*` pointer
|
||||||
|
(userWaitAny's outPid, ulib.c) that receives which pid
|
||||||
|
actually got reaped; nullptr means the caller does not care.
|
||||||
|
Written as a full 8-byte store below to match `long`'s real
|
||||||
|
width, not truncated to sizeof(uint32) -- a narrower store
|
||||||
|
would leave the user variable's upper 4 bytes as whatever
|
||||||
|
uninitialized garbage the caller's stack already had. */
|
||||||
|
if (regs->rsi != 0 && !userRangeOk(regs->rsi, sizeof(uint64))) {
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
uint32 reapedId;
|
||||||
|
int64 code;
|
||||||
|
WaitScanResult scan = threadJoinAnyChild(currentThreadId(), &reapedId, &code);
|
||||||
|
if (scan == waitScanNoChildren) {
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
if (scan == waitScanNoneDead) {
|
||||||
|
/* Same retry-syscall dance as the specific-pid path below,
|
||||||
|
just parked on our OWN slot instead of a named child's --
|
||||||
|
threadExit/threadKill wake this channel for EVERY dying
|
||||||
|
child of ours, not just one in particular (see
|
||||||
|
threadJoinAnyChild's doc comment), so re-scanning from
|
||||||
|
scratch on each retry is what actually picks up whichever
|
||||||
|
one died. */
|
||||||
|
currentThread()->sigInRetryableSyscall = true;
|
||||||
|
schedBlockCurrent(currentThread());
|
||||||
|
regs->rip -= 2;
|
||||||
|
return schedule((uint64)regs);
|
||||||
|
}
|
||||||
|
if (regs->rsi != 0)
|
||||||
|
*(uint64*)regs->rsi = (uint64)reapedId;
|
||||||
|
regs->rax = (uint64)code;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
if (id == 0 || id >= threadCapacity()) {
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
Thread* target = threadAt(id);
|
||||||
|
if (target->state == threadUnused) {
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
if (target->state != threadDead) {
|
||||||
|
/* Not dead yet: parking here and calling halt() the way
|
||||||
|
threadJoin (sched.c) does for a plain kernel thread body
|
||||||
|
would hang the whole machine, not just this thread -- this
|
||||||
|
handler runs with interrupts disabled (int 0x80 is an
|
||||||
|
interrupt gate, idt.c) on the single shared bootstrap kernel
|
||||||
|
stack (isrStubs.S/tss.c), not the calling thread's own
|
||||||
|
persistent one, so unlike kmain.c's joinerThread it cannot
|
||||||
|
literally halt in place and pick its own C call frame back
|
||||||
|
up later: hlt with IF=0 never wakes, since nothing can ever
|
||||||
|
deliver the timer interrupt that would run schedule() again.
|
||||||
|
Instead: block on the same channel threadExit wakes
|
||||||
|
(schedWakeChannel(&threads[id])), rewind rip by the 2 bytes
|
||||||
|
of the `int $0x80` opcode this trap just came from so it
|
||||||
|
re-executes once this thread is scheduled again, and hand
|
||||||
|
the CPU to schedule() -- the same "flip state, then let the
|
||||||
|
interrupt-return path do the actual waiting" technique
|
||||||
|
sysSleep already uses. The retry re-enters this exact case
|
||||||
|
with the same rax/rdi; since threadExit will have already
|
||||||
|
flipped the target to Dead before waking this thread back
|
||||||
|
up, it takes the branch below next time and completes
|
||||||
|
without blocking again. */
|
||||||
|
currentThread()->sigInRetryableSyscall = true;
|
||||||
|
schedBlockCurrent(target);
|
||||||
|
regs->rip -= 2;
|
||||||
|
return schedule((uint64)regs);
|
||||||
|
}
|
||||||
|
regs->rax = (uint64)threadJoin(id);
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysMmap: {
|
||||||
|
/* addrHint==0 means "kernel picks" (mmapReserve's own first-fit,
|
||||||
|
real mmap(NULL, ...) shape); a nonzero addrHint is taken as an
|
||||||
|
exact placement request, refused (not silently relocated) if it
|
||||||
|
does not fit -- there is no MAP_FIXED bit to distinguish the two,
|
||||||
|
every addrHint here behaves like real mmap's own weaker "hint"
|
||||||
|
contract already narrowed down to "exact or refused". fd/offset
|
||||||
|
are read (and validated below) even when MAP_ANONYMOUS is set,
|
||||||
|
harmlessly ignored by mmapReserve itself in that case, exactly
|
||||||
|
like real mmap(2)'s own documented behavior. */
|
||||||
|
uint64 addrHint = regs->rdi;
|
||||||
|
uint64 length = regs->rsi;
|
||||||
|
uint32 prot = (uint32)regs->rdx;
|
||||||
|
uint32 flags = (uint32)regs->r10;
|
||||||
|
int fd = (int)regs->r8;
|
||||||
|
uint64 offset = regs->r9;
|
||||||
|
bool anonymous = (flags & mmapAnonymous) != 0;
|
||||||
|
|
||||||
|
if (!anonymous) {
|
||||||
|
/* Same three seekable, real-data fd kinds vfsLseek itself
|
||||||
|
already restricts to (vfs.c) -- a pipe/fifo/console/pidfd fd
|
||||||
|
has no fixed byte range a page fault could ever re-read
|
||||||
|
deterministically, so mmap refuses them here rather than
|
||||||
|
letting mmapFaultHandler discover that failure lazily, one
|
||||||
|
page at a time, later. */
|
||||||
|
if (fd < 0 || fd >= maxOpenFiles) {
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
FileDescriptor* fdEntry = ¤tThread()->fds[fd];
|
||||||
|
if (fdEntry->kind != fdFile && fdEntry->kind != fdSfs && fdEntry->kind != fdBlockDev) {
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 mappedAddr;
|
||||||
|
if (!mmapReserve(currentThread(), addrHint, length, prot, anonymous, fd, offset, &mappedAddr)) {
|
||||||
|
regs->rax = (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
regs->rax = mappedAddr;
|
||||||
|
return (uint64)regs;
|
||||||
|
}
|
||||||
|
case sysMunmap:
|
||||||
|
regs->rax = mmapRelease(currentThread(), regs->rdi, regs->rsi) ? 0 : (uint64)-1;
|
||||||
|
return (uint64)regs;
|
||||||
default:
|
default:
|
||||||
regs->rax = (uint64)-1;
|
regs->rax = (uint64)-1;
|
||||||
return (uint64)regs;
|
return (uint64)regs;
|
||||||
|
|||||||
Reference in new issue
Block a user