| apps | ADD: integrate musl libc and wire syscall layer to userland apps | |
| assets | Add logo (on assets folder) and improve the README.md | |
| musl | ADD: Update README.md and the musl libc PORTING.md | |
| src | ADD: integrate musl libc and wire syscall layer to userland apps | |
| .gitignore | Initial commit of everos/8 | |
| LICENSE | Initial commit | |
| Makefile | ADD: integrate musl libc and wire syscall layer to userland apps | |
| README.md | ADD: Update README.md and the musl libc PORTING.md | |
The everos/8 project is a minimal operating system for x86, written in C and assembly. Everything in the system is a node in a hierarchical namespace tree, inspired by Plan 9 from Bell Labs.
The design
In Plan 9, there is no distinction between reading a file, querying a process, or talking to a device, every resource is a path you open, read, and write. everos/8 applies the same idea at the smallest possible scale:
/
├── dev/
│ ├── cons read: keyboard | write: screen
│ ├── null discards writes, returns empty on read
│ └── rand returns pseudo-random bytes on read
├── proc/
│ └── shell read: current process state
├── sys/
│ ├── version
│ └── motd
└── fs/ user-created files
ns_read(node, buf, len) works identically whether node is a plain file, a device, or a proc entry.
That uniformity is the entire point.
Memory layout
0x010000 – 0x1FFFFF kernel image (code + data + stack)
0x200000 – 0x3FFFFF kernel heap (kmalloc free-list allocator)
0x400000 – userland / musl brk region
The heap is a first-fit free-list allocator with immediate coalescing on kfree.
kmalloc / kzalloc / kfree are available everywhere in the kernel after mm_init().
Syscall ABI
Userland communicates with the kernel via int 0ドルx30 (not Linux's int 0ドルx80).
The register convention mirrors i386 Linux so musl can be ported with a one-file change:
| Register | Role |
|---|---|
eax |
syscall number |
ebx |
arg 1 |
ecx |
arg 2 |
edx |
arg 3 |
eax |
return value |
Syscalls implemented so far:
| Number | Name | Description |
|---|---|---|
| 0 | exit |
terminate the current app |
| 1 | write |
write bytes to VGA (fd 1 or 2) |
| 3 | read |
read a line from the keyboard (fd 0) |
| 7 | getpid |
returns 1 |
| 45 | brk |
move the userland heap break |
musl libc port
everos/8 ships with a port of musl libc 1.2.4 under musl/.
The entire port required changing exactly two files from upstream:
| File | Change |
|---|---|
musl/arch/i386/syscall_arch.h |
int 0ドルx80 -> int 0ドルx30 |
musl/arch/i386/bits/syscall.h.in |
everos/8 syscall numbers |
All 1500+ other musl source files are unmodified. Apps in apps/ are compiled against musl and linked statically into the kernel binary, so standard headers like <unistd.h>, <string.h>, and <stdlib.h> work out of the box.
musl port status:
| Phase | Modules | Syscalls needed | Status |
|---|---|---|---|
| 1 | string, stdlib, ctype, prng |
none | done |
| 2 | stdio (printf, fgets...) |
write, read |
next |
| 3 | malloc / free |
brk |
next |
| 4 | unistd (getpid, sleep...) |
exit, getpid |
next |
Writing an app
Apps live in apps/ and are compiled with musl headers. Add an entry to the table in src/kernel/app.c to register it with the shell.
// apps/hello.c
#include <unistd.h>
int hello_main(void) {
const char msg[] = "hello from everos/8\n";
write(1, msg, sizeof(msg) - 1);
return 0;
}
// src/kernel/app.c — register the new app
static const App apps[] = {
{ "hello", hello_main },
{ "myapp", myapp_main }, // add here
};
From the shell:
/ apps — list available apps
/ run hello — run an app
Requirements
sudo apt install binutils gcc-multilib qemu-system-x86 make gdb
Tested on Debian 13 (trixie) x86-64. The toolchain targets i386.
Building
make
Produces everos.img, a raw ~1.40 MB disk image.
Running
make run # inside the terminal (QEMU curses display)
make run-sdl # separate SDL window
To quit QEMU: Ctrl-A then X (curses), or close the window (SDL).
Manual invocation:
qemu-system-i386 -drive file=everos.img,format=raw,index=0,media=disk -m 32M
Debugging
make debug
Starts QEMU paused and waiting for GDB on port 1234, then launches GDB connected to it.
Shell commands
| Command | Description |
|---|---|
ls [path] |
List nodes in a directory |
read <path> |
Read a node |
write <path> <text> |
Write text to a node |
echo <text> |
Print text to the screen |
mkfile <path> [text] |
Create a file |
mkdir <path> |
Create a directory |
touch <path> |
Create an empty file |
rm <path> |
Remove a node |
mv <src> <dst> |
Move or rename a node |
dd if=<src> of=<dst> |
Copy data between nodes |
stat <path> |
Show node metadata |
cd [path] |
Change working directory |
ps |
List running processes |
mem |
Show heap usage |
apps |
List available apps |
run <name> |
Run an app |
clear |
Clear the screen |
about |
About |
help |
List commands |
Tab completion and arrow-key history navigation are supported.
Boot sequence
- QEMU loads the boot sector (
boot.S) at0x7C00in real mode. - The bootloader reads 32 sectors from disk into physical address
0x10000via INT 13h. - A20 is enabled, the GDT is loaded, and the CPU switches to 32-bit protected mode.
- Execution jumps to
entry.Sat0x10000, which sets up segment registers and the stack. kernel_main()initialises the heap, VGA, namespace tree, process table, and IDT.- The scheduler loop runs
shell_run()on every tick.
Limitations
- No virtual memory or paging.
- No hardware timer; the scheduler is purely cooperative.
- Apps are linked statically into the kernel image (no on-disk loading yet).
- No disk persistence — the namespace resets on reboot.
- x86 (i386) only.