From fa03edc4a511d918a5500bd41013377820d7dfc5 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 9 Sep 2026 17:32:28 +0200 Subject: [PATCH] feat: extend stopchldtarget with syscall4 and syscall6 --- user/stopchldtarget.c | 68 +++++++++++++++++++++++++++++++++++++++++++ user/ulib.c | 45 ++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 user/stopchldtarget.c diff --git a/user/stopchldtarget.c b/user/stopchldtarget.c new file mode 100644 index 0000000..6fa58d6 --- /dev/null +++ b/user/stopchldtarget.c @@ -0,0 +1,68 @@ +#include "ulib.h" + +/* + * Only ever spawned by stopchldtest.c, with two shared pipe handles as + * argv[1] (the "ready" pipe, this process writes) and argv[2] (the "go" + * pipe, this process reads) -- identical shape to user/jobtarget.c, reused + * here so stopchldtest.c can stop/continue a target genuinely Blocked + * mid-syscall while ALSO watching for the new SIGCHLD-on-stop/ + * SIGCHLD-on-continue notifications (sched.c's threadStop/threadContinue) + * against itself, something jobtest.c/jobtarget.c never needed since + * neither side there installs a sigChld handler. A distinct exit code (66, + * not jobtarget.c's 55) keeps the two pairs' needles/asserts from ever + * being confusable in the serial log. + */ + +int main(int argc, char** argv) +{ + print("stopchldtarget: start, pid="); + printDec(userGetpid()); + print("\n"); + + if (argc != 3) { + print("stopchldtarget: FAIL expected two argv (ready handle, go handle)\n"); + return 1; + } + + long readyHandle = parseDec(argv[1]); + long goHandle = parseDec(argv[2]); + + long goFd = userPipeAttach(goHandle, 0); + if (goFd < 0) { + print("stopchldtarget: FAIL attach go pipe as reader\n"); + return 1; + } + + long readyFd = userPipeAttach(readyHandle, 1); + if (readyFd < 0) { + print("stopchldtarget: FAIL attach ready pipe as writer\n"); + return 1; + } + if (userWrite((int)readyFd, "r", 1) != 1) { + print("stopchldtarget: FAIL write ready byte\n"); + return 1; + } + userClose((int)readyFd); + print("stopchldtarget: ready, blocking on go pipe\n"); + + char goByte = 0; + /* Real blocking read: parks this thread (threadBlocked) until a byte is + actually available. stopchldtest.c raises SIGSTOP against this exact + blocking read, waits for its own SIGCHLD-on-stop notification, writes + the byte, raises SIGCONT, waits for its own SIGCHLD-on-continue + notification, and only then this read can return -- the same + restart-syscall retry contract user/jobtarget.c already relies on. */ + if (userRead((int)goFd, &goByte, 1) != 1) { + print("stopchldtarget: FAIL read go byte\n"); + return 1; + } + userClose((int)goFd); + + if (goByte != 'g') { + print("stopchldtarget: FAIL wrong go byte\n"); + return 1; + } + print("stopchldtarget: received go\n"); + + return 66; +} diff --git a/user/ulib.c b/user/ulib.c index f0ea959..79f0550 100644 --- a/user/ulib.c +++ b/user/ulib.c @@ -11,6 +11,41 @@ static long syscall3(long number, long a1, long a2, long a3) return ret; } +/* Same trap as syscall3, with a 4th argument -- passed in r10, not rcx, + the same choice the real Linux x86-64 syscall ABI makes (rcx is already + spoken for by the `int $0x80`/syscall instruction's own use, so a fourth + argument needs a register the trap does not otherwise touch). Only + userSigAction needs one today. */ +static long syscall4(long number, long a1, long a2, long a3, long a4) +{ + long ret; + register long a4Reg __asm__("r10") = a4; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(number), "D"(a1), "S"(a2), "d"(a3), "r"(a4Reg) + : "memory", "rcx", "r11"); + return ret; +} + +/* Same trap again, with a 5th and 6th argument in r8/r9 -- the same real + Linux x86-64 syscall ABI register order this file's own 4th-argument + choice (r10) already follows, continued one register further. Only + userMmap needs this many today. */ +static long syscall6(long number, long a1, long a2, long a3, long a4, long a5, long a6) +{ + long ret; + register long a4Reg __asm__("r10") = a4; + register long a5Reg __asm__("r8") = a5; + register long a6Reg __asm__("r9") = a6; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(number), "D"(a1), "S"(a2), "d"(a3), "r"(a4Reg), "r"(a5Reg), "r"(a6Reg) + : "memory", "rcx", "r11"); + return ret; +} + long userWrite(int fd, const char* buffer, unsigned long length) { return syscall3(sysWrite, fd, (long)buffer, (long)length); @@ -46,6 +81,16 @@ void userSleep(unsigned long milliseconds) syscall3(sysSleep, (long)milliseconds, 0, 0); } +unsigned long userAlarm(unsigned long seconds) +{ + return (unsigned long)syscall3(sysAlarm, (long)seconds, 0, 0); +} + +long userPoll(PollFd* fds, unsigned long nfds, long timeoutMs) +{ + return syscall3(sysPoll, (long)fds, (long)nfds, timeoutMs); +} + long userOpen(const char* path, unsigned int flags) { return syscall3(sysOpen, (long)path, (long)flags, 0);