What this is
Ouroboros draws its design ideas from Linux, Minix, and Plan 9: a microkernel architecture, POSIX-ish system calls, preemptive multitasking, and a filesystem story that started with "research needed" and has since grown into a real, hand-rolled FAT32 read/write stack. It's built and tested primarily against Parallels Desktop on Apple Silicon rather than an emulator alone — a deliberate choice, since Parallels boots ARM VMs exclusively through UEFI firmware with no direct-kernel-boot shortcut, and real hardware has repeatedly disagreed with what "worked" on the emulator.
Every driver in this kernel — the console, the disk controller, the filesystem, the USB host controller — is hand-rolled directly against the relevant specification, with no external hardware crate. That's not a purity rule; it's mostly a consequence of running with no heap after boot services exit, and it's produced a genuinely large amount of real, documented, hard-hat debugging along the way — see the postmortems below.
Where things stand
The short version: there is a real, disk-loaded userland shell, with a working command surface over a real filesystem, running on real Parallels hardware — with a live display, a real, physical USB keyboard typing into it, real preemptive multitasking, and, as of the latest milestones, a real disk (a passed-through USB stick, driven over the kernel's own xHCI driver — the only storage path Parallels actually exposes), programs loaded and spawned from it at runtime, job control, and real message-passing IPC between tasks.
-
Boot & bring-up
UEFI entry, exception vectors, an MMU running on the kernel's own identity-mapped tables, a GICv2 + generic-timer preemption tick, and a real EL0/syscall boundary — all confirmed working, not just compiling.
-
Preemptive task switching
A real (if still coarse — two hardcoded tasks, strict round-robin) scheduler, alternating two EL0 tasks off the timer tick.
-
An interactive shell
First UART input, then a disk-loaded, config-selected userland shell — a genuinely separate program, not kernel code, with real tokenized command dispatch.
-
Runtime storage: virtio-blk + hand-rolled FAT32
Synchronous polling block I/O and a from-scratch FAT32 reader/writer, growing over several milestones into a full command surface:
ls,cat,cd,pwd,mkdir,rmdir,touch,rm,write,cp,mv. -
The console-discovery saga
Devicetree, ACPI/SPCR, and PCI-serial console discovery all confirmed dead ends on real Parallels hardware; a transmit-only virtio-console driver confirmed working on QEMU but confirmed not what Parallels actually exposes; a GOP framebuffer console — five real-hardware round trips, four real bugs — finally reaching a live, rendering shell prompt on real hardware.
-
USB HID keyboard input
A from-scratch xHCI driver, seven independently-confirmed real-hardware bugs (five found in the initial push; a sixth — dropped keystrokes under real preemption — and a seventh — an iteration-bounded timeout that should have been time-bounded, found by a user just booting the thing normally — both found and fixed weeks later), ending in a real physical keyboard typing into the shell on real Parallels hardware — full round trip, letters, backspace, Enter.
-
Real interrupt-controller discovery, and full preemption on Parallels
Real ACPI MADT parsing replaced the old QEMU-devicetree-derived GIC address; a new GICv3 driver reaches real hardware for the first time. A second real-hardware-only bug (the task switch hanging via
wfe, likely trapped by the host hypervisor) was root-caused and fixed the same session. Preemptive, two-task multitasking now works end to end on real Parallels hardware. -
A real relocating loader, and a real task lifecycle
Userland programs are genuine position-independent ELF now (killing the old no-
core::fmt/no-literal-comparison restrictions — and surfacing a latent syscall-path register-clobber bug along the way); tasks block instead of busy-polling, andexec,exit,wait,kill,fg, and a Ctrl+C escape hatch make spawned programs a real, controllable part of the system — including nested interactive shell sessions. -
A real disk on real Parallels hardware: USB mass storage
A dedicated diagnostic first proved Parallels exposes no storage controller of any kind on the guest PCI bus — so the kernel's own xHCI driver grew multi-device support and bulk endpoints, and a from-scratch Bulk-Only Transport + SCSI driver now mounts a passed-through USB 3.x stick: real FAT32, real files, programs
exec'd off it at runtime. -
Real IPC — driver isolation, part 1
Fixed-size messages copied through the kernel into bounded per-task mailboxes, a blocking receive on the proven wait machinery, and a real long-lived userland message server — demonstrated end to end on real hardware, off the real stick.
-
What's next
Driver isolation, part 2: moving the FAT32 filesystem out of the kernel into a userland server — the textbook microkernel move, speaking the new IPC to clients and raw block syscalls to the kernel.
Architecture at a glance
Before the debugging stories or the build-it-yourself tutorial, the shortest way in: a single visual page laying out the layers, the EL0/EL1 privilege boundary everything crosses, and who is responsible for what — including the one design decision that defines this kernel, a filesystem that runs as an unprivileged, restartable userland process.
Learn: build one yourself
The whole path this project walked, distilled into a staged tutorial — the inverse of the postmortems below. Where they document the debugging that found the working design, the tutorial presents only the design itself: nine ordered stages, each independently bootable, each ending with a concrete way to prove it works, with code samples drawn from this kernel's confirmed-working source.
The postmortems
Every real bug found along the way — the ones that mattered, not the routine typos — is written up in detail: symptom, root cause, how it was actually confirmed, and the generalizable lesson. Four bug-hunt chapters in chronological order, plus five design retrospectives from the microkernel-isolation, network, and userland arcs — each usable on its own.
From "it doesn't boot" to a real console
Exception vectors, the MMU switch, GIC/timer bring-up, the EL0/syscall boundary, and a console-discovery saga that killed three mechanisms and one whole driver before finding the one that worked on real hardware.
Part 2 · Shell & filesystemFrom a bare prompt to a real, disk-backed OS
A relocation bug found twice before its real shape was understood, a hang with zero diagnostic output, a disk that lied about its own filesystem format, and the write-support arc built on top.
Part 3 · xHCI keyboardGetting a USB keyboard working on Parallels
Seven real, confirmed hardware bugs bringing keyboard input to the kernel — a PCI Command register bit, a real UEFI firmware panic, a page-table assumption, a silently-dropped USB request class, a driver reading the wrong device, dropped keystrokes under real preemption, and a timeout that was iteration-bounded instead of time-bounded — the last two found weeks later, one of them by a user, not a script.
Part 4 · USB storageGiving Parallels a disk
Proving the hypervisor exposes no storage controller at all, USB passthrough that routes devices by speed and attaches them after your boot scan already ran, a mid-transfer keystroke that would have permanently killed the keyboard, SCSI Unit Attention found by a hot-plug test, and two error codes whose collapse silently turned append into overwrite.
Retrospective · a different eraEnforce the isolation, rebuild the data flow
Not a bug hunt but a design-led day: MMU-enforced per-task isolation and fault containment, then rebuilding every cheap data path enforcement broke — grant/safecopy IPC, a FAT32 offset-write, and the one works-on-QEMU-fails-on-silicon reversal.
Retrospective · driver isolationMoving the console into userland
The second component out of the kernel: a driver the kernel itself depends on (a split, not a move), the gated-primitives-vs-mapped-memory decision, a scheduler lie that routing echo through IPC exposed, a missed client that stranded on real hardware, and verifying pixels by screendump.
Retrospective · capability & hardeningScope it down before you build it
Five hardening milestones in a day — a health ping, the capability model, program-to-program pipes, a stack guard page, a userland heap — where three shrank when asked whether the clean design needed the hard part, the guard page caught a real silent overflow on its first test, and a one-build gate proved the heap couldn't be alloc-backed on stable.
Retrospective · network stackA network stack in a day
From a virtio-net driver to a concurrent HTTP server — the no-IOMMU driver/protocol split, the async-receive primitive a server needs, testing loss recovery on a wire that can't lose a packet, two bugs visible only in a packet trace, and the guard page catching the stack overflow twice more.
Retrospective · userland maturation/bin, pipelines, and where a command belongs
Builtins became a real /bin of standalone programs with multi-stage pipelines: the ps/kill/wait revert that showed job control belongs in the shell, a capability bitfield collision caught before it shipped, delegation needing less than feared, a pipeline heuristic replaced wholesale, and building a bootable GPT disk just to test one.
exFAT, ext2, and what real hardware revealed
Two filesystems added to the server — each read-only then read-write, all behind one unchanged protocol — with the lessons: an abstraction is only tested by a genuinely different implementation (ext2's inodes), validate against a foreign checker not your own reader, a freed inode's i_dtime that e2fsck misread as an orphan pointer, and a real-hardware pass that found a USB keyboard/storage bug the emulator structurally cannot show.
Every document in this project — the guides, the architecture and reference material, all nine postmortems, and the research notes — is collected in the documentation library.
Following along
The full, unfiltered history — every milestone, in order, with the same level of detail as the postmortems above — lives in the repository itself: CHANGELOG.md for what's done, ROADMAP.md for what's next, and CLAUDE.md for the single, continuously-updated document tracking the whole project's architecture and reasoning in real time.