A RISC-V 64-bit hobby operating system kernel written in Rust. No third-party runtime dependencies — if third-party crates are used, they are build-time only.
Inspired by SerenityOS, this project exists because writing an OS from scratch is fun. Check out the sysheap YouTube channel for coding videos.
An experiment: can we write a Linux-compatible kernel in Rust from scratch?
The long-term goal is a kernel that runs unmodified Linux userspace binaries — the same programs you'd run on a real Linux system, without recompilation. Only the kernel is rewritten; all userspace comes from existing projects (musl libc, dash, coreutils, etc.).
This is a hobby project and an honest experiment. We don't know how far we'll get. Progress is measured by running real programs: first a shell, then coreutils, then Python, then nginx. Each milestone is a concrete proof of compatibility, not a line-count metric.
See plans/ for the roadmap and strategy.
- Memory management — Bitmap page allocator with lazy zeroing, Sv39 page tables (3-level), kernel heap, per-process address spaces, mmap/munmap, brk
- Processes & threads — ELF loading, per-process file descriptor tables, thread states (Running/Runnable/Waiting/Zombie), async syscall model with wakers
- Scheduler — Per-CPU round-robin scheduler with 10ms quantum, global run queue, SMP support
- SMP — Multi-core boot via SBI hart management, per-CPU state structs, inter-processor interrupts (IPI)
- Syscalls — 74 Linux-compatible syscalls including signals, mmap, futex, networking, and filesystem operations
- Interrupts — RISC-V trap handling, PLIC for external interrupts, timer interrupts
- Networking — TCP and UDP stacks with ARP, IPv4, Ethernet framing; VirtIO network driver; per-port socket binding
- Filesystem — VFS layer with tmpfs, procfs, devfs, and read-only ext2; programs can read from disk images
- Drivers — VirtIO network and block devices (feature negotiation, virtqueues), PCI enumeration with MMIO BAR allocation
- Debugging — DWARF-based backtrace with symbol resolution, Rust demangling, state dump on Ctrl+D, configurable per-module debug logging
- Shell (SoSH) — Command parsing, program execution with arguments, background processes (
&), built-in help - 30+ programs — init, shell, TCP/UDP networking, sleep, stress testing, filesystem tests, signal tests, and various test utilities
- Programs are compiled against musl libc and embedded directly into the kernel binary
- MCP server — AI agents can boot QEMU, send shell commands, build the kernel, and run tests over the Model Context Protocol
- GDB MCP server — Programmatic GDB debugging (breakpoints, stepping, register inspection) exposed as MCP tools
- System tests — Integration tests that boot the OS in QEMU and interact via stdin/stdout, covering networking, processes, signals, stress, and shell behavior
- Unit tests — Kernel unit tests with a custom
#[test_case]framework, plus Miri for undefined behavior detection - CI — Build, fmt, clippy, unit tests, Miri, and system tests on Ubuntu via CMake
- AI-assisted development — Issues and PRs may be created by Claude Code under the maintainer's GitHub account
- Copy-on-Write fork (currently copies the full address space on fork)
- Demand paging (mmap allocates all pages eagerly)
- epoll / select (only ppoll is implemented)
- Unix domain sockets
- Pseudo-terminals (PTY)
- Writable disk filesystem
The build system is CMake + Kconfig, driven through a thin justfile
passthrough. Three steps on a fresh clone:
just configure # cmake --preset riscv64-virt (seeds build/.config)
just toolchain # stages musl + linux-headers + compiler-rt (~2 min, cached)
just # builds the kernel binary (build/kernel/solaya.bin)
just run # boot the kernel in QEMUThe cross-toolchain itself (clang / lld / llvm-*) comes from the host
distro and must be LLVM >= 18. just toolchain only stages the musl
sysroot + linux UAPI headers + compiler-rt builtins into
.toolchain/riscv64/ (outside build/ so rm -rf build doesn't nuke
it), so the only host deps are distro LLVM, rustup, and the packages
listed below.
Rust toolchain. The repo pins a specific nightly via
rust-toolchain.toml; install rustup and it will fetch the right
toolchain automatically on first cargo invocation:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --default-toolchain none --profile minimalUbuntu / Debian. Exactly the set CI installs:
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential \
cmake ninja-build \
python3 \
clang-18 lld-18 llvm-18 libclang-dev \
tmux \
flex bison bc \
pkg-config \
rsync \
shellcheck \
e2fsprogs \
qemu-system-misc qemu-system-data seabios ipxe-qemu
# `just` is not packaged on 24.04; install via rustup's cargo:
cargo install just --lockedFedora. Same tools, Fedora package names:
sudo dnf install -y \
gcc gcc-c++ make \
cmake ninja-build \
python3 \
clang lld llvm clang-devel \
tmux \
flex bison bc \
pkgconf-pkg-config \
rsync \
shellcheck \
e2fsprogs \
just \
qemu-system-riscv seavgabios-bin ipxe-roms-qemue2fsprogs is needed by the ext2 system tests (they shell out to
mkfs.ext2). The seabios (Ubuntu) / seavgabios-bin (Fedora)
package ships vgabios-bochs-display.bin, which the framebuffer +
doom system tests need for -device bochs-display.
The ipxe-qemu (Ubuntu) / ipxe-roms-qemu (Fedora) package ships
efi-virtio.rom and the other PCI option ROMs that QEMU loads
unconditionally for virtio-net-pci; without it QEMU aborts with
failed to find romfile "efi-virtio.rom". It is a Recommends of
the qemu packages, so systems that install with --no-install-recommends
(e.g. CI) have to list it explicitly.
cargo-nextest. Required for running the system tests:
cargo install cargo-nextest --lockedType help in the shell for available commands. Type a program name to execute it. Append & to run it in the background. See userspace/src/bin/ for the full list of programs.
kernel/ Main kernel (RISC-V 64-bit, no_std)
userspace/ Userspace programs (musl libc)
common/ Shared no_std library
system-tests/ Integration tests (run on x86, test via QEMU)
qemu-infra/ Shared QEMU communication library
mcp-server/ MCP server for AI agent interaction
gdb_mcp_server/ GDB MCP server for programmatic debugging
headers/ Linux C header bindings via bindgen
doc/ai/ Detailed AI-facing documentation
plans/ Roadmap and strategy documents
The justfile at the root is a one-line passthrough to
cmake --build build --target ... for the common targets, plus a few
arg-taking recipes that are awkward to express as CMake targets.
just # Build kernel binary
just run # Build and run in QEMU
just run-fb # Run with framebuffer
just debug # QEMU (paused) + GDB attached in tmux
just debug FUNC # … with a breakpoint on FUNC
just debug USERBIN FUNC # Debug inside a userspace binary
just attach # Attach GDB to an already-running QEMU
just disasm # Disassemble the boot ELF
just addr2line 0xADDR # Resolve an address in the kernel ELF
just test # Run all tests (unit + system)
just test-system TEST # Run a single system test
just clippy # Run linter across every workspace
just fmt-check # cargo fmt --check across every workspace
just miri # Undefined-behavior detector
just ci # Full pre-merge gate (fmt + clippy + tests + miri + system)
just menuconfig # Kconfig TUI to edit build/.config