Architecture

zuzu is a microkernel. The kernel runs at PL1 (SVC mode) and does the minimum necessary to support isolated userspace processes.

In the kernel (PL1)In userspace (PL0)
Address space managementDevice drivers (zuart, zusd)
Thread and process schedulingFilesystem server (fat32d, fbox)
IPC message passingNetwork stack (Phase 22+)
Interrupt forwardingInit / service registry (sysd)
Minimal fault handlingShell (zzsh)

Memory map

0xFFFFFFFF ┌─────────────────────┐
           │   Kernel stacks     │
0xF0100000 ├─────────────────────┤
           │   MMIO region       │  (ioremap area)
0xF0000000 ├─────────────────────┤
           │   (unmapped guard)  │
           ├─────────────────────┤
           │   Kernel BSS/data   │
           │   Kernel text       │
0xC0000000 ├─────────────────────┤  ← KERNEL_VA_BASE
           │                     │
           │   User space        │  (per-process, switched via TTBR0)
           │   User stack ↓      │
           │   User heap ↑       │
           │   User text/data    │
           │                     │
0x00001000 ├─────────────────────┤
           │   NULL guard page   │
0x00000000 └─────────────────────┘

The ARM MMU uses two translation table base registers simultaneously. TTBR1 holds the kernel's L1 table and never changes. TTBR0 holds the current process's L1 table and is swapped on every context switch. The split boundary (TTBCR.N = 1) sits at 0xC0000000.


Server hierarchy

sysd (PID 1)  ── name server + service registry
  │
  ├── devmgr (PID 2)  ── hardware authority, device enumeration
  │     ├── zuart  ── PL011 UART driver
  │     └── zusd   ── PL181 SD card driver
  │
  ├── fat32d  ── FAT32 filesystem (talks to zusd via IPC)
  ├── fbox    ── VFS IPC layer (talks to fat32d)
  │
  └── zzsh    ── shell

Processes discover each other through sysd's name table. A server registers its port under a name; clients look it up at startup. No hardcoded port numbers anywhere in userspace.


Key design decisions

SVC immediate as syscall number

The syscall number is encoded in the lower 8 bits of the SVC #n immediate field rather than a register (Linux uses r7). Arguments pass in r0–r3 per AAPCS — no register shuffling required at the call site. This commits the ABI to ARM mode; Thumb SVC has only 8 usable bits and would need different extraction logic.

TTBR0/TTBR1 split

Kernel mappings live in TTBR1 and are always accessible regardless of which process is running. TTBR0 switches per context switch. This eliminates per-process kernel mapping copies and makes the kernel permanently visible without TLB pressure.

Synchronous rendezvous IPC

send blocks until a receiver is ready; recv blocks until a sender arrives. When both are present, the kernel copies four registers (r0–r3) and unblocks both. No message queues, no heap allocation in the IPC fast path. This is the same model used by L4, seL4, and QNX Neutrino.

IPC as mutex

Synchronous IPC naturally serializes access to server state. A single-threaded server process cannot be re-entered while handling a request, which eliminates the need for explicit locking in most server designs. This mirrors QNX Neutrino's architecture.

Capability model

Userspace never sees raw kernel pointers. Every kernel object (port, notification, shared memory region, thread) is referenced by a slot index into a per-process handle table. Possession of a valid handle slot equals permission. Userspace only ever sees integers.