An xv6-inspired teaching operating system written in Rust for 64-bit RISC-V.
It boots on QEMU's virt machine and explores how Rust ownership and shared
types can be applied to a small, multi-core kernel.
Important
xv6-rust is an experimental learning project, not a production operating system. Kernel interfaces and on-disk formats may change without notice.
- Multi-core RV64 kernel running on QEMU
virt - Virtual memory, traps, timer interrupts, and system calls
- Buddy-system kernel allocator
- xv6-style filesystem backed by a VirtIO block device
- UART console plus PCI/E1000 initialization
- Scheduler-managed system threads
- User threads with shared address spaces and process resources
- FIFO scheduler run queue backed by
VecDeque - Userspace
quitcommand for cleanly leaving QEMU
Install the following tools and make sure they are available on PATH:
- Rust through rustup
- GNU Make, a host C compiler, Perl, and Python 3
- a RISC-V bare-metal C toolchain providing either
riscv64-unknown-elf-*orriscv64-elf-* qemu-system-riscv64(QEMU 11.1.1 is the latest validated release)
The root rust-toolchain.toml pins nightly-2026-09-02, the RISC-V target,
and llvm-tools-preview. Rustup installs that exact snapshot automatically
when you enter the repository. Install the separately distributed binary-tool
proxy at its validated version:
cargo install cargo-binutils --version 0.4.0 --lockedThe kernel still needs nightly only for the no-std allocation error handler. From the repository root, verify the selected compiler with:
rustc --version
rustc 1.100.0-nightly (5db7f4be8 2026-09-01)
Cargo dependencies are reproducible through the committed kernel/Cargo.lock.
See the toolchain policy before updating the pinned
snapshot or dependency lockfile.
Clone all submodules, build the filesystem image and kernel, then start QEMU:
git clone --recurse-submodules https://github.com/Ko-oK-OS/xv6-rust.git
cd xv6-rust
make runAt the xv6 Rust >>> prompt, try ls, cat README.md, threadtest, or
forktest. Run quit to shut down the guest and exit QEMU.
If the repository was cloned without --recurse-submodules, initialize the
userspace, allocator, and filesystem-builder repositories with:
git submodule update --init --recursiveThe integration harness rebuilds the guest, copies fs.img to a temporary
directory, and exercises a user program in a fresh QEMU instance. For example:
python3 tests/qemu_user_program.py scheduler-queue
python3 tests/qemu_user_program.py user-threads
python3 tests/qemu_user_program.py stressfsAvailable cases are listed by:
python3 tests/qemu_user_program.py --helpThe scheduler-queue case deliberately fills, drains, and reuses process slots
within one boot. It protects the run-queue invariants across fork, user-thread
creation, yield, sleep, and wakeup.
Process objects live in a fixed-size table because kernel stacks, raw parent
pointers, CPU-local process references, and user-thread trapframe addresses all
depend on stable slot addresses. Scheduling does not scan that table: a locked
VecDeque<usize> stores runnable slot indices in FIFO order. A per-process
queued bit makes the state transition and queue membership one invariant and
prevents two harts from selecting the same saved context.
See the scheduler design for the state transitions and lock-order rules.
| Path | Purpose |
|---|---|
kernel/ |
Rust kernel and RISC-V platform code |
xv6-user/ |
C userspace and syscall wrappers (submodule) |
allocator/ |
Buddy allocator (submodule) |
xv6-mkfs/ |
Host-side filesystem image builder (submodule) |
tests/ |
QEMU-driven regression programs and harness |
docs/ |
Design notes and project documentation |
Useful commands from the repository root:
make -C kernel build # build the kernel binary
make fs.img # build userspace and the filesystem image
make run # build and boot a three-hart QEMU guest
make asm # write the kernel disassembly to kernel.S
make clean # remove generated build artifactsFor a monitor that presents xv6 as a single-hart S-mode payload, build the separate SBI entry path with:
make sbiThis keeps the normal QEMU M-mode entry unchanged. The SBI build takes the
hart ID from a0, uses legacy SBI calls for the console and timer, and expects
to be loaded at 0x80000000. It is intended for a virtual SBI implementation
such as Hypocaust rather than for direct loading alongside OpenSBI, whose
firmware occupies the beginning of RAM.
make -C kernel debug starts QEMU and GDB in a tmux session. The GDB executable
is currently configured by GDB in kernel/Makefile; override that variable
for your local RISC-V toolchain when necessary.
The core educational path—boot, memory management, processes, system calls, locking, filesystem access, multi-core scheduling, system threads, and user threads—is implemented. Current areas for further work include:
- completing the E1000 data path and adding a network stack
- documenting and simplifying the kernel memory model
- expanding scheduler policy and observability
- adding asynchronous I/O
- supporting more boards and architectures
Open bugs and proposed work are tracked in GitHub Issues.
Issues, documentation fixes, tests, and focused pull requests are welcome.
- Open or select an issue so the expected behavior is clear.
- Create a descriptive branch such as
feature/fifo-schedulerorfix-bugs/exec-cleanup. - Keep commits focused and explain non-obvious kernel invariants in code.
- Run the relevant QEMU regression cases and include the results in the PR.
- Describe user-visible behavior, design tradeoffs, and follow-up work.
For substantial architecture changes, please start with an issue or discussion before investing in an implementation.
- Project design document (中文)
- Boot sequence
- Virtual memory
- Process model
- Scheduler
- Locks
- Interrupts
- Filesystem (中文)
- Toolchain and update policy
This project builds on ideas and teaching material from xv6-riscv, rCore, and Writing an OS in Rust.
xv6-rust is available under the MIT License.
