30K lines of no_std Rust: a bare-metal ARM64 hypervisor that replaces Google's Hafnium SPMC

⚓ Rust    📅 2026-04-09    👤 surdeus    👁️ 6      

surdeus

I built a bare-metal Type-1 hypervisor at ARM's S-EL2 (Secure EL2) in no_std Rust. It replaces Hafnium — Google's 200K+ line C reference implementation — as the Secure Partition Manager Core, and runs alongside Android pKVM on the same chip.

What it does

  • Boots Linux 6.12 to a BusyBox shell (4 vCPUs, virtio-blk)
  • Manages 3 Secure Partitions at S-EL1
  • Full FF-A v1.1: messaging, memory sharing, ownership transfer, notifications
  • Coexists with pKVM (NS-EL2) on 4 physical CPUs
  • 35/35 E2E tests through real TF-A firmware + pKVM
  • 230KB release binary

Rust-specific things I learned

no_std is surprisingly viable at EL2. One dependency (fdt for DTB parsing). alloc crate with a bump allocator gives you Box/Vec. Enum dispatch replaces trait objects for zero-cost MMIO routing — no vtables, the compiler monomorphizes everything.

SP lifecycle as an enum is a game-changer. The state machine (Reset→Idle→Running→Blocked→Preempted) has ~15 valid transitions. Adding Blocked→Preempted for chain preemption, match forced me to handle every case. Caught two bugs at compile time. In C this would have been an int and asserts scattered across files.

Debug-mode codegen will bite you below the OS. read_volatile compiles to NEON SIMD (cnt v0.8b) in debug mode for alignment checks. ARM's EL3 firmware traps all FP/SIMD by default. Silent hang, no fault, no output. Hours of GDB. Fix: one TF-A build flag. Lesson: start with opt-level = 1 from day one for bare-metal.

No unwrap() in the entire codebase. Everything is ? or pattern matching. When you're running at EL2 there's no panic handler that helps — you just hang. Rust's error propagation makes this natural rather than painful.

Quick start

git clone https://github.com/willamhou/hypervisor
cd hypervisor
make run          # 34 test suites, ~5 seconds on QEMU
make run-linux    # boots Linux to shell

For pKVM integration, you need TF-A + AOSP kernel (both build via Docker, ~30min first time).

Links

Happy to answer questions about no_std bare-metal, ARM security architecture, or the FF-A protocol.

1 post - 1 participant

Read full topic

🏷️ Rust_feed