An OS written in Rust and Svelte on the web. Inspired by:
Install dependencies:
rustup toolchain install nightly # at least nightly 2020-07-15
cargo install bootimage
cargo install bacon --locked --features "clipboard"
npm install
uv venv
uv pip install pre-commit
source .venv/bin/activate
pre-commit install
flyctl auth loginCommands:
# test the kernel
cargo test -p os --target crates/os/x86_64-os.json -Z build-std=core,compiler_builtins,alloc -Z build-std-features=compiler-builtins-mem
# build the kernel
cargo bootimage -p os --target crates/os/x86_64-os.json -Z build-std=core,compiler_builtins,alloc -Z build-std-features=compiler-builtins-mem
# run the kernel
qemu-system-x86_64 -d int -no-reboot -no-shutdown -serial stdio -D qemu.log -drive format=raw,file=target/x86_64-os/debug/bootimage-os.bin
# test the app
cargo test -p app
# run the app
mprocs
# run pre-commit hooks
uvx pre-commit run --all-files
# deploy to fly.io
flyctl launch # once
flyctl deploy- Bootloader: bootable disk image via
bootloadercrate - GDT/TSS: segment descriptors with double-fault IST stack
- IDT: exception handlers, PIC remap, timer (100 Hz) + keyboard IRQs
- Syscall entry:
syscall/sysret, MSR setup (IA32_STAR,IA32_LSTAR,IA32_FMASK) - Ring 3 transition: enter user mode and return via syscall
- Context switching: process table, PCB, CR3 tracking, kernel/user stacks
- Preemptive scheduler: timer-driven round-robin with context save/restore
- Program loading: flat binary + ELF64 loader (PT_LOAD segments)
- Trap coverage: page fault/GP/DF/invalid-opcode paths with panic-safe reporting and tests
- Stack guards: guard pages for kernel/user stacks; IST coverage for nested faults
- Paging: 4-level page tables via
x86_64, per-process roots - Frame allocator: allocate physical frames from
BootInfomemory map - Heap allocator: kernel heap via
linked_list_allocator - User pointer safety: copyin/copyout helpers that fault on invalid user buffers
- User heap:
sbrkgrowth/shrink with guard pages
- Process lifecycle:
fork/exec/exit/wait/waitpidwith zombie reaping and reparenting - Blocking primitives: sleep queues for
nanosleep,waitpid, IPC pipes/mailboxes - Sleep/wakeup channels for generic I/O wait; blocking mutexes and condition variables
- Voluntary
yieldand per-process time accounting - Process control extras:
kill,pause,uptime
- Message-passing: per-process mailboxes with bounded queues and blocking receive
- Pipes:
pipe,pipe_read,pipe_write,pipe_close(anonymous byte streams) - Pipe correctness suite: half-close, EOF, backpressure, large writes, fairness under contention
- Event waits: select/poll-style waits across channels, pipes, and keyboard input
- Ramfs bring-up: inode table, directory entries, root directory
- Per-process FD table with inheritance;
dup/dup2support and close semantics - Path resolution: absolute/relative traversal with
./..and permissions - File I/O syscalls:
open,close,read,write,lseek,stat,fstat - Directories and links:
link,unlink,readdir/getdents(havemkdir,chdir,getcwd) - File descriptor utilities: close-on-exec flags, fd table limits, edge cases
- File system tests: directory traversal, link counts, permission checks
- VGA text mode: 80×25 character output mirrored to browser via serial
- Serial (UART): COM1 logging and VGA frame transport via
uart_16550 - PS/2 keyboard: scancode reading and decoding via
pc-keyboard - Timer (PIT): 100 Hz tick counter and uptime tracking via
pic8259 - Interrupt hardening: spurious IRQ handling, prioritization, and metrics
- Console I/O:
read(fd=0),write(fd=1)for keyboard/console - Process control:
fork,exec,exit,wait,waitpid - Process info:
getpid,getppid - Timing:
nanosleep - IPC:
channel_send,channel_recv,pipe,pipe_read,pipe_write,pipe_close - File system:
open,close,read,write,lseek,stat,fstat,dup,dup2 - Process control extras:
kill,pause,uptime,yield - User buffer safety: copyin/copyout with fault recovery for all pointer-using syscalls
- FS coverage:
link,unlink(havemkdir,chdir,getcwd)
- Shell core:
- REPL with line editing; builtins
exit,echo,help - Piping (
|), redirection (>,<,>>), background&, jobs/fg/bg
- REPL with line editing; builtins
- Utilities:
-
cat,wc,stat,pwd -
ls,echo,rm,mkdir,ln,grep,time,yes
-
- Process/IPC tools:
-
sleep N -
ps,kill, lightweighttop - IPC demos: pipe ping-pong; mailbox/channel echo and stress
-
- Testing and stress:
-
forktest,stressfs/logstress, simple zombie/orphan demo, basic user test suite
-
- Init:
- Minimal
initthat execs the shell and reaps children
- Minimal
- Axum backend: serve static assets, manage QEMU bridge state
- QEMU integration: spawn QEMU, capture serial, decode VGA markers
- WebSocket VGA stream: push frames/cells to browser, cache for new clients
- Keyboard bridge: browser
keydown→ QMPsendkey - VM controls: reset, pause/resume via QMP
- Multi-session: isolated QEMU instances per user with resource caps and lifecycle management
- Observability: expose scheduler/process telemetry, syscall stats, and backpressure signals to the UI
- Browser UX: overlays for file system, IPC demo tooling, and keyboard/mouse capture options
- Test framework: custom harness with QEMU exit codes
- Boot tests: println, heap allocation, stack overflow IST, program loader
- Syscall tests: user-mode FS happy-path + error suite
- Scheduler tests: preemption, blocking, wake-up ordering, starvation checks
- Concurrency tests: lock correctness and contention stress
- FS tests: file/directory operations and durability once storage lands
- IPC tests: pipe edge cases, mailbox saturation, channel fairness
- Soak/fuzz: ELF loader fuzzing, QMP parser fuzzing, long-running leak/stability runs
Some interesting features (beyond that covered in the blog post series):
- The kernel's
vga_buffermirrors screen state over UART using three compact text formats:[[VGA_FRAME …]](full 80×25 snapshot),[[VGA_CELL …]](single-character update), and[[VGA_SCROLL …]](line shift with new bottom row). The Axum server tails QEMU's serial output, parses these markers, and broadcasts typedVgaUpdatemessages over WebSocket. The browser builds the grid once and applies incremental updates—only changed cells touch the DOM. - The web server communicates with QEMU via QMP (QEMU Monitor Protocol) over a Unix socket. Browser keyboard events are translated to
sendkeycommands, and VM controls (reset, pause, resume) use HMP commands tunneled through QMP—allowing full interaction without a local display. - The timer interrupt periodically emits
[[SYSINFO uptime_ms=… usable_mb=… total_mb=… ticks=…]]and[[PROC pid=… name=… state=… …]]lines. The backend parses these intoOsStatsandProcessesSnapshotstructs, cached inAppStateand exposed via/api/os/statsand/api/os/processesfor real-time dashboard panels. - The kernel uses
syscall/sysretwith MSR configuration (STAR/LSTAR/FMASK). A nakedsyscall_entrystub saves registers and dispatches to Rust handlers implementing console I/O, process control (fork,exec,exit,wait,waitpid), timing (nanosleep), and IPC (channel_send/channel_recv,pipe/pipe_read/pipe_write/pipe_close), plus PID queries. - The PCB table tracks each process's PID, parent, kernel stack, entry point, run state, saved registers, and page table root (CR3). The timer IRQ fires at 100 Hz; each tick saves the outgoing context, picks the next
Readyprocess, restores its context (including CR3), and returns viairetq. Context switches only occur from ring 3 to avoid interrupting in-flight syscalls. - The ELF loader parses ELF64 headers and PT_LOAD segments, supporting both flat (vaddr=0) and position-dependent layouts. Segments are copied into heap-backed regions, .bss is zero-filled, and the entry point/stack pointer are recorded in the PCB. This allows loading separately-compiled user binaries rather than embedding raw assembly.
- The message-passing IPC subsystem gives each process a kernel-managed mailbox (bounded queue).
channel_send(target_pid, buf, len)copies payloads into the target's queue and wakes any blocked receiver;channel_recv(buf, len)pops from the caller's mailbox, blocking if empty until a message arrives. - Anonymous pipes provide unidirectional byte streams between processes.
pipeallocates a 512-byte circular buffer;pipe_readandpipe_writetransfer data with blocking when empty/full;pipe_closesignals EOF to the other end.
The important files are:
crates/
app/
src/
handlers.rs HTTP + WebSocket handlers for VGA stream, QEMU control, and OS stats/processes APIs
qmp.rs QMP client over Unix socket for `sendkey` and HMP commands (reset, pause, resume)
main.rs Axum server; serves `/api/*`, `/ws/vga`, and the built SvelteKit `dist/`
tests/
handlers.rs Smoke tests for static asset and API endpoints
os/
src/
boot/
gdt.rs GDT/TSS setup (kernel + user segments, IST stack for double-fault)
interrupts.rs IDT, PIC remapping, timer (100 Hz) + keyboard IRQ handlers, exception handlers
memory.rs Paging setup, frame allocator, per-process tables, copy-on-write fork
drivers/
serial.rs UART (COM1) initialization and `serial_print!` macros
vga.rs VGA text console + serial mirroring (`[[VGA_FRAME]]`, `[[VGA_CELL]]`, `[[VGA_SCROLL]]`)
process/
mod.rs PCB table, PID allocation, context save/restore, blocking/wake primitives
scheduler.rs Preemptive round-robin scheduler triggered by timer IRQ
programs/
binaries.rs Embeds prebuilt Rust user ELF images via include_bytes!
loader.rs ELF64 parser and PT_LOAD segment loader for user programs
user_mode.rs Launches user space, spawns builtin programs, enters ring 3 via `sysretq`
task/
executor.rs Cooperative async executor with task queue and Wakers
keyboard.rs Scancode queue, async `ScancodeStream`, and blocking `read_char_blocking` for syscalls
allocator.rs Kernel heap: fixed-size block allocator with linked-list fallback
context.rs `SavedRegisters`, `CpuContext`, `PageTableRoot` (CR3) for context switching
ipc.rs Message-passing mailboxes + anonymous pipes with blocking read/write
syscall.rs `syscall`/`sysret` entry stub, MSR setup, and Rust syscall handlers
sysinfo.rs Emits `[[SYSINFO]]` and `[[PROC]]` telemetry over serial for the frontend
tests/
basic_boot.rs println smoke test
heap_allocation.rs Heap allocator stress/regression tests
stack_overflow.rs Double-fault IST coverage
should_panic.rs Ensures panic path exits QEMU as expected
program_loader.rs Validates spawn_program stacks + ELF guards
x86_64-os.json Custom target spec: no_std, soft-float, disable red zone, panic=abort
user/
src/bin/
shell.rs User-mode shell compiled as ELF and embedded in kernel
src/
routes/
+page.svelte UI (VGA grid, overlays, stats/IPC panels, websocket + keyboard bridge)
