Skip to content

Repository files navigation

SimpleOS

An experimental operating system built from scratch in C and x86 assembly.

License: MITGitHub starsPRs Welcome

Star History

Star history chart for raintree-technology/SimpleOS

SimpleOS is a learning project — a from-scratch kernel that implements processes, virtual memory, syscalls, pipes, signals, a filesystem, and a shell, all in roughly 8,000 lines of code. It boots as a real ISO image via GRUB, and an optional web app can run the OS in-browser using the v86 x86 emulator.

This isn't a production operating system. It's a proof of concept and a learning tool — built to understand how operating systems actually work by writing one from the ground up.

Table of Contents

What's Inside

SubsystemKey filesWhat it does
Bootboot.s, grub.cfg, linker.ldMultiboot2 entry, GDT, stack setup, jumps to kernel_main
Kernel corekernel.cInitializes every subsystem, creates test processes, starts scheduler
Processesprocess.c, scheduler.cRound-robin preemptive scheduling (10-tick quantum), kernel threads and user processes, up to 64
Context switchingcontext_switch.sSaves/restores register state, interrupt-return trampoline for user mode
Memorypmm.c, vmm.c, kmalloc.cBitmap physical allocator, two-level paging with copy-on-write, free-list heap with block coalescing
Syscallssyscall.c23 syscalls via int 0x80 — fork, execve, wait, pipe, dup2, kill, signal, chdir, and more
IPCpipe.c, signal.c4 KB blocking circular pipes, Unix-style signals (SIGINT, SIGKILL, SIGSTOP, etc.)
Filesystemfs.cIn-memory ramfs — 64 nodes, 512KB of 512-byte blocks, pre-populated with /bin/*
Driversterminal.c, keyboard.c, timer.c, vt.cVGA text mode, PS/2 keyboard with Ctrl+C/Z, PIT timer, virtual terminal switching (Alt+F1–F4)
ELF loaderelf.cLoads 32-bit ELF binaries into user address space
User modeusermode.c, tss.cRing 0 → Ring 3 via IRET, TSS for kernel stack on interrupts
Userspaceshell.c, grep.c, wc.c, hello.cFreestanding programs built as ELF, converted to C byte arrays, embedded in the kernel image
Web demoEmulator.tsx, CRTFrame.tsxv86 emulator with asset preflight, CRT-styled terminal with animated ASCII background

Boot Flow

GRUB (Multiboot2)
→ _start src/arch/i386/boot.s load GDT, set up stack
→ kernel_main src/kernel/kernel.c init all subsystems
→ scheduler_enable src/kernel/scheduler.c start round-robin loop
→ idle process PID 0 runs when nothing else is ready

The kernel is linked at 1MB (linker.ld). The heap lives at 2–3.5MB. User processes get virtual addresses starting at 128MB, with stacks below 3GB.

Memory Layout

0x00100000 (1MB) Kernel code (.multiboot, .text, .data, .bss)
0x00200000 (2MB) Kernel heap start
0x00380000 (3.5MB) Kernel heap end
0x00400000 (4MB) Physical memory allocator starts here
0x04000000 (64MB) End of kernel identity-mapped window
0x08000000 (128MB) User virtual address space begins
0xBFFFF000 (~3GB) User stack top (grows down)
0xC0000000 (3GB) Kernel boundary

Syscalls

All syscalls use int 0x80 with arguments in eax (number), ebx, ecx, edx. Userspace wrappers live in ulib.h.

#NameDescription
1exitTerminate process
2writeWrite to fd (terminal, file, or pipe)
3readRead from fd
4getpidGet current PID
5sleepSleep N milliseconds
6sbrkGrow user heap
7forkFork process (COW page tables)
8waitWait for child, reap zombie
9execveLoad and run ELF binary
10psList all processes
11openOpen file
12closeClose fd
13statFile metadata
14mkdirCreate directory
15readdirRead directory entries
16killSend signal to process
17pipeCreate pipe pair
18dup2Duplicate fd
19signalInstall a user-mode signal handler
20sigreturnReturn from a signal handler (used by the trampoline)
21setfgSet the terminal foreground job (Ctrl+C/Z target)
22chdirChange working directory
23getcwdGet working directory

Shell

The shell supports:

  • Built-ins: help, ps, echo, cd, pwd, ls, cat, clear, history, jobs, fg, bg, kill, exit
  • External programs: /bin/hello, /bin/grep, /bin/wc (via fork + execve)
  • Pipes: echo hello | wc — multi-stage too (cmd1 | cmd2 | cmd3)
  • Redirection: cmd > file, cmd >> file (append), cmd < file
  • Background jobs: cmd &, then jobs, fg, bg
  • Job control: Ctrl+C (SIGINT), Ctrl+Z (SIGTSTP) routed to the foreground job
  • History: up/down arrows (10 entries)

Repository Layout

src/
arch/i386/ boot.s, asm_functions.s, context_switch.s, tss.c, usermode.c
boot/ exceptions.c (ISR/IDT setup)
kernel/ kernel.c, process.c, scheduler.c, syscall.c, panic.c
mm/ kmalloc.c, pmm.c, vmm.c
drivers/ terminal.c, keyboard.c, timer.c, ports.c, vt.c, serial.c
fs/ fs.c
ipc/ pipe.c, signal.c
lib/ elf.c, string.c
include/ headers mirroring src/ layout
userspace/ shell.c, grep.c, wc.c, hello.c, selftest.c, ulib.h, Makefile
boot/grub/ grub.cfg
web/ Next.js app (Emulator.tsx, CRTFrame.tsx, page.tsx)
scripts/ dev-shell.sh, setup-toolchain.sh, smoke-test.sh, selftest.sh
.devcontainer/ Dockerfile + devcontainer.json

Building

The kernel targets 32-bit freestanding x86. The Makefile cross-compiles with i686-elf-gcc and produces simpleos.iso via grub-mkrescue.

Userspace programs are compiled separately (userspace/Makefile), then converted to C byte arrays with xxd -i and #included into the kernel's filesystem init.

Dev Container (recommended)

Open the repo in VS Code, Cursor, or Zed — the devcontainer config has everything pre-installed.

make
cd web && npm install && npm run dev

Local Toolchain

See scripts/setup-toolchain.sh for install instructions. You need:

  • i686-elf-gcc / i686-elf-as
  • grub-mkrescue and xorriso
  • qemu-system-i386 (for local testing)
make # build simpleos.iso
make run # boot in QEMU

To build and boot just the kernel (no ISO/GRUB needed), use the Multiboot1 path:

make kernel.bin
qemu-system-i386 -kernel kernel.bin

Docker Build

./build.sh # builds simpleos.iso → web/public/os/

The Dockerfile extends a cross-compiler image with GRUB and xorriso. You can also get an interactive shell with scripts/dev-shell.sh.

Browser Demo

The web app boots the ISO in a v86 emulator with a CRT-styled UI. It expects three assets:

  • web/public/os/simpleos.iso — built by make or ./build.sh
  • web/public/bios/seabios.bin — SeaBIOS ROM
  • web/public/bios/vgabios.bin — VGA BIOS ROM

It also expects web/public/v86/libv86.js and web/public/v86/v86.wasm from an official v86 release. These third-party build artifacts and BIOS files are intentionally not checked in.

If anything is missing, the preflight check shows exactly what's absent instead of booting into a broken state.

cd web
npm install
npm run dev # starts on port 3500
npm run build:os # rebuild kernel and copy ISO in one step

Reading the Code

Start here:src/kernel/kernel.ckernel_main() initializes every subsystem in order and is the best map of how the pieces connect.

From there:

  1. Boot: boot.s → how the CPU gets from GRUB to kernel_main
  2. Interrupts: asm_functions.s → ISR stubs, IDT/GDT loading, paging enable
  3. Processes: process.c → PCB structure, creation, destruction
  4. Scheduling: scheduler.c → preemptive round-robin, context switch calls
  5. Memory: vmm.c → page tables, COW fork, address space management
  6. Syscalls: syscall.cint 0x80 dispatch, all 23 implementations
  7. Userspace: ulib.h → syscall wrappers that user programs call
  8. Shell: shell.c → pipes, redirection, job control in ~700 lines

Verification

The kernel can be exercised headlessly — COM1 serial mirrors all terminal output, and the kernel boots directly via its Multiboot1 header with qemu -kernel (no ISO/GRUB needed). Two scripts drive this:

  • scripts/smoke-test.sh — build, boot, and assert the kernel reaches full init without panicking.
  • scripts/selftest.sh — build a self-test kernel that runs userspace/selftest.c as init and verifies fork/wait, pipes (including blocking transfers larger than the buffer), signals (default actions and user-mode handlers), sbrk, BSS zeroing, O_APPEND, working-directory resolution, and the terminal's ANSI escape parser.

A separate verify_os.py checks code integrity without compiling — include dependencies, function signatures, syscall coverage, and common issues.

Contributing

Contributions are welcome!

  1. Fork the repo and create a branch: git checkout -b feat/your-feature
  2. Make your changes and ensure the build passes: make kernel.bin
  3. Run the checks: scripts/smoke-test.sh and scripts/selftest.sh (and python3 verify_os.py)
  4. Open a pull request with a clear description of what changed and why

Please follow the existing code style (K&R-adjacent C, descriptive names, comments on non-obvious logic). For larger changes, open an issue first to discuss the approach.

License

SimpleOS source is MIT licensed. Third-party tools and runtime assets used to build or run the project keep their own licenses.

About

Experimental Unix-like x86 operating system built from scratch in C and assembly.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages