Skip to content

Repository files navigation

AcheronOS

A bare-metal AArch64 Unix-style operating system for Raspberry Pi 5 hardware and QEMU, with user/kernel isolation, virtual memory, copy-on-write fork, ELF userspace, an inode VFS, POSIX-style signals, threads, memory-mapped device I/O, and graphical terminals.

Documentation website: https://acheron-systems.github.io/acheron-os-docs-website/

Table of Contents


Overview

AcheronOS is a Unix-inspired operating system written from scratch for AArch64. It runs on Raspberry Pi 5 hardware and under QEMU's Raspberry Pi 3B emulator, combining bare-metal hardware bring-up with realistic OS mechanisms: virtual memory, multitasking, persistent storage, a filesystem-backed /bin, and an interactive userspace shell.

The project is intentionally educational rather than a complete POSIX implementation. It focuses on the core mechanics that make Unix-style systems understandable: processes own resources, threads are scheduled, files and devices share descriptor paths, page faults drive memory behavior, and userspace reaches the kernel through a narrow syscall ABI.


Current Feature Status

Area Implemented
Architecture Bare-metal AArch64 kernel, SMP boot path, EL1/EL0 isolation, traps, IRQs, syscalls, timer preemption
Processes fork, exec, waitpid, exit, process groups, zombies, orphans, job control
Scheduling SMP-aware multi-priority round-robin scheduling, per-CPU current/idle state, timer interrupts, blocking sleep, thread wakeups
Virtual Memory Per-process page tables, page faults, lazy allocation, demand paging, mmap, copy-on-write
ELF Loading Runtime exec, ELF validation, argument stack setup, lazy PT_LOAD paging from /bin
Filesystem Inode filesystem, directories, symlinks, permissions, VFS, file descriptors, block/inode caches
Virtual Filesystems procfs, devfs, device nodes, mount-table reporting
IPC Pipes, POSIX-style signals, process-group signal delivery, SIGCHLD
Threads Kernel/user threading support, mutexes, semaphores, condition variables
Drivers / Devices Memory-mapped hardware drivers, UART, SD/block device support, framebuffer, TTY backends, fan
Terminal / GUI UART TTY, framebuffer graphical terminal, multi-terminal support, raw/canonical mode
Userspace ELF executables, shell, user libraries, Unix-style commands, seeded /tests scripts, cpubusy, and editor utilities

Why This Was Hard

This project goes beyond a toy kernel or emulator-only OS. Raspberry Pi 5 support was especially difficult because the board moved to the BCM2712 SoC and the newer RP1 peripheral controller. That changes the practical bring-up path for almost every hardware-facing driver compared with older Raspberry Pi boards.

The RP1 peripherals are partially documented, but there is very little dedicated bare-metal reference material for the Raspberry Pi 5 firmware and hardware handoff path. Even getting UART working required finding the right MMIO addresses, initialization order, interrupt routing behavior, and device-tree assumptions with almost no direct examples to follow.

Much of the hardware work came from reverse engineering behavior by reading the Linux kernel source, comparing it against device-tree data and available register documentation, and then deriving the minimal sequence our kernel needed. We had to use Linux as a map without copying Linux's architecture, because our OS has a much smaller scope and different tradeoffs.

The software stack was also built from scratch. Other than fixed-width integer types from stdint.h, the kernel does not rely on a standard library or imported runtime. That meant writing our own boot path, memory routines, allocators, scheduler, page-table management, filesystem, syscall layer, userspace loader, shell support, synchronization primitives, and device abstractions.

This made implementation more than a matter of following existing kernel formatting. For each subsystem, we had to research how mature systems solve the problem, understand the tradeoffs in the Linux source and other references, and then design a smaller version that fit our goals: understandable, educational, and still realistic enough for features like fork, copy-on-write page faults, filesystem-backed exec, file descriptors, pipes, signals, process groups, job control, and waitpid to interact correctly.

The result is a full vertical stack: boot code, kernel, drivers, memory manager, filesystem, syscall layer, userspace runtime, shell, and applications, all brought up on real Raspberry Pi 5 hardware and QEMU.


Project Scope

This repository contains the kernel, userspace runtime, shell commands, architecture documentation, and API references for OS-PI-is-cool. The project is designed to be understandable as a complete operating-system codebase while still implementing realistic Unix-style mechanisms such as process isolation, filesystem-backed execution, signals, pipes, and persistent storage.


Project Goals

The primary goals of this operating system are:

  • Build a complete Unix-inspired operating system from scratch
  • Develop every major kernel subsystem without relying on an existing OS
  • Emphasize clean architecture and readable code
  • Demonstrate modern operating-system concepts through practical implementation
  • Provide comprehensive documentation for every major subsystem

The result is an educational operating system that implements many of the mechanisms found in traditional Unix kernels while remaining approachable enough to understand as a complete codebase.


Design Philosophy

Several principles guide the design of the project.

  • Keep the architecture modular. Each subsystem has well-defined responsibilities.
  • Follow Unix ideas where practical. Processes, files, permissions, pipes, and signals all follow familiar Unix semantics.
  • Prefer correctness over optimization. Clarity and maintainability take precedence over micro-optimizations.
  • Document every subsystem. Every major component links to a dedicated design document describing both implementation and rationale.
  • Develop incrementally. Features are built one subsystem at a time rather than all at once.

What Makes It Unix-style

The operating system adopts many of the classic Unix abstractions.

  • Process-based execution model
  • fork() / exec() process creation
  • POSIX-style signal handling
  • Hierarchical inode-based filesystem
  • User and kernel privilege separation
  • Virtual memory with process isolation
  • Pipes for interprocess communication
  • Permissions and ownership
  • Shell with job control
  • Small userspace utilities

While not a complete POSIX implementation, the system intentionally mirrors familiar Unix behavior whenever practical.


What Is Intentionally Simplified

The OS is designed as an educational Unix-style kernel rather than a production replacement for Linux. Some production-scale features are intentionally out of scope:

  • One global SMP scheduler queue rather than per-CPU run queues, CPU affinity, load balancing, or scheduler IPIs
  • No networking stack yet
  • Focused hardware support for the devices needed to boot, interact with, render output, and persist data
  • ext2-inspired educational filesystem rather than a fully POSIX-compliant production filesystem
  • Simple, readable subsystem designs over highly optimized production algorithms

These tradeoffs keep the full OS understandable while still implementing the core mechanisms of a Unix-style kernel.


Other Documentation Files

General Docs

Document Scope
About Us Project authorship, collaboration model, and room for a deeper team writeup.
Quickstart Guide Build, rebuild, Raspberry Pi 5 boot, and QEMU boot instructions.
Demo Guide Demo workflow and commands to show the OS running.
Testing and Validation Smoke tests, manual validation flows, and debugging interfaces.

Architecture Docs

Document Scope
Architecture Boot flow, linker layout, platform split, EL1/EL0 boundary, IRQs, timers, traps, and syscalls.
Filesystem Architecture Inode filesystem, VFS, open-file table, caches, permissions, and disk layout.
Processes Architecture Scheduler, trap-frame return path, context switching, fork, exec, process groups, zombies/orphans, waitpid, sleep blocking, multithreading, synchronization, isolation.
ELF Loading Architecture Runtime exec, ELF validation, argument stack setup, page-table replacement, lazy segment loading, and demand paging from /bin.
Signals Architecture Kernel signal delivery, masks, pending sets, default actions, process groups, job-control signals, SIGCHLD, and scheduler delivery checkpoints.
Userspace Architecture Userspace build pipeline, linker scripts, embedded ELF blobs, EL0 isolation, init, shell, and user libraries.
Memory Architecture Virtual memory, per-process page tables, lazy allocation, demand paging, page fault handling, copy-on-write, and allocators.
Device Drivers Architecture Block devices, SDHCI, UART, char devices, TTY backends, framebuffer terminal, pipes, fan, and driver init order.

API Docs

Document Scope
Syscall API Reference Raw syscall table with SVC numbers and brief syscall notes.
Userspace API Reference Userspace library functions, shell helpers, and command mini man pages.
Procfs API Reference /proc files, generated fields, and mount-table reporting.
Signals API Reference Signal ids, default dispositions, masks, sigaction, and signal helper behavior.

About Us

OS-PI-is-cool was developed by Veer Kakar and Matthew Karounos.

See this dedicated project-ownership page for more information. That page is intentionally structured as a place to add more depth later.


Kernel Space vs User Space

The operating system follows a traditional split between privileged kernel code and isolated user processes.

Kernel Space

Kernel responsibilities include:

  • Scheduler
  • Virtual memory manager
  • Process management
  • Interrupt and exception handling
  • System call dispatcher
  • Filesystem
  • Device drivers
  • Pipes
  • Signal delivery
  • Terminal drivers
  • ELF loading

User Space

User space contains:

  • Shell
  • Core command-line utilities
  • User libraries
  • ELF executables
  • Test programs
+----------------------------+
|       User Programs        |
|  shell • ls • cat • grep   |
+----------------------------+
|      System Call API       |
+----------------------------+
|          Kernel            |
| Scheduler • VM • FS • IPC  |
| Drivers • Signals • TTY    |
+----------------------------+
| Raspberry Pi Hardware      |
+----------------------------+

Why Raspberry Pi 5 + QEMU Pi 3B

Development targets two complementary platforms.

Raspberry Pi 5

The Raspberry Pi 5 provides modern ARM64 hardware for running the operating system on real hardware with the supported UART, USB HID keyboard, framebuffer, interrupt, fan, and SD-backed storage paths.

QEMU Raspberry Pi 3B

QEMU enables rapid development, debugging, and automated testing without requiring physical hardware.

Supporting both platforms makes development significantly faster while ensuring the kernel also runs correctly on real hardware.


Major Accomplishments

Major completed subsystems include:

  • Full virtual memory implementation
  • Copy-on-write fork()
  • ELF executable loading
  • Preemptive multitasking
  • Process groups and job control
  • POSIX-style signals
  • Persistent inode-based filesystem
  • Virtual filesystem layer
  • Demand paging
  • Lazy page allocation
  • Anonymous and file-backed mmap() region tracking
  • SMP multicore boot and scheduler execution
  • Seeded shell smoke tests under /tests
  • cpubusy CPU-load utility for multicore and stress demos
  • Graphical framebuffer terminal
  • Raspberry Pi 5 RP1 USB HID keyboard input with connect/reconnect support
  • Interactive shell with userspace commands

Future Enhancements

Planned or in-progress areas:

  • TCP/IP networking stack
  • CPU affinity, load balancing, and inter-processor interrupts
  • More complete POSIX userspace APIs
  • GUI desktop environment on top of the framebuffer terminal system
  • On-device C toolchain or small C-like compiler for writing userspace programs inside the OS
  • Package-management or search tooling for discovering and installing userspace programs

Major Features

Every subsystem has a dedicated design document located in docs/.

  • Bare-metal AArch64 kernel
  • Raspberry Pi 5 support
  • QEMU Raspberry Pi 3B support
  • EL1 kernel / EL0 userspace
  • SMP secondary-core release and per-CPU kernel state
  • Interrupt and exception handling
  • System calls
  • Timer-driven preemption
  • Software timers and sleep()
  • Error handling and fatal exception policy
  • UART console
  • SD card persistence
  • Multi-priority round-robin scheduler
  • One global scheduler queue protected by a scheduler spinlock
  • Per-CPU current thread, idle context, and scheduler timer state
  • End-to-end trap frame, scheduler interrupt, context switch, and EL0 return path
  • fork() with Copy-on-Write
  • exec() process replacement
  • Process groups
  • Zombie and orphan handling
  • waitpid()
  • Timer-backed sleep blocking
  • Multithreading and Synchronization
  • Filesystem-backed exec()
  • AArch64 ELF validation
  • argc/argv stack construction
  • Fresh TTBR0 page-table replacement
  • Lazy PT_LOAD segment registration
  • Demand paging executable pages from /bin
  • Init and shell command integration
  • POSIX-style signal actions and masks
  • Process-level and thread-level pending signals
  • Process-group signal delivery
  • SIGCHLD and waitpid() wakeups
  • TTY job-control signals
  • Scheduler checkpoint delivery
  • Virtual memory
  • Page tables
  • Lazy stack allocation
  • Lazy heap allocation
  • Demand paging
  • Page fault handling
  • Process isolation
  • Copy-on-Write
  • Kernel memory allocator
  • mmap()
  • ext2-inspired inode filesystem
  • Directories
  • Hard and symbolic links
  • Open-file table
  • Virtual filesystem layer
  • procfs and devfs root virtual filesystems
  • Character devices
  • LRU block cache
  • Inode cache
  • Permissions
  • Memory-mapped hardware driver implementation
  • Block-device and SDHCI support for persistent storage
  • UART driver and interrupt-driven input
  • Character-device layer for TTY backends
  • UART TTY and framebuffer graphical TTY
  • Multi-terminal support with raw and canonical modes
  • Raspberry Pi 5 fan/device support
  • Device initialization order and kernel driver registration
  • Interactive shell

  • Job control

  • Userspace ELF build and /bin seeding

  • Seeded executable shell tests under /tests

  • cpubusy for timed or infinite CPU-bound workloads

  • Core Unix-style commands including:

    • cat
    • ls
    • grep
    • kill
    • ln
    • readlink
    • sleep
    • vim style text editor
    • wc
    • and other small utilities

Project tree

.
├── Makefile                       -- Cross-build, userspace ELF, QEMU, and install targets
├── README.md                      -- Project overview and repository map
├── build_to_sd                    -- Helper script for SD-card deployment
├── config.txt                     -- Raspberry Pi boot configuration
├── linker.ld                      -- Kernel linker script for supported platforms
├── user
│   ├── user_boot.S                -- EL0 userspace entry bootstrap
│   ├── user_bins.h                -- Embedded userspace binary table interface
│   ├── user_linker.ld             -- Userspace ELF linker script
│   ├── linker.ld                  -- Alternate userspace linker script
│   ├── lib                        -- Userspace syscall wrappers and libc-style helpers
│   │   ├── errno.c/h              -- Errno names, messages, and printing
│   │   ├── fs_syscall.h           -- Filesystem syscall wrappers and constants
│   │   ├── malloc.c/h             -- Userspace heap allocator and memory helpers
│   │   ├── signals.h              -- Signal wrappers, constants, and sigaction types
│   │   ├── stdio.c/h              -- printf and puts
│   │   ├── string.c/h             -- Minimal string and parsing helpers
│   │   ├── syscall.c/h            -- Base syscall wrappers and process helpers
│   │   ├── tests.c/h              -- Userspace smoke tests
│   │   └── tty_syscall.h          -- TTY and alternate-screen wrappers
│   └── cmds                       -- Statically linked userspace commands
│       ├── shell.c/h              -- Interactive shell
│       ├── shell                  -- Shell parser, jobs, vectors, and I/O helpers
│       └── *.c                    -- cat, chmod, clear, cp, echo, grep, ls, vim, wc, etc.
├── kernel
│   ├── boot.S                     -- Kernel assembly entry point
│   ├── kernel.c                   -- Kernel C entry point
│   ├── errno.h                    -- Kernel errno values
│   ├── string.c/h                 -- Kernel string helpers
│   ├── data-structs               -- Hash map, linked list, ring buffer, and vector
│   ├── devices                    -- Device registry and TTY devices
│   ├── disk                       -- Block-device and SDHCI support
│   ├── fan                        -- Raspberry Pi 5 fan support
│   ├── fs                         -- Filesystem, VFS, procfs, ELF loader, and file table
│   │   ├── caches                 -- Inode cache and LRU block cache
│   │   ├── cmds.c/h               -- Filesystem commands called by syscalls
│   │   ├── dirs.c/h               -- Directory operations
│   │   ├── disk.c/h               -- Filesystem disk layout and mount support
│   │   ├── elf_loader.c/h         -- Userspace ELF loading
│   │   ├── errors.c/h             -- Filesystem error handling
│   │   ├── inodes.c/h             -- Inode operations
│   │   ├── kapi.c/h               -- File-descriptor kernel API
│   │   ├── oft.c/h                -- Open-file table
│   │   ├── devfs.c/h              -- Devfs virtual device nodes
│   │   ├── procfs.c/h             -- Procfs virtual files
│   │   ├── symlink.c/h            -- Symbolic-link fops and readlink support
│   │   ├── types.h                -- Filesystem types
│   │   └── virtual_fs.c/h         -- Virtual filesystem routing
│   ├── gui                        -- Framebuffer GUI and terminal rendering
│   │   └── tty_gui_device.c/h     -- Registered framebuffer TTY backend char driver
│   ├── irq                        -- Interrupt controller logic
│   ├── memory                     -- MMU, kmalloc, user allocator, mmap, and page tables
│   │   ├── mmap.h                 -- mmap protection and mapping flags
│   │   └── page_table             -- Page-table construction and lookup helpers
│   ├── pipe                       -- Pipe implementation
│   ├── scheduler                  -- Process, thread scheduling, and context switch
│   ├── signals                    -- Kernel signal delivery
│   ├── syscall                    -- Syscall dispatcher and /proc syscall formatting
│   ├── threading                  -- Threads, mutexes, semaphores, and condition variables
│   ├── timer                      -- Timer ticks, sleeps, and delays
│   ├── traps                      -- Exception vectors and trap handling
│   └── uart                       -- UART drivers and kernel printf
│       └── uart_device.c/h        -- Registered UART backend char driver
└── docs
    ├── quickstart.md              -- Build and boot instructions
    ├── demo.md                    -- Demo workflow notes
    ├── testing.md                 -- Smoke tests, manual validation, and debugging interfaces
    ├── architecture               -- Subsystem architecture documents
    │   ├── architecture.md        -- Hardware, boot, linker layout, traps, IRQs, timers, and syscalls
    │   ├── device-drivers.md      -- Block devices, char drivers, UART, TTY, TTYGUI, pipes, and fan
    │   ├── elf-loading.md         -- Runtime exec, ELF validation, stack setup, and lazy segment paging
    │   ├── filesystem.md          -- Inodes, VFS, mkfs, mount, caches, permissions, and dev nodes
    │   ├── memory.md              -- MMU, page tables, page faults, COW, and allocators
    │   ├── processes.md           -- Scheduler, context switching, fork, exec, waitpid, sleep, and resources
    │   ├── signals.md             -- Signal delivery, masks, process groups, job control, and SIGCHLD
    │   └── userspace.md           -- Userspace build, linker scripts, ELF embedding, init, shell, and libs
    └── api-docs
        ├── procfs-api.md          -- Procfs file reference and output fields
        ├── signals-api.md         -- Signal ids, defaults, masks, sigaction, and signal helpers
        ├── syscall-table.md       -- Raw syscall/SVC reference
        └── user-api.md            -- Userspace library and command reference

License

This project is protected under the MIT License. For more details, please look at LICENSE.md

About

A bare-metal AArch64 Unix-style operating system for Raspberry Pi 5 hardware and QEMU, with user/kernel isolation, virtual memory, copy-on-write fork, ELF userspace, an inode VFS, POSIX-style signals, threads, memory-mapped device I/O, and graphical terminals.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages