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"
{"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"}
{"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 ----
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 {
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"
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++) {