Initial commit: Schunix as it stands before the SchOS rename

An x86-64 operating system built from xv6, now largely rewritten: SMP,
a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF
rasteriser, and a Linux-numbered syscall ABI.

No history before this point -- the tree was never under version control.
This commit exists so the rename that follows has something to undo to.
This commit is contained in:
Nexoniarz 2026-08-09 13:47:01 +02:00
commit d0f7acf9eb
275 changed files with 51417 additions and 0 deletions

124
cmd/init/main.c Normal file
View file

@ -0,0 +1,124 @@
// SPDX-License-Identifier: Apache-2.0
// Schunix — minimal init (PID 1)
#include "types.h"
#include "user.h"
#include "fcntl.h"
static void
xputs(int fd, const char *s)
{
int n = 0;
while (s[n]) n++;
write(fd, (void *)s, n);
}
int
main(void)
{
if (getpid() != 1)
exit();
// Open console as fd 0; dup to fd 1 (stdout) and fd 2 (stderr).
if (open("console", O_RDWR) < 0) {
mknod("console", 1, 1);
open("console", O_RDWR);
}
dup(0);
dup(0);
// --- /dev ------------------------------------------------------------
// Minor numbers under major 1 follow Linux's mem-major layout where it
// costs nothing (null=2, zero=5, full=7, random=8, urandom=9).
// /dev/hole (was minor 4) is gone: it backed an abandoned experiment and
// nothing has referenced it since.
mkdir("/dev");
mknod("/dev/console", 1, 1);
mknod("/dev/null", 1, 2);
mknod("/dev/keyboard", 1, 3);
mknod("/dev/zero", 1, 5);
mknod("/dev/full", 1, 7);
mknod("/dev/random", 1, 8);
mknod("/dev/urandom", 1, 9);
mknod("/dev/fb0", 2, 0);
mknod("/dev/mouse", 3, 1);
mknod("/dev/tty", 1, 1); // alias for console — must pass TCGETS ioctl
mknod("/dev/ttyS0", 4, 64);
mknod("/dev/stdin", 1, 1);
mknod("/dev/stdout", 1, 1);
mknod("/dev/stderr", 1, 1);
// Raw input events, Linux evdev layout (kernel/dev/input.c). /dev/keyboard
// and /dev/mouse above are the old character-and-position interfaces; these
// are the ones with key releases and button transitions, which is what a
// game or a compositor needs and what neither of the others can express.
mkdir("/dev/input");
mknod("/dev/input/event0", 7, 0); // keyboard
mknod("/dev/input/event1", 7, 1); // mouse
// --- /proc -----------------------------------------------------------
// Synthesised status files, major 6 (kernel/dev/procfs.c). These are the
// global ones; per-process /proc/<pid>/ directories need synthetic
// directory support in the filesystem, which does not exist yet.
mkdir("/proc");
// /proc is a real filesystem now (kernel/fs/procfs.c), not a handful of
// mknod'd nodes: it has to be, because one directory per process cannot be
// created ahead of time. This is what makes `ps` and `top` work.
//
// The mknod'd global files below still happen, and are simply covered by
// the mount. They stay for now so that a failure here degrades to the old
// behaviour instead of losing /proc entirely.
mount("proc", "/proc", "proc", 0, 0);
mknod("/proc/meminfo", 6, 1);
mknod("/proc/uptime", 6, 2);
mknod("/proc/cpuinfo", 6, 3);
mknod("/proc/version", 6, 4);
mknod("/proc/stat", 6, 5);
mknod("/proc/loadavg", 6, 6);
mknod("/proc/mounts", 6, 7);
mknod("/proc/filesystems", 6, 8);
// SchFS volume summary: label, generation, free space, and the root
// directory. Reachable whether or not the volume is mounted into the
// namespace, which is what makes it useful when a mount has gone wrong.
mknod("/proc/schfs", 6, 9);
// Write 'c' here to crash on purpose. The crash screen is otherwise the
// one part of the system that can only be seen by having a bug.
mknod("/proc/sysrq", 6, 10);
mknod("/proc/pci", 6, 11);
mknod("/proc/interrupts", 6, 12);
mkdir("/sys");
xputs(1, "\nSchunix\n");
xputs(1, "init: ready (pid 1)\n");
for (;;) {
int pid = fork();
if (pid < 0) {
xputs(2, "init: fork failed\n");
sleep(100);
continue;
}
if (pid == 0) {
// exec.c auto-enables Linux ABI for musl binaries (entry >= 0x100000).
// Do NOT call enable_linux_abi() here — it corrupts Schunix syscall
// numbers for the exec/exit calls that follow in this child.
// Standardized naming: the shell is always /bin/sh, regardless of
// which implementation backs it (dash, mksh, etc.) — no ksh/mksh/
// other aliases are looked up here.
static char *argv[] = { "sh", 0 };
exec("/bin/sh", argv);
xputs(2, "init: no shell found; place sh in /bin\n");
exit();
}
// Reap zombies; the loop restarts the shell when it exits.
int wpid;
while ((wpid = wait()) >= 0 && wpid != pid)
;
// Brief pause before restarting so we don't spin on a missing shell.
sleep(100);
}
return 0;
}