zOS is a small educational kernel written in Zig, targeting RISC-V (RV32). It is designed to be simple, readable, and hackable, showing how to bring up a system from bare metal all the way to handling traps and making SBI calls.
- Show how to write a tiny kernel\
- Be easy to read, learn from, and experiment with\
- Provide a minimal but real trap pipeline and allocator\
- Demonstrate using Zig in a bare-metal environment\
- Zig --- low-level programming without fear
- RISC-V / RV32 --- clean and open architecture
- OpenSBI --- standard interface for firmware/syscalls
- QEMU --- fast virtual RISC-V environment
- Custom linker script sets the entry symbol (
boot) - First code executed is
_start()function - Stack pointer is initialized manually
- Control is transferred cleanly to
kernelMain()
- Minimal
putchar()implemented through OpenSBI ecall - A tiny, compile-time-formatted
printf()+log()implementation - Prints hex/decimal/string without relying on the standard library
- A fully custom trap vector:
trap_vector.Saligns the handler correctly- Jumps into a Zig
kernelEntryfunction
kernelEntry:- Saves all registers manually
- Calls
handleTrapwith a well-definedTrapFrame - Restores registers and executes
sret
- Trap debugging includes:
scausestvalsepc
- Extremely simple linear allocator:
- Allocates whole pages
- Zeros allocations with
memset - Pointer range validated using linker-defined symbols:
__free_ram__free_ram_end
- Helpful for early boot and experimentation
The linker script defines ENTRY(boot), and the .text.boot section
contains:
pub export fn _start() linksection(".text.boot") callconv(.naked) noreturn {...}This function:
- Initializes
next_addrfor the allocator - Sets up the stack pointer
- Jumps to
kernelMain
Inside kernelMain:
- Install trap vector (
stvec) - Test allocator (
allocPages) - Print system state
- Enter low-power
wfiloop
Exception → stvec register → trap_vector.S → kernelEntry → handleTrap()
- Fully manual context save & restore
- Clean C-ABI trap handler in Zig
- Prints a panic with CSR information
The linker defines:
[text | rodata | data | bss] → stack → RAM (64 MB)
Key exported symbols:
__stack_top # initial stack pointer
__free_ram # start of free RAM allocator
__free_ram_end # allocator limit
You can run the kernel under QEMU: ./run.sh
Ensure OpenSBI firmware is present (default QEMU uses one automatically).
[*] starting kernel...
[*] trap vector address: 0x80203000
[*] allocPages test: paddr0=0x80220000
[*] allocPages test: paddr1=0x80222000
[*] continuing execution...And if something traps:
[*] trap scause=0x00000002, stval=0x00000000, sepc=0x80200420
[*] in: kernel.zig:123This project is licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.