The plan is for the kernel to be just a scheduler, IPC relay, a physical memory manager and a small virtual memory manager.
The system design steals borrows ideas from:
- Zircon: the reference counted capability model
- seL4: synchronous IPC endpoints and asynchronous signals
- Minix3: posix compat services, like process manager
- Plan9/RedoxOS: filesystem URI to reference different services, like fs:///etc/hosts, initfs:///sbin/init, tcp://10.0.0.1:80 or https://archlinux.org
zig build run # thats it
# read 'Project-Specific Options' from `zig build --help` for more options
zig build run -Dtest=true # include custom unit test runner
zig build # generates the os.iso in zig-out/os.iso
zig build run --prominent-compile-errors --summary none -freference-trace \
-Doptimize=Debug -Duefi=false -Ddebug=1 -Dgdb=false -Ddisplay=true -Dtest=true -Dcpus=1
- kernel: src/kernel
- kernel/user interface: src/abi
- root process: src/userspace/root
-
kernel
- PMM
- VMM
- PCID for better context switch performance
- Mapping cache modes with PAT, uncacheable, write-combining, write-through, write-protect, write-back and uncached
- GDT, TSS, IDT
- ACPI, APIC
- SMP
- HPET
- TSC
- I/O Privilege Bitmap for port io caps (IOPB)
- scheduler
- binary loader
- stack tracer with line info
- message IPC, shared memory IPC
- multiple parallel recvs to the same endpoint
- multiple parallel calls to the same endpoint
- figure out userland interrupts (ps2 keyboard, ..)
- capabilities
- free list of capabilities
- allocate capabilities
- deallocate capabilities
- map capabilities
- unmap capabilities
- send capabilities
- disallow mapping a frame twice without cloning the cap
- disallow overlapping maps
- restrict capability rights, ex: read-only frame can only create read-only frames
- scrap the derivation tree, use refcounts like Zircon
- per process handle tables, allowing more dynamic kernel object metadata (user handle (u32) is an index to process local table, which holds a pointer (and rights) to a kernel object)
- objects
- Thread
- Vmem
- Frame
- multiple Frame caps to the same physical memory (for shared memory)
- lazy alloc
- map into multiple
Vmem
s - copy on write
- use a multi-level tree to store the pages, like how hardware page tables do it
- custom Frames where the creator provides data on page faults and gets notified when all other handles are closed
- set_size
- Receiver
- Reply
- Sender
- Notify
- syscalls
- move all object methods to be syscalls
- syscall tracker
- method call tracker
-
user-space
- posix abstraction layer, like
open
instead of the manual ipc call - stack traces with line info
- posix abstraction layer, like
-
root + initfsd process
- decompress initfs.tar.zst
- server manifest embedded into the ELF
- name
- imports
- exports
- execute servers
- grant server imports and exports
- execute initfs:///sbin/init and give it a capability to IPC with the initfs
-
initfs:///sbin/pm server process
- handles individual processes and their threads
-
initfs:///sbin/vfs server process
- create fs://
- exec required root filesystem drivers
- read /etc/fstab before mounting root (root= kernel cli arg)
- mount everything according to /etc/fstab
- exec other filesystem drivers lazily
-
initfs:///sbin/init process
- launch initfs:///sbin/rngd
- launch initfs:///sbin/vfsd
- launch services from initfs://
-
initfs:///sbin/fsd.fat32
-
initfs:///sbin/rngd process
-
/sbin/outputd process
-
/sbin/kbd process
-
/sbin/moused process
-
/sbin/timed process
-
/sbin/fbd process
-
/sbin/pcid process
-
/sbin/usbd process
Approximate synchronous IPC performance: call
+ replyRecv
loop takes about 323ns (3 091 603 per second) (in QEMU+KVM with Ryzen 9 5950X):
// server
while (true) {
msg = try rx.replyRecv(msg);
}
// client
while (true) {
try tx.call(.{});
}