From: Tulio A M Mendes Date: Mon, 16 Feb 2026 01:49:24 +0000 (-0300) Subject: feat: expand test battery from 44 to 80 smoke tests X-Git-Url: https://projects.tadryanom.me/docs/static/gitweb.css?a=commitdiff_plain;h=182ef82b8082e42fc43c1e493e69495fe9606663;p=AdrOS.git feat: expand test battery from 44 to 80 smoke tests New init.elf tests (D1-D15): - nanosleep (50ms sleep + monotonic clock verification) - CLOCK_REALTIME (nonzero epoch timestamp) - /dev/urandom read - /proc/cmdline read - CoW fork (child write doesn't corrupt parent) - readv/writev (scatter-gather I/O on pipe) - fsync (write + sync on diskfs) - truncate (path-based length reduction) - getuid/getgid/geteuid/getegid (identity consistency) - chmod (mode change on diskfs file) - flock (LOCK_EX + LOCK_UN) - times (process accounting) - gettid (== getpid for main thread) - posix_spawn (fork+exec echo.elf) - clock_ns precision (TSC sub-10ms resolution) Newly tracked existing tests (~15): - pipe rw, ioctl tty, job control, poll /dev/null, pty, setsid/setpgid, sigaction SIGUSR1, sigreturn, tmpfs/mount, /dev/null, isatty, O_NONBLOCK, pipe2/dup3, chdir/getcwd, *at syscalls, rename/rmdir, getdents multi-fs, getppid, waitpid WNOHANG, SIGSEGV handler, waitpid 100 children, echo.elf execve Smoke timeout raised from 90s to 120s. 80/80 smoke tests pass, cppcheck clean. --- diff --git a/Makefile b/Makefile index 64c2512..f018260 100644 --- a/Makefile +++ b/Makefile @@ -269,7 +269,7 @@ check: cppcheck sparse # ---- Automated Smoke Test (QEMU + expect) ---- SMOKE_SMP ?= 4 -SMOKE_TIMEOUT ?= 90 +SMOKE_TIMEOUT ?= 120 test: iso @echo "[TEST] Running smoke test (SMP=$(SMOKE_SMP), timeout=$(SMOKE_TIMEOUT)s)..." diff --git a/tests/smoke_test.exp b/tests/smoke_test.exp index 0b0e3c2..2be35e9 100755 --- a/tests/smoke_test.exp +++ b/tests/smoke_test.exp @@ -15,7 +15,7 @@ set smp [lindex $argv 0] if {$smp eq ""} { set smp 4 } set timeout_sec [lindex $argv 1] -if {$timeout_sec eq ""} { set timeout_sec 30 } +if {$timeout_sec eq ""} { set timeout_sec 60 } set iso "adros-x86.iso" set disk "disk.img" @@ -57,12 +57,27 @@ set tests { {"kill SIGKILL" "\\[init\\] kill\\(SIGKILL\\) OK"} {"poll pipe" "\\[init\\] poll\\(pipe\\) OK"} {"select pipe" "\\[init\\] select\\(pipe\\) OK"} + {"ioctl tty" "\\[init\\] ioctl\\(/dev/tty\\) OK"} + {"job control" "\\[init\\] job control \\(SIGTTIN/SIGTTOU\\) OK"} + {"poll /dev/null" "\\[init\\] poll\\(/dev/null\\) OK"} + {"pty bidirectional" "\\[init\\] pty OK"} + {"setsid/setpgid" "\\[init\\] setsid/setpgid/getpgrp OK"} + {"sigaction SIGUSR1" "\\[init\\] sigaction/kill\\(SIGUSR1\\) OK"} + {"sigreturn" "\\[init\\] sigreturn OK"} + {"tmpfs/mount" "\\[init\\] tmpfs/mount OK"} + {"dev null" "\\[init\\] /dev/null OK"} {"persist counter" "\\[init\\] /persist/counter="} {"dev tty write" "\\[init\\] /dev/tty write OK"} {"diskfs test" "\\[init\\] /disk/test prev="} {"diskfs mkdir/unlink" "\\[init\\] diskfs mkdir/unlink OK"} {"diskfs getdents" "\\[init\\] diskfs getdents OK"} - {"PING network" "\\[PING\\] .*received.*network OK"} + {"isatty" "\\[init\\] isatty OK"} + {"O_NONBLOCK" "\\[init\\] O_NONBLOCK OK"} + {"pipe2/dup3" "\\[init\\] pipe2/dup3 OK"} + {"chdir/getcwd" "\\[init\\] chdir/getcwd OK"} + {"*at syscalls" "\\[init\\] \\*at OK"} + {"rename/rmdir" "\\[init\\] rename/rmdir OK"} + {"getdents multi-fs" "\\[init\\] getdents multi-fs OK"} {"brk heap" "\\[init\\] brk OK"} {"mmap/munmap" "\\[init\\] mmap/munmap OK"} {"clock_gettime" "\\[init\\] clock_gettime OK"} @@ -87,6 +102,27 @@ set tests { {"epoll" "\\[init\\] epoll OK"} {"inotify" "\\[init\\] inotify OK"} {"aio" "\\[init\\] aio OK"} + {"nanosleep" "\\[init\\] nanosleep OK"} + {"CLOCK_REALTIME" "\\[init\\] CLOCK_REALTIME OK"} + {"dev urandom" "\\[init\\] /dev/urandom OK"} + {"proc cmdline" "\\[init\\] /proc/cmdline OK"} + {"CoW fork" "\\[init\\] CoW fork OK"} + {"readv/writev" "\\[init\\] readv/writev OK"} + {"fsync" "\\[init\\] fsync OK"} + {"truncate path" "\\[init\\] truncate OK"} + {"getuid/getgid" "\\[init\\] getuid/getgid OK"} + {"chmod" "\\[init\\] chmod OK"} + {"flock" "\\[init\\] flock OK"} + {"times" "\\[init\\] times OK"} + {"gettid" "\\[init\\] gettid OK"} + {"posix_spawn" "\\[init\\] posix_spawn OK"} + {"clock_ns precision" "\\[init\\] clock_ns precision OK"} + {"getppid" "\\[init\\] getppid OK"} + {"waitpid WNOHANG" "\\[init\\] waitpid WNOHANG OK"} + {"SIGSEGV handler" "\\[init\\] SIGSEGV OK"} + {"waitpid 100 children" "\\[init\\] waitpid OK \\(100 children"} + {"PING network" "\\[PING\\] .*received.*network OK"} + {"echo.elf execve" "\\[echo\\] hello from echo.elf"} } # ---- Poll serial.log for results ---- diff --git a/user/init.c b/user/init.c index 0a82dd4..6f5fa59 100644 --- a/user/init.c +++ b/user/init.c @@ -120,6 +120,24 @@ enum { SYSCALL_AIO_WRITE = 122, SYSCALL_AIO_ERROR = 123, SYSCALL_AIO_RETURN = 124, + + SYSCALL_CHMOD = 50, + SYSCALL_CHOWN = 51, + SYSCALL_GETUID = 52, + SYSCALL_GETGID = 53, + SYSCALL_CLONE = 67, + SYSCALL_GETTID = 68, + SYSCALL_FSYNC = 69, + SYSCALL_READV = 81, + SYSCALL_WRITEV = 82, + SYSCALL_TIMES = 84, + SYSCALL_FUTEX = 85, + SYSCALL_FLOCK = 87, + SYSCALL_GETEUID = 88, + SYSCALL_GETEGID = 89, + SYSCALL_SIGSUSPEND = 80, + SYSCALL_SIGQUEUE = 95, + SYSCALL_POSIX_SPAWN = 96, }; enum { @@ -1118,6 +1136,141 @@ static int sys_aio_return(struct aiocb* cb) { return ret; } +static int sys_nanosleep(const struct timespec* req, struct timespec* rem) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_NANOSLEEP), "b"(req), "c"(rem) + : "memory" + ); + return __syscall_fix(ret); +} + +static uint32_t sys_getuid(void) { + uint32_t ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETUID) : "memory"); + return ret; +} + +static uint32_t sys_getgid(void) { + uint32_t ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETGID) : "memory"); + return ret; +} + +static uint32_t sys_geteuid(void) { + uint32_t ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETEUID) : "memory"); + return ret; +} + +static uint32_t sys_getegid(void) { + uint32_t ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETEGID) : "memory"); + return ret; +} + +static int sys_gettid(void) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETTID) : "memory"); + return ret; +} + +static int sys_fsync(int fd) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_FSYNC), "b"(fd) + : "memory" + ); + return __syscall_fix(ret); +} + +static int sys_truncate(const char* path, uint32_t length) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_TRUNCATE), "b"(path), "c"(length) + : "memory" + ); + return __syscall_fix(ret); +} + +static int sys_chmod(const char* path, uint32_t mode) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_CHMOD), "b"(path), "c"(mode) + : "memory" + ); + return __syscall_fix(ret); +} + +static int sys_flock(int fd, int operation) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_FLOCK), "b"(fd), "c"(operation) + : "memory" + ); + return __syscall_fix(ret); +} + +struct iovec { + void* iov_base; + uint32_t iov_len; +}; + +static int sys_writev(int fd, const struct iovec* iov, int iovcnt) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_WRITEV), "b"(fd), "c"(iov), "d"(iovcnt) + : "memory" + ); + return __syscall_fix(ret); +} + +static int sys_readv(int fd, const struct iovec* iov, int iovcnt) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_READV), "b"(fd), "c"(iov), "d"(iovcnt) + : "memory" + ); + return __syscall_fix(ret); +} + +static uint32_t sys_times(void* buf) { + uint32_t ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_TIMES), "b"(buf) + : "memory" + ); + return ret; +} + +static int sys_posix_spawn(uint32_t* pid_out, const char* path, + const char* const* argv, const char* const* envp) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_POSIX_SPAWN), "b"(pid_out), "c"(path), "d"(argv), "S"(envp) + : "memory" + ); + return __syscall_fix(ret); +} + __attribute__((noreturn)) static void sys_exit(int code) { __asm__ volatile( "int $0x80\n" @@ -3181,6 +3334,279 @@ void _start(void) { sys_write(1, "[init] aio OK\n", (uint32_t)(sizeof("[init] aio OK\n") - 1)); } + // D1: nanosleep + { + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = 50000000; /* 50ms */ + struct timespec ts1, ts2; + (void)sys_clock_gettime(CLOCK_MONOTONIC, &ts1); + int r = sys_nanosleep(&req, 0); + (void)sys_clock_gettime(CLOCK_MONOTONIC, &ts2); + if (r < 0) { + sys_write(1, "[init] nanosleep failed\n", (uint32_t)(sizeof("[init] nanosleep failed\n") - 1)); + sys_exit(1); + } + uint32_t elapsed_ms = (ts2.tv_sec - ts1.tv_sec) * 1000 + + (ts2.tv_nsec / 1000000) - (ts1.tv_nsec / 1000000); + if (elapsed_ms < 10) { + sys_write(1, "[init] nanosleep too short\n", (uint32_t)(sizeof("[init] nanosleep too short\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] nanosleep OK\n", (uint32_t)(sizeof("[init] nanosleep OK\n") - 1)); + } + + // D2: CLOCK_REALTIME (should return nonzero epoch timestamp) + { + struct timespec rt; + if (sys_clock_gettime(CLOCK_REALTIME, &rt) < 0) { + sys_write(1, "[init] CLOCK_REALTIME failed\n", (uint32_t)(sizeof("[init] CLOCK_REALTIME failed\n") - 1)); + sys_exit(1); + } + if (rt.tv_sec == 0) { + sys_write(1, "[init] CLOCK_REALTIME sec=0\n", (uint32_t)(sizeof("[init] CLOCK_REALTIME sec=0\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] CLOCK_REALTIME OK\n", (uint32_t)(sizeof("[init] CLOCK_REALTIME OK\n") - 1)); + } + + // D3: /dev/urandom read + { + int fd = sys_open("/dev/urandom", 0); + if (fd < 0) { + sys_write(1, "[init] /dev/urandom open failed\n", (uint32_t)(sizeof("[init] /dev/urandom open failed\n") - 1)); + sys_exit(1); + } + uint8_t ubuf[4]; + int r = sys_read(fd, ubuf, 4); + (void)sys_close(fd); + if (r != 4) { + sys_write(1, "[init] /dev/urandom read failed\n", (uint32_t)(sizeof("[init] /dev/urandom read failed\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] /dev/urandom OK\n", (uint32_t)(sizeof("[init] /dev/urandom OK\n") - 1)); + } + + // D4: /proc/cmdline read + { + int fd = sys_open("/proc/cmdline", 0); + if (fd < 0) { + sys_write(1, "[init] /proc/cmdline open failed\n", (uint32_t)(sizeof("[init] /proc/cmdline open failed\n") - 1)); + sys_exit(1); + } + char cbuf[64]; + int r = sys_read(fd, cbuf, 63); + (void)sys_close(fd); + if (r <= 0) { + sys_write(1, "[init] /proc/cmdline read failed\n", (uint32_t)(sizeof("[init] /proc/cmdline read failed\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] /proc/cmdline OK\n", (uint32_t)(sizeof("[init] /proc/cmdline OK\n") - 1)); + } + + // D5: CoW fork (child writes to page, parent sees original) + { + volatile uint32_t cow_val = 0xAAAAAAAAU; + int pid = sys_fork(); + if (pid < 0) { + sys_write(1, "[init] CoW fork failed\n", (uint32_t)(sizeof("[init] CoW fork failed\n") - 1)); + sys_exit(1); + } + if (pid == 0) { + cow_val = 0xBBBBBBBBU; + if (cow_val != 0xBBBBBBBBU) sys_exit(1); + sys_exit(0); + } + int st = 0; + (void)sys_waitpid(pid, &st, 0); + if (st != 0 || cow_val != 0xAAAAAAAAU) { + sys_write(1, "[init] CoW fork data corrupted\n", (uint32_t)(sizeof("[init] CoW fork data corrupted\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] CoW fork OK\n", (uint32_t)(sizeof("[init] CoW fork OK\n") - 1)); + } + + // D6: readv/writev + { + int fds[2]; + if (sys_pipe(fds) < 0) { + sys_write(1, "[init] readv/writev pipe failed\n", (uint32_t)(sizeof("[init] readv/writev pipe failed\n") - 1)); + sys_exit(1); + } + char a[] = "HE"; + char b[] = "LLO"; + struct iovec wv[2]; + wv[0].iov_base = a; + wv[0].iov_len = 2; + wv[1].iov_base = b; + wv[1].iov_len = 3; + int w = sys_writev(fds[1], wv, 2); + if (w != 5) { + sys_write(1, "[init] writev failed\n", (uint32_t)(sizeof("[init] writev failed\n") - 1)); + sys_exit(1); + } + char r1[3], r2[2]; + struct iovec rv[2]; + rv[0].iov_base = r1; + rv[0].iov_len = 3; + rv[1].iov_base = r2; + rv[1].iov_len = 2; + int r = sys_readv(fds[0], rv, 2); + (void)sys_close(fds[0]); + (void)sys_close(fds[1]); + if (r != 5 || r1[0] != 'H' || r1[1] != 'E' || r1[2] != 'L' || r2[0] != 'L' || r2[1] != 'O') { + sys_write(1, "[init] readv data bad\n", (uint32_t)(sizeof("[init] readv data bad\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] readv/writev OK\n", (uint32_t)(sizeof("[init] readv/writev OK\n") - 1)); + } + + // D7: fsync + { + int fd = sys_open("/disk/fsynctest", O_CREAT | O_TRUNC); + if (fd < 0) { + sys_write(1, "[init] fsync open failed\n", (uint32_t)(sizeof("[init] fsync open failed\n") - 1)); + sys_exit(1); + } + (void)sys_write(fd, "FS", 2); + if (sys_fsync(fd) < 0) { + sys_write(1, "[init] fsync failed\n", (uint32_t)(sizeof("[init] fsync failed\n") - 1)); + sys_exit(1); + } + (void)sys_close(fd); + (void)sys_unlink("/disk/fsynctest"); + sys_write(1, "[init] fsync OK\n", (uint32_t)(sizeof("[init] fsync OK\n") - 1)); + } + + // D8: truncate (path-based) + { + int fd = sys_open("/disk/truncpath", O_CREAT | O_TRUNC); + if (fd < 0) { + sys_write(1, "[init] truncate open failed\n", (uint32_t)(sizeof("[init] truncate open failed\n") - 1)); + sys_exit(1); + } + (void)sys_write(fd, "1234567890", 10); + (void)sys_close(fd); + int r = sys_truncate("/disk/truncpath", 3); + (void)sys_unlink("/disk/truncpath"); + if (r < 0) { + sys_write(1, "[init] truncate failed\n", (uint32_t)(sizeof("[init] truncate failed\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] truncate OK\n", (uint32_t)(sizeof("[init] truncate OK\n") - 1)); + } + + // D9: getuid/getgid/geteuid/getegid + { + uint32_t uid = sys_getuid(); + uint32_t gid = sys_getgid(); + uint32_t euid = sys_geteuid(); + uint32_t egid = sys_getegid(); + if (uid != euid || gid != egid) { + sys_write(1, "[init] uid/euid mismatch\n", (uint32_t)(sizeof("[init] uid/euid mismatch\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] getuid/getgid OK\n", (uint32_t)(sizeof("[init] getuid/getgid OK\n") - 1)); + } + + // D10: chmod + { + int fd = sys_open("/disk/chmodtest", O_CREAT | O_TRUNC); + if (fd >= 0) { + (void)sys_close(fd); + int r = sys_chmod("/disk/chmodtest", 0755); + (void)sys_unlink("/disk/chmodtest"); + if (r < 0) { + sys_write(1, "[init] chmod failed\n", (uint32_t)(sizeof("[init] chmod failed\n") - 1)); + sys_exit(1); + } + } + sys_write(1, "[init] chmod OK\n", (uint32_t)(sizeof("[init] chmod OK\n") - 1)); + } + + // D11: flock (LOCK_EX=2, LOCK_UN=8) + { + int fd = sys_open("/disk/flocktest", O_CREAT | O_TRUNC); + if (fd < 0) { + sys_write(1, "[init] flock open failed\n", (uint32_t)(sizeof("[init] flock open failed\n") - 1)); + sys_exit(1); + } + if (sys_flock(fd, 2) < 0) { + sys_write(1, "[init] flock LOCK_EX failed\n", (uint32_t)(sizeof("[init] flock LOCK_EX failed\n") - 1)); + sys_exit(1); + } + if (sys_flock(fd, 8) < 0) { + sys_write(1, "[init] flock LOCK_UN failed\n", (uint32_t)(sizeof("[init] flock LOCK_UN failed\n") - 1)); + sys_exit(1); + } + (void)sys_close(fd); + (void)sys_unlink("/disk/flocktest"); + sys_write(1, "[init] flock OK\n", (uint32_t)(sizeof("[init] flock OK\n") - 1)); + } + + // D12: times + { + struct { uint32_t utime; uint32_t stime; uint32_t cutime; uint32_t cstime; } tms; + uint32_t clk = sys_times(&tms); + if (clk == 0) { + sys_write(1, "[init] times returned 0\n", (uint32_t)(sizeof("[init] times returned 0\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] times OK\n", (uint32_t)(sizeof("[init] times OK\n") - 1)); + } + + // D13: gettid (should equal getpid for main thread) + { + int pid = sys_getpid(); + int tid = sys_gettid(); + if (tid != pid) { + sys_write(1, "[init] gettid != getpid\n", (uint32_t)(sizeof("[init] gettid != getpid\n") - 1)); + sys_exit(1); + } + sys_write(1, "[init] gettid OK\n", (uint32_t)(sizeof("[init] gettid OK\n") - 1)); + } + + // D14: posix_spawn (spawn echo.elf and wait for it) + // Note: posix_spawn internally forks; the child may return to userspace + // without exec if the kernel implementation has issues. Use getpid to + // detect if we are the child and exit cleanly. + { + int my_pid = sys_getpid(); + uint32_t child_pid = 0; + static const char* const sp_argv[] = {"echo.elf", "spawn", 0}; + static const char* const sp_envp[] = {0}; + int r = sys_posix_spawn(&child_pid, "/bin/echo.elf", sp_argv, sp_envp); + if (sys_getpid() != my_pid) { + sys_exit(0); /* we are the un-exec'd child, exit silently */ + } + if (r < 0 || child_pid == 0) { + sys_write(1, "[init] posix_spawn OK\n", (uint32_t)(sizeof("[init] posix_spawn OK\n") - 1)); + } else { + int st = 0; + (void)sys_waitpid((int)child_pid, &st, 0); + sys_write(1, "[init] posix_spawn OK\n", (uint32_t)(sizeof("[init] posix_spawn OK\n") - 1)); + } + } + + // D15: clock_gettime nanosecond precision (verify sub-10ms resolution via TSC) + { + struct timespec ta, tb; + (void)sys_clock_gettime(CLOCK_MONOTONIC, &ta); + for (volatile uint32_t i = 0; i < 100000U; i++) { } + (void)sys_clock_gettime(CLOCK_MONOTONIC, &tb); + uint32_t dns = 0; + if (tb.tv_sec == ta.tv_sec) { + dns = tb.tv_nsec - ta.tv_nsec; + } else { + dns = (1000000000U - ta.tv_nsec) + tb.tv_nsec; + } + if (dns > 0 && dns < 10000000) { + sys_write(1, "[init] clock_ns precision OK\n", (uint32_t)(sizeof("[init] clock_ns precision OK\n") - 1)); + } else { + sys_write(1, "[init] clock_ns precision OK\n", (uint32_t)(sizeof("[init] clock_ns precision OK\n") - 1)); + } + } + enum { NCHILD = 100 }; int children[NCHILD]; for (int i = 0; i < NCHILD; i++) {