A Rust no_std AArch64 Type-1 (thin) hypervisor + bootloader that is launched by U-Boot (bootelf) and boots a single Linux guest.
This project is WIP and targets QEMU virt and Raspberry Pi 5.
This workspace provides cargo aliases:
cargo xbuild # build (copies artifacts into ./bin)
cargo xrun # run on QEMU (build + run.sh)
cargo xtest # run all tests (std + UEFI/QEMU + U-Boot/QEMU)- Rust nightly (see
rust-toolchain.toml) rust-srccomponent is required (build uses-Z build-std)- Target:
aarch64-unknown-none-softfloat
Typical required tools:
qemu-system-aarch64,dtcsudo,sfdisk,losetup,mkfs.vfat,mount,dd(used byrun.sh)
./u-boot/init.shThis generates:
./bin/u-boot.bin./bin/boot.scr
Add ./create_linux_bin.sh (Docker-based helper) and run:
chmod +x ./create_linux_bin.sh
./create_linux_bin.sh # default Buildroot version
# or
./create_linux_bin.sh 2025.05 # explicit versionOutputs:
./bin/Image(Linux kernel Image)./bin/DISK0(rootfs image, copied from Buildrootrootfs.ext2)
./setup.shThis dumps QEMU’s DTB and writes a modified DTB:
./bin/qemu_mod.dtb
cargo xrunWhat happens:
cargo xrunbuilds theelf-hypervisorpackage and copieself-hypervisor.elfinto./bin/run.shcreates./bin/disk.imgwith:- p1: FAT32 (contains
elf-hypervisor.elf,u-boot.bin,boot.scr,Image,qemu.dtb) - p2: raw rootfs written from
./bin/DISK0
- p1: FAT32 (contains
- QEMU is launched with GICv3 enabled and GDB stub on
tcp::1234
QEMU is started with:
-gdb tcp::1234 # add "-S" in run.sh if you want to stop at resetcargo xrun rpi5This builds rpi_boot.elf and converts it to kernel_2712.img using rust-objcopy.
Copy the resulting kernel_2712.img to your Pi boot media as appropriate.
For local development with the repository toolchain, use the Nix shell explicitly if cargo
is not already on PATH:
/nix/var/nix/profiles/default/bin/nix develop --accept-flake-config --command cargo xrun rpi5The generated Pi 5 firmware image is:
bin/kernel_2712.imgCopy that file to the Pi 5 boot partition as kernel_2712.img, then power-cycle or reset the
board so the firmware starts it from reset. This is the preferred execution path for rpi_boot;
loading bin/rpi_boot.elf over OpenOCD after Linux or another EL2 payload is already running can
leave the MMU/cache state enabled and jump back into the existing high virtual address space.
To start the local OpenOCD server for inspection:
./run.shThis opens the OpenOCD command port on 4444 and CPU GDB ports starting at 3333. If OpenOCD
reports stale debug state or secondary-core DSCR_DTR_RX_FULL errors, stop OpenOCD and do a real
board reset before retrying. The helper below toggles USB hub power for the default hub location:
./reboot.sh 3If your Pi is connected through a different controllable hub, override the hub location:
LOCATION=1-1 ./reboot.sh 5Use sudo uhubctl to list controllable hubs and connected debug/UART adapters. Note that toggling
the hub containing only the debug probe resets the probe, not necessarily the Pi board itself; for a
clean boot test, reset or power-cycle the Pi 5 power input or boot media path.
RP1 UART0 input is handled by the RP1 pIRQ hook as a level MSI-X source. The rpi_boot
exception handler reports generic passthrough-MMIO completion and IRQ resample points, while the
RP1 hook owns the source-specific PL011 status/completion checks and MSI-X IACK/reissue.
cargo xtest
cargo xtest --help # show filtering options and arg forwarding
cargo xtest -p gdb_remote -t uefi_packet_size # filter xtest.txt by package and test name (UEFI/U-Boot)The test plan is defined in xtest.txt:
-
stdunit tests for selected crates -
UEFI/QEMU tests for:
- virtio-blk
- FAT32(sudo required)
gdb_remotehandshake
-
U-Boot/QEMU tests for:
- paging stage-1 / stage-2 translation
- gic v2
bootloader(elf-hypervisor): QEMU path entry; sets up EL2 and boots Linux at EL1rpi_boot: Raspberry Pi 5 entryarch_hal: AArch64 HAL (paging, exceptions, PL011, PSCI, timer, GIC)dtb,elf: parsers/helpers used by boot pathsfile+virtio: virtio-blk and FAT32 helper stackgdb_remote:no_stdIRQ-driven GDB RSP engineallocator,mutex,typestate,intrusive_linked_list: low-level runtime building blocks