No description
  • C 83.1%
  • Assembly 11.3%
  • Makefile 5.6%
Find a file
2026-10-16 23:22:33 +01:00
include refactor: add arch interface and split riscv code from generic kernel 2026-10-16 18:10:42 +01:00
src feat(aarch64): add raspi3b target that boots to the prompt 2026-10-16 18:18:25 +01:00
targets feat(aarch64): add raspi3b target that boots to the prompt 2026-10-16 18:18:25 +01:00
.clang-format chore: make headers self-contained and apply clang-format 2026-10-16 17:00:48 +01:00
.gitignore chore: ignore compile_commands.json 2026-10-16 16:47:01 +01:00
current_plan.md feat(readme): refactor project overview & roadmap for shell-first focus 2026-09-26 01:26:32 +01:00
LICENSE GPLv3 2026-02-10 16:14:50 +00:00
Makefile feat(aarch64): add raspi3b target that boots to the prompt 2026-10-16 18:18:25 +01:00
README.md chore: update readme 2026-10-16 23:22:33 +01:00

SquidgeOS

A small bare-metal 64-bit kernel for RISC-V and ARM, focused on booting, debugging, and eventually a useful built-in command shell.

Project overview

SquidgeOS is a hobby kernel for exploring low-level systems programming. It boots on two architectures from one codebase: the CPU-specific code lives behind a small interface (include/arch.h), and everything above it is shared. QEMU is the immediate target for both; real hardware (Raspberry Pi 3 and StarFive VisionFive 2) comes later.

Targets

Target Architecture Machine Status
qemu-riscv64 (default) RISC-V 64 (M-mode) QEMU virt Boots to an interactive prompt: UART input, timer, PLIC interrupts
raspi3b AArch64 (EL1) QEMU raspi3b (Pi 3 Model B) Boots to the prompt; interrupts, timer and UART input not implemented yet
qemu-riscv64 raspi3b
Load address 0x80000000 (ELF) 0x80000 (raw kernel8.img)
Console NS16550A PL011
Interrupt controller PLIC not implemented (BCM2836 local + BCM2835 ARM controller)
Timer CLINT, 10 MHz not implemented (ARM generic timer)
Power off SiFive test device halts (wfi)

Requirements

  • clang, ld.lld and llvm-objcopy (one toolchain for both architectures)
  • make
  • qemu-system-riscv64 and qemu-system-aarch64

Building and running

make run                    # RISC-V on QEMU virt (default target)
make TARGET=raspi3b run     # AArch64 on QEMU raspi3b

make                        # build only -> output/qemu-riscv64/kernel.elf
make TARGET=raspi3b         # build only -> output/raspi3b/kernel8.img
make clean                  # remove build/ and output/ for every target

Quit QEMU with Ctrl-A then X.

Each target builds into its own build/<target>/ and output/<target>/, so both can exist side by side. Header dependencies are not tracked yet: run make clean after changing a header.

make also writes compile_commands.json (via clang -MJ) covering every target built so far, for clangd.

Source layout

include/arch.h          CPU interface used by generic code
src/
  arch/riscv64/         boot, trap entry, CSR helpers, CLINT timer
  arch/aarch64/         boot (EL2 -> EL1), vector table, system register helpers
  drivers/              ns16550, pl011, plic, syscon, and stubs (irq_none, power_halt)
                        uart.h / irq.h / power.h are the driver interfaces
  kernel/               kmain, console (kprintf, line input), panic, memory, timer ticks
  lib/                  string functions, ring buffer
targets/<target>/
  target.mk             architecture flags, drivers, image format, QEMU command
  linker.ld             load address and memory layout
  platform.h            device addresses and RAM size (until device tree support)

Generic code (kernel/, lib/, drivers/) contains no inline assembly and never includes anything from arch/.

Current state

Implemented

  • Boot on both targets; AArch64 parks the secondary cores, RISC-V assumes a single hart
  • Trap handling: RISC-V machine-mode traps, AArch64 exception vectors (unhandled exceptions panic with ESR/ELR/FAR)
  • Console output and kprintf on both targets
  • Interrupt-driven, line-buffered UART input on RISC-V
  • 1 ms timer tick on RISC-V
  • Page allocator and heap (kmalloc/kfree) with self-tests at boot
  • Panic handling (kpanic, KASSERT)

Not implemented yet

  • AArch64 interrupts, generic timer and UART input
  • Shell command parsing and built-in commands
  • Supervisor mode, virtual memory, scheduler, user mode

Roadmap

  1. Rewrite the RISC-V trap entry (macro-generated save/restore) and fix the trap frame layout
  2. Make the console ring buffer safe between the interrupt handler and the main loop
  3. RISC-V S-mode under OpenSBI (SBI timer, console and reset), as required by real RISC-V boards
  4. Shell milestone: parser, command table, and help, echo, clear, time, mem, panic, reboot
  5. Device tree support: memory size, timer frequency and device addresses from the DTB instead of platform.h
  6. AArch64 interrupts (BCM2836/BCM2835 controllers), generic timer and PL011 input on raspi3b
  7. Real hardware: Raspberry Pi 3, then StarFive VisionFive 2
  8. Later: virtual memory, scheduler, user mode and syscalls