Maestro: A Unix-Like Kernel Written from Scratch in Rust

A solo developer scrapped 18 months of C kernel work to restart in Rust, and has since built a bootable monolithic OS with a package manager, running bash and a working subset of GNU coreutils.

Maestro: A Unix-Like Kernel Written from Scratch in Rust

Kernel development is usually framed as C’s natural domain — the language closest to the hardware, trusted by the systems running everything. Starting a kernel in Rust doesn’t just question that assumption; restarting an existing C kernel in Rust, after 18 months of work, means paying the full cost to find out whether it was ever true.

Source

Maestro - Introduction

By: blog.lenot.re
Published: Jan 02 2024
Type: Personal engineering blog

Worth saving as a first-person account of what the Rust-for-kernels decision costs at the project level — not a language debate but a practitioner’s running notes from years of doing it.

How a C-to-Rust restart changes what kernel debugging looks like

Maestro started as a school project in December 2018 — a C kernel, first committed at 3 AM, which is either a good origin story or a warning depending on how you read late-night programming. The C phase lasted about a year and a half, long enough to build working foundations but also long enough for the codebase to become too hard to maintain. At that point, the author made the call to throw away the existing work and restart in Rust.

The motivation was specific to a property of kernel development that makes it qualitatively harder than userspace programming: when something goes wrong, the normal debugging tools aren’t available. Valgrind requires the kernel to already be running. GDB works with QEMU and VMware but not reliably — the kernel can behave differently on emulated versus real hardware, and GDB itself can crash. BIOS documentation is incomplete and implementation-specific. When the kernel writes to the wrong memory address, it might be writing to its own code, producing a failure that destroys the evidence before any debugger arrives. Memory safety enforced at compile time doesn’t eliminate all of these problems, but it eliminates a large class before any code runs — which in kernel space, where a runtime memory error means a crash with limited introspection, is a meaningful shift in the development experience.

At time of publication, the kernel is monolithic, supports only x86 32-bit, and implements about 135 out of 437 Linux system calls (roughly 31%). The codebase is 48,800 lines across 615 files by cloc count. Those numbers have context: “toy kernel” usually means paging, a scheduler, and a handful of syscalls. 135 syscalls is enough to run bash, GNU coreutils (ls, cat, mkdir, rm, uname, and others), musl as a C standard library, and a patched neofetch. Not a production OS, but further along than most hobby kernels get before stalling.

The project includes a userland ecosystem alongside the kernel. Solfège handles boot and daemon management, described as similar to systemd but lighter. maestro-utils provides basic system utilities. blimp is a package manager — but getting blimp fully functional on Maestro requires two missing pieces: network support, currently in development, and shared library loading via mmap, not yet implemented. The development loop the blog describes is iterative: run a program, watch it fail, find the first syscall it trips on, implement or fix that syscall, repeat. Each new program that works exercises a different slice of the syscall surface and makes the kernel more stable in practice.

The x86 32-bit limitation is the most constraining current gap. It means the kernel won’t run on modern hardware that has dropped 32-bit support, can’t use 64-bit-only CPU features, and is working with a memory address space capped at 4 GB. The author acknowledges this and the broader technical debt accumulated when the project had a school deadline. The blog’s stated agenda is to cover problems that appear after the basics — the kind of kernel behavior that tutorial series stop before reaching because OSDev.org and Philipp Oppermann’s writing already cover the entry-level material well. The goal, in the blog’s own framing, is to push as far as Rust will go in kernel space and document what you find when you do.

The common misread of a project like this is to classify it as a teaching exercise or a language experiment. The package manager, the daemon manager, the syscall completeness tracking, and the stated goal of running compilers and Git on Maestro itself all point toward something more durable: an OS the author intends to actually use, built in a language the author thinks is the right one for the job, and documented carefully enough to be useful to others working on the same problems later.

What I want to remember

Remember: Memory safety pays off most in kernel space precisely because the normal debugging tools don’t exist there — no valgrind, unreliable GDB, and a bad pointer that can corrupt the evidence before you see it. Shifting that class of errors to the compiler is worth the restart cost.