A simple x86_64 operating system written in Rust.
Ralph OS is implemented entirely from scratch with no external crates. This includes:
- Custom bootloader (16-bit → 32-bit → 64-bit mode transitions)
- Custom serial driver (direct UART 16550 register access)
- Custom memory allocator
- All other OS components
Only Rust's core library is used. The alloc crate is enabled once we have a custom allocator.
- Target: x86_64 only
- Single core: No SMP support
- Cooperative multitasking: Tasks yield control voluntarily via
yield_now()
- Flat address space: No virtual memory, all tasks share the same linear address space
- Linked list allocator: Supports allocation and freeing (no paging)
- Identity mapped: Physical addresses = virtual addresses
- ELF executables can be embedded and spawned as tasks
- BASIC programs are embedded as faux-files (
bas/*.bas) and loaded viaLOAD "name"
- Serial output only: COM1 (0x3F8) via UART 16550
- Optional graphics: VGA mode 13h can be enabled (see
make run-vga*)
- NE2000 ISA NIC driver (IRQ 10, I/O base 0x300)
- TCP/IP stack: Ethernet, ARP, IPv4, ICMP, TCP - all from scratch
- Non-blocking sockets: Integrates with the cooperative scheduler
- Telnet server: Per-connection BASIC REPL on TCP port 23
- Custom two-stage bootloader written in assembly
- Stage 1: Boot sector (512 bytes), loads stage 2
- Stage 2: Sets up protected mode, long mode, page tables, loads kernel
- Identity-mapped page tables (physical = virtual addresses)
# One-time setup (installs Rust nightly and required components)
make setup
# Recommended: VGA + mouse + networking + port forwards
make run-vga-mouse-net- Rust nightly toolchain (with
rust-srcandllvm-tools-preview) - NASM assembler
- QEMU for x86_64 emulation
Install everything with:
# Install system packages (Ubuntu/Debian)
sudo apt install qemu-system-x86 nasm
# Install Rust tools (run once)
make setup| Command | Description |
|---|---|
make all |
Build bootable image (default) |
make build |
Build bootable image |
make bootloader |
Build bootloader only |
make kernel |
Build kernel only |
make programs |
Build all user programs |
make image |
Create bootable disk image |
make run |
Run (serial, no VGA) |
make run-net |
Run with NE2000 + port forwards |
make run-net-tap |
Run with TAP networking (ping support) |
make run-vga |
Run with VGA visualization |
make run-vga-mouse |
Run with VGA + mouse |
make run-vga-mouse-net |
Run with VGA+mouse+network |
make test-vga |
Smoke-test VGA via screenshot |
make debug |
Run with QEMU interrupt logging |
make gdb |
Run with GDB server (port 1234) |
make clean |
Remove build artifacts |
make setup |
Install required Rust tools (run once) |
make help |
Show Makefile help |
# Build bootable image
make image
# Run in QEMU
qemu-system-x86_64 \
-drive format=raw,file=target/ralph_os.img \
-serial stdio \
-display none \
-no-reboot# Terminal 1: Start QEMU with GDB server
make gdb
# Terminal 2: Connect with GDB
gdb -ex "target remote :1234"Ralph OS includes a TCP/IP network stack with:
- NE2000 NIC driver
- Ethernet, ARP, IPv4, ICMP, TCP protocols
- Non-blocking socket API for user programs
# Run with QEMU user-mode networking (no ping from host).
# Also forwards:
# - host tcp/2323 -> guest tcp/23 (telnet)
# - host tcp/8080 -> guest tcp/8080 (HTTP, if you run it)
make run-netEach telnet connection spawns its own BASIC REPL task.
telnet localhost 2323The repo includes a BASIC program that runs an HTTP server (bas/todo.bas). It is not auto-started; load it from the REPL:
> LOAD "todo"
> RUN
With make run-net / make run-vga-mouse-net, the host forwards localhost:8080 to the guest, so you can hit it from the host:
curl http://localhost:8080/Network configuration (QEMU user networking defaults):
- IP:
10.0.2.15 - Netmask:
255.255.255.0 - Gateway:
10.0.2.2
QEMU's user-mode networking doesn't forward inbound ICMP. To ping the VM, use TAP networking:
# 1. Create TAP interface (one-time setup, requires root)
sudo ip tuntap add dev tap0 mode tap user $USER
sudo ip addr add 10.0.2.1/24 dev tap0
sudo ip link set tap0 up
# 2. Run Ralph OS with TAP networking
make run-net-tap
# 3. In another terminal, ping the VM
ping 10.0.2.15You should see output like:
[icmp] Echo request from 10.0.2.1 seq=1
[icmp] Sent echo reply to 10.0.2.1 seq=1
To clean up the TAP interface:
sudo ip link set tap0 down
sudo ip tuntap del dev tap0 mode tapralph_os/
├── bootloader/
│ ├── stage1.asm # Boot sector (512 bytes, 16-bit)
│ └── stage2.asm # Mode transitions (16→32→64-bit)
├── bas/ # Embedded BASIC programs (*.bas)
├── src/
│ ├── main.rs # Kernel entry point
│ ├── serial.rs # Custom UART 16550 driver
│ ├── allocator.rs # Linked list heap allocator
│ ├── scheduler.rs # Cooperative task scheduler
│ ├── api.rs # Kernel API for programs
│ ├── telnet.rs # Telnet server spawning BASIC sessions
│ ├── basic/ # BASIC interpreter
│ └── net/ # TCP/IP network stack
│ ├── ne2000.rs # NE2000 NIC driver
│ ├── tcp.rs # TCP state machine
│ └── ... # Ethernet, ARP, IPv4, ICMP
├── programs/ # User programs (compiled to ELF)
├── Cargo.toml # Project manifest (no dependencies!)
├── Makefile # Build commands
├── kernel.ld # Kernel linker script
├── x86_64-ralph_os.json # Custom target specification
├── .cargo/config.toml # Build configuration
├── README.md # This file
├── ARCHITECTURE.md # Technical architecture documentation
└── run.sh # Build and run script
See ARCHITECTURE.md for detailed technical documentation.