Skip to content

Repository files navigation

Viper OS

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.


Boot Ownership

           Power On
               │
               ▼
      UEFI Firmware
               │
               │ Loads BOOTX64.EFI ()
               ▼
      Kernel Entry (Ring 0)
               │
               │ ExitBootServices()
               ▼
        Viper Kernel
               │
               │ spawn()
               ▼
      User Process (Ring 3)

Control changes ownership only twice:

  1. Firmware → Kernel via ExitBootServices()
  2. Kernel → User Process via iretq .

Everything else happens while the kernel remains fully in control.s


Boot Timeline

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

Boot Stages

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.

Interrupt Architecture

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.


About

A tiny OS, built from scratch

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages