An experimental, tiny OS built from scratch.
Supported:
- x86-64 UEFI
- RISCV & ARM - coming soon
This doc describes exactly what exists in memory, who owns CPU ticks & how execution moves from UEFI firmware to unprivileged user space.
Power On
│
▼
UEFI Firmware
│
│ Loads BOOTX64.EFI ()
▼
Kernel Entry (Ring 0)
│
│ ExitBootServices()
▼
Viper Kernel
│
│ spawn()
▼
User Process (Ring 3)
Control changes ownership only twice:
- Firmware → Kernel via
ExitBootServices() - Kernel → User Process via
iretq.
Everything else happens while the kernel remains fully in control.s
Firmware
│
├── Load PE32+ EFI executable
│ RCX = ImageHandle
│ RDX = SystemTable
│
├── Locate GOP (as opposed to vesa)
├── Read memory map
├── Save framebuffer information
│
└──────────── ExitBootServices ────────────
(Point of no return)
│
▼
Kernel
│
├── Install/Configure GDT mapping
├── Reload segment registers
├── Install IDT
├── Generate ISR stubs
├── Configure PIC
├── Configure PIT
├── Enable keyboard
├── Create bitmap allocator
├── Build page tables
├── Load CR3
├── Enable interrupts
├── Install TSS
├── Configure SYSCALL MSRs
└── Spawn first process
│
▼
iretq
│
▼
Ring 3 begins
| Stage | Result |
|---|---|
Firmware loads BOOTX64.EFI |
EFI maps the PE32+ executable and calls efi_entry(). |
| Collect boot information | Locate the Graphics Output Protocol, read the memory map and save the framebuffer. |
| ExitBootServices() | Firmware permanently relinquishes control. Boot Services disappear forever. |
| Install the GDT | Load the Global Descriptor Table and reload segment registers. |
| Install the IDT | Generate 48 interrupt stubs and load the Interrupt Descriptor Table. |
| Configure hardware | Remap the PIC, configure the PIT at 100 Hz and enable keyboard IRQs. |
| Create virtual memory | Build page tables, identity-map the first 4 GiB using 2 MiB pages and load CR3. |
| Enable interrupts | Execute sti; timer interrupts begin immediately. |
| Configure privilege transitions | Install the TSS and configure STAR/LSTAR/FMASK. |
| Spawn the first process | Construct a synthetic interrupt frame and enter Ring 3 using iretq. |
Every exception & hardware interrupt produces exactly the same stack layout for now:
High Addresses
──────────────────────────────
SS
RSP
RFLAGS
CS
RIP
Error Code
Vector Number
──────────────────────────────
R15
R14
R13
R12
R11
R10
R9
R8
RDI
RSI
RBP
RBX
RDX
RCX
RAX
──────────────────────────────
Low Addresses (RSP)
The CPU pushes:
- RIP
- CS
- RFLAGS
- RSP
- SS
- Error Code (only for certain exceptions)
The ISR stub then pushes:
- Interrupt vector
- Dummy error code (when required)
- All fifteen general-purpose registers
Every interrupt therefore produces the same structure.
isr_dispatch() always receives a single pointer regardless of the interrupt source.