# Vision: a microkernel, built to learn danos exists first and foremost as a **learning-by-doing project**: the point is to build a real operating system, bump into the hard constraints for real, and research them from a position of having actually hit them. The docs in this folder are part of that — they're where a constraint gets understood once it's been met. That framing sets the priorities. danos is not chasing a spec or a product; it's chasing understanding, with a concrete, motivating **win condition** to aim at. ## The win condition danos is a "win" when it: - **boots and runs on real hardware** — the author's **PC** (x86-64) and **both Raspberry Pis**: the **Zero 2 W** and the **Pi 5** (both `aarch64`, one backend — see [arm.md](arm.md)), - **has a graphical user interface**, ideally — building on the framebuffer it already draws to. Everything below serves that, or serves the curiosity that the project runs on. ## Why a microkernel: resilience The kernel stays **minimal** — only what genuinely must run privileged: - scheduling, - inter-process communication (IPC), - memory management (address spaces, page tables), - low-level interrupt dispatch. Everything else — device drivers, filesystems, the GUI, the network stack — runs as an **isolated user-space server**, each in its own address space with only the privileges it needs. The reason for this shape is **resilience**: the ability to **re-initialise parts of the OS while it runs**. A driver bug can't corrupt the kernel or another driver; a crashed or wedged component is contained, killed, and **restarted** — "if I break something, I can just fix it," without rebooting. Keeping the kernel tiny is part of that strategy: the one thing that *can't* be restarted is the trusted base, so the less code in it, the less that can take the whole system down. This is the project's real motivation, and it has its own design note: [resilience.md](resilience.md). The cost is that **IPC becomes the backbone**: what used to be a function call inside a monolithic kernel is now a message between address spaces. In a microkernel, IPC performance essentially *is* system performance (the lesson of L4), so it's a first-class concern. Hardware interrupts become IPC too: the kernel turns an IRQ into a message to the driver that owns the device. ## On real-time: an option, not a commitment danos was originally framed as a hard **real-time** OS. That's now held as **one interesting constraint to explore, not a requirement** — because real-time is a *pervasive* invariant (every operation must be provably time-bounded, everywhere) that would slow every milestone, whereas resilience is a set of *structural* features that's lighter to build and is what the project actually wants. The trade-off is written up in [smp.md](smp.md#does-the-right-choice-depend-on-real-time-vs-resilience). What danos keeps from the real-time direction, because it's cheap and useful anyway: - **Fixed-priority preemptive scheduling** — the highest-priority ready task runs, and preemption lets a runaway component be interrupted and killed (which *serves resilience*). Already built ([scheduling.md](scheduling.md)). - **A calibrated, deterministic clock** — already built ([device-interrupts.md](device-interrupts.md)). What danos does *not* owe anyone unless it deliberately chooses real-time later: timing *guarantees*, priority inheritance, bounded allocators, tickless timers, MCS scheduling contexts. Concretely, the current [heap](heap.md) is a first-fit free list with unbounded allocation time — fine here, and only a problem *if* a hard-real-time path is ever added. Note that **QNX is both** a real-time and a restartable microkernel, so choosing resilience now doesn't close the real-time door — it just doesn't pay the tax yet. ## The roadmap — tracks, not a strict line Because the driver is curiosity plus the win condition, the roadmap is a set of **tracks** with dependencies, not a rigid sequence. Pick by interest; mind the prerequisites. **Done:** UEFI boot, framebuffer + [serial](testing.md), [physical frames](frame-allocator.md) (with boot-services memory reclaimed), [paging](paging.md) with W^X, [exceptions and interrupts](interrupts.md), a [calibrated timer + ns clock](device-interrupts.md), a [heap](heap.md), a [fixed-priority preemptive scheduler](scheduling.md) with blocking, in-kernel [IPC channels](ipc.md), SMP (all cores scheduling, with affinity), and **ring 3**: user GDT/TSS plumbing, U/S-bit mappings, an `int 0x80` syscall gate, and `/sbin/init` — a real user ELF built from `sbin/`, shipped on the boot volume, loaded by the kernel, run at CPL 3 — plus a [test harness](testing.md). - **Isolation track** — **user mode + address-space isolation** (higher-half kernel, ring 3, per-process page tables). The substrate everything else needs. *In progress: ring 3 + a loaded `/sbin/init` work (M1); next the higher-half move (M2), then per-process address spaces + `syscall`/`sysret` + init as a real schedulable process (M3), then ELF/initrd generalisation (M4).* - **Resilience track** — fault → kill → notify, a supervisor/reincarnation server, resource cleanup on death, then a restartable driver as proof. Needs isolation. See [resilience.md](resilience.md). - **ARM track** — the `aarch64` port so danos runs on the Zero 2 W and Pi 5. Largely independent of the others (it's the [arch layer](arch.md)); directly serves the win condition. Likely via aarch64-UEFI first (QEMU `virt` + AAVMF), then real boards. See [arm.md](arm.md), and [discovery.md](discovery.md) for the device tree it needs. - **GUI track** — a framebuffer-based windowing/compositor, and the input + display drivers under it. Builds on the neutral framebuffer (so it's arch-independent), and on the driver model from the isolation/resilience tracks. The visible payoff. The natural spine is **isolation → (resilience + drivers) → GUI**, with the **ARM track** pursued alongside whenever the itch to see it boot on a Pi wins out. ## How to use this page Read it before adding anything structural. When a design decision comes up, the question is: does it serve the **win condition** (runs on the three machines, with a GUI), or the **learning** (a constraint worth meeting)? If it serves neither — e.g. paying the full real-time tax with no payoff in sight — it can wait.