Skip to content

Architecture

Limine loads the kernel over UEFI using the Limine boot protocol at base revision 6, and calls main():

  1. GDT, IDT, frame allocator, kernel heap.
  2. ACPI tables, local APIC, I/O APIC, HPET. The APIC timer is calibrated against the HPET at boot; on a typical guest that lands around 1000 MHz with a 3.5 GHz TSC.
  3. SMP bring-up. Each application processor enables SYSCALL/SYSRET, turns on the FPU and SSE, and initialises its own scheduler before reporting online.
  4. The reaper and inode-eviction kernel threads, then the block page cache.
  5. PCI enumeration and driver init: virtio-gpu, xHCI, e1000e, Intel HDA, AHCI.
  6. Write-back and journal-committer kernel threads, fs::init, window::init.
  7. A system-mount kernel thread mounts the root filesystem, then devfs on /dev, procfs on /proc and memfs on /tmp.
  8. It loads bin/edos-init and queues it on the scheduler.

The whole sequence takes about 1.6 seconds on a four-core guest with KVM, and the boot log on the home page is that sequence verbatim.

The root filesystem is chosen from the Limine command line:

root=UUID=87654321-4321-8765-cba9-987654321fed rootfstype=efs

mount_system_fs enumerates GPT partitions and mounts the one whose partition GUID matches. Two details matter:

  • The ISO carries a complete GPT disk image as a Limine boot module. A RAM-backed block device exposes it, which is how the ISO boots to a desktop with nothing else attached. Writes go into the module’s own memory, so the live root is writable and forgets everything on reboot.
  • When both a real disk and the live root match, the real disk wins, so an installed machine boots its installed system. root=live forces the live root instead.

If nothing matches, the kernel logs every partition it saw and falls back to a memfs root, which boots but keeps nothing. It never mounts an arbitrary partition just because one was there.

The kernel starts one process. bin/edos-init supervises edos-wm, edos-taskbar and edos-terminal: it spawns them, waits on them, and restarts them with backoff. Session policy lives in userspace, and a binary that fails to load is logged rather than panicking the kernel.

  • A bitmap frame allocator over the Limine memory map.
  • Per-process page tables, with the kernel mapped in the higher half of every address space.
  • VMAs tracked per thread, with demand paging: a mapping is a promise, and the frame arrives on the fault.
  • Copy-on-write fork.
  • TLB shootdown by IPI when a mapping that other CPUs may have cached changes.
  • Shared memory regions, which is how userspace window buffers reach the compositor.

User address space is reclaimed: find_free_address is a first fit over the VMA tree, so an mmap/munmap cycle returns the same address rather than walking forward forever.

Per-CPU run queues with work stealing, preemptive and timer-driven. Threads are either kernel threads, spawned by name at boot, or user threads belonging to a process.

ps shows both, with the CPU each one last ran on and the milliseconds it has accumulated:

The EDOS process table listing kernel threads and user processes with their state, CPU and accumulated time.

Two rules shape most of the kernel’s locking:

  • Nothing parks with preemption suppressed or interrupts disabled. thread_park, thread_sleep and thread_yield debug-assert on it.
  • Drop impls reachable from a dying thread never block. No disk I/O, no blocking mutex, no waiting. Work that must block is posted to a kernel thread instead; inode eviction is the canonical example.

Both are enforced in debug builds, and both exist because violating them produces hangs that are extremely hard to attribute after the fact. Lock order covers the third rule.

Entry is through SYSCALL/SYSRET. There are 102 syscalls, grouped by area: filesystem, I/O, memory, networking, shared memory, synchronisation, process control and windowing. The syscall reference lists every one with its arguments, its return value and the errors it can raise.

Rather than printf debugging, the kernel exposes counters through procfs:

Path What it shows
/proc/meminfo frames total, free and used; page size
/proc/processes every thread with its state, CPU and accumulated time
/proc/block_cache block page cache occupancy and hit rates
/proc/ahci_stats NCQ submissions, completions, and stalls
/proc/inflight_stats outstanding block I/O
/proc/evict_stats inode evictions, including ones dropped under pressure
/proc/lock_order_stats rank violations observed by the tracker
/proc/kernel the kernel log ring, which is what dmesg reads
/proc/cmdline the Limine command line