Preemptive Threads — Bare-Metal Multithreading in Rust
Designing and implementing a preemptive multithreading system on bare-metal ARM64, without an operating system.
Overview
Preemptive Threads is a low-level systems project exploring how preemptive multitasking can be implemented from first principles on bare metal hardware using Rust. The target platform is the Raspberry Pi Zero 2 W (ARM64), running with no OS, no standard library, and full control over interrupts, scheduling, and context switching.
The goal was not to build a toy scheduler, but to understand and implement the core mechanics behind real operating systems: timer interrupts, thread context management, and safe preemption under tight constraints.
The Challenge
Implementing preemptive multitasking on bare metal introduces several non-trivial problems:
No operating system abstractions to rely on
No standard library (#![no_std])
Interrupt handlers must safely switch execution contexts
ARM64 exception return (eret) restores CPU state automatically, making naïve context switching incorrect
Debugging happens close to the hardware, often through UART and QEMU
A particularly tricky issue was that ARM64 IRQ returns overwrite register state, which means switching stacks alone does not work. Context switching had to be designed with full awareness of the CPU exception model.
What I Built
Core Scheduling & Context Switching
Implemented preemptive context switching driven by timer interrupts (1ms tick)
Designed thread context structures to explicitly store and restore register state
Switched execution by loading thread contexts directly rather than relying on the IRQ stack
Built a priority-based scheduler with round-robin scheduling for equal priorities
Architecture-Specific Work
Wrote ARM64-specific interrupt and exception handling logic
Implemented vector tables and IRQ handlers for AArch64
Added support for both QEMU virt and real Raspberry Pi Zero 2 W hardware
Built custom linker scripts to control memory layout precisely
Tooling, Safety & Testing
Added fuzzing infrastructure for threading primitives
Built benchmarks to validate scheduler behavior
Created examples demonstrating multiple threads running concurrently on bare metal
Maintained strict separation between architecture-independent scheduling logic and architecture-specific code
Current Capabilities
Working
Preemptive multitasking
Timer-based scheduling (1ms resolution)
Priority scheduling with round-robin fairness
UART output for debugging
QEMU and real-hardware execution
Not Implemented (by design, yet)
Multi-core scheduling (currently single-core)
Memory protection
Thread join / exit semantics
Outcome
This project resulted in a fully functioning bare-metal preemptive scheduler running real concurrent threads on ARM64 hardware, written entirely in Rust without an operating system.
Beyond the implementation itself, it deepened my understanding of:
CPU exception models and interrupt mechanics
Low-level concurrency and scheduling
Systems design under extreme constraints
Writing safe, structured Rust in no_std environments
This project serves as a foundation for more advanced kernel-level work and demonstrates hands-on systems engineering beyond application-level development.