RISC-V · QEMU · BARE METAL

RISC-V Bare-Metal
on QEMU

Four complete projects — from a 5-instruction reset vector to a 4-hart SMP payload running on real OpenSBI firmware. Full source, Eclipse-debuggable, documented line by line.

About This Repository

Four self-contained RISC-V bare-metal projects that run on QEMU virt. Each project builds on the previous, progressing from raw M-mode assembly to a full S-mode payload running under production OpenSBI firmware. No OS. Fully debuggable in Eclipse CDT.

── QEMU virt boot chain ────────────────────────────────────────────────────
0x1000 MROM reset vector (5 instructions, hardcoded by QEMU)
↓ loads kernel_entry from 0x1018, jumps to it
0x80000000 Project 1: _start → main() (-bios none, pure M-mode)
── or ──
0x80000000 Project 2: zsbl.S (M-mode hand-written SBI, -bios zsbl.elf)
↓ mret with MPP=S
0x80200000 Project 2: supervisor_main() (S-mode, CLINT legacy SBI timer)
── or ──
0x80000000 Project 3: fw_jump.elf (real OpenSBI built from source, -bios)
↓ mret with MPP=S
0x80200000 Project 3: payload_main() (S-mode, SBI BASE queries, stimecmp timer)
── or ──
0x80000000 Project 4: fw_jump.elf (OpenSBI v1.8, 4 harts, -smp cpus=4,cores=2,threads=2)
↓ mret with MPP=S (non-deterministic boot hart, atomic lottery)
0x80200000 Project 4: _start → primary_main() + secondary_main() × 3 (SMP, HSM, IPI, per-hart timers)

Bare Metal QEMU

Simplest possible RV64I bare-metal

  • 3 files: main.S, linker.ld, Makefile
  • Stack setup, arithmetic (10+20=30), 5-iteration loop
  • Eclipse CDT GDB Hardware Debug step-through
  • No C runtime, no OS, no OpenSBI
RV64I M-mode -bios none QEMU virt

ZSBL + Supervisor

Two-stage boot with SBI timer interrupts

  • 6 files: assembler ZSBL + C supervisor
  • M-mode → S-mode transition via mret
  • Hand-written SBI ecall (legacy SET_TIMER 0x00)
  • 3 timer interrupts via CLINT + STIP injection
RV64IMAC M+S-mode SBI legacy CLINT

OpenSBI Payload

Real OpenSBI + SBI v1.0 S-mode demo

  • Build OpenSBI v1.8 from source (riscv64-linux-gnu-)
  • SBI BASE queries: spec version, impl, extension probe
  • SBI TIME extension (0x54494D45) — correct SBI v1.0 API
  • sstc: write stimecmp directly from S-mode
S-mode OpenSBI v1.8 SBI v1.0 sstc

SMP Payload

4-hart multiprocessing, HSM, IPI, atomic spinlock

  • 4 harts (2 cores × 2 SMT threads) — S-mode
  • Atomic lottery for primary election (any boot hart)
  • SBI HSM: explicit secondary hart start via hart_start
  • Per-hart timers, cross-hart IPI, atomic shared counter
  • amoswap UART spinlock on isolated 64-byte cache line
S-mode 4 harts SBI HSM amoswap SMP

Prerequisites

Projects 1 and 2 need only the bare-metal toolchain, QEMU, and GDB. Projects 3 and 4 additionally require the Linux RISC-V toolchain to build OpenSBI from source.

RISC-V Toolchain (bare-metal)
riscv64-unknown-elf-*

GCC cross-compiler for bare-metal. Used by all 3 projects for the payload/app code.

RISC-V Toolchain (Linux)
riscv64-linux-gnu-*

Required only for Project 3 to build OpenSBI from source. Supports PIE relocation.

QEMU
qemu-system-riscv64

RISC-V system emulator. Version 6.x or later. Ships with most Linux distros.

GDB Multiarch
gdb-multiarch

Required for Eclipse CDT hardware debug and standalone GDB sessions.

Install on Ubuntu / Debian

# QEMU and GDB
sudo apt update
sudo apt install qemu-system-misc gdb-multiarch make

# Linux RISC-V toolchain (Project 3 — OpenSBI build only)
sudo apt install gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu

# Bare-metal toolchain — Option A: distro package
sudo apt install gcc-riscv64-unknown-elf binutils-riscv64-unknown-elf

# Option B: SiFive prebuilt GCC 10.2 (matches this project)
# Download from https://github.com/sifive/freedom-tools/releases
export PATH="/path/to/riscv64-unknown-elf-toolchain/bin:$PATH"

Quick Start

# Clone
git clone https://github.com/vwire/riscv-bare-metal-qemu.git
cd riscv-bare-metal-qemu

# ── Project 1: bare metal ─────────────────────────────────────
cd bare_metal_qemu && make qemu-run
# Loops silently (no UART output) — Ctrl-A X to quit

# ── Project 2: ZSBL + Supervisor ──────────────────────────────
cd ../zsbl_supervisor && make qemu-run
# Prints full demo output and 3 timer interrupts

# ── Project 3: OpenSBI Payload ────────────────────────────────
# Step 1: build OpenSBI from source (one-time setup)
git clone https://github.com/riscv-software-src/opensbi.git ~/eclipse-workspace/opensbi
cd ~/eclipse-workspace/opensbi
sed -i 's/firmware-genflags-y += -DFW_TEXT_START=0x0/firmware-genflags-y += -DFW_TEXT_START=0x80000000/' \
    firmware/objects.mk
make CROSS_COMPILE=riscv64-linux-gnu- PLATFORM=generic \
    FW_JUMP=y FW_JUMP_ADDR=0x80200000 -j$(nproc)

# Step 2: build and run the S-mode payload
cd ~/eclipse-workspace/riscv-bare-metal-qemu/opensbi_payload && make qemu-run

# ── Project 4: SMP Payload ────────────────────────────────────
# (uses same OpenSBI build from Project 3 — no rebuild needed)
cd ~/eclipse-workspace/riscv-bare-metal-qemu/smp_payload && make qemu-run
# Starts 4 harts. Prints timer IRQs and IPI demo. Ctrl-A X to quit.
All three projects produce self-contained ELF files. No OS installation beyond the toolchain and QEMU.

Q & A Reference

All 50 questions and answers from the development sessions — covering OpenSBI source walkthrough, RISC-V architecture, PMP, cache, SMP boot, and every bug found and fixed in Project 4. Filterable by category.

50 QUESTIONS · 7 CATEGORIES
Setup & Build · OpenSBI Source · RISC-V Architecture · PMP & Memory · Cache · SMP Boot · Project 4 Debug
Open Q & A →
Topics covered
· Why amoswap beats LR/SC for spinlocks
· GP register and .option norelax
· PMP region vs domain distinction
· Cache conflict misses and MESI protocol
· OpenSBI scratch/stack layout per hart
· Every Project 4 bug: root cause and fix