SPENCER is a comprehensive OS construction system that integrates the A9N Microkernel, Nun OS Runtime, and architecture-appropriate boot software.
It automatically generates executable OS images, significantly simplifying the development of embedded systems based on A9N.
-
A9N Microkernel: Capability-based 3rd-generation microkernel
-
Nun OS Framework: OS runtime framework for building embedded operating systems on top of the A9N microkernel
-
A9NLoader-rs: x86_64 UEFI bootloader for A9N-based systems
-
U-Boot: aarch64 secondary bootloader
SPENCER ties these together using a single build interface (cargo xtask),
producing a bootable image automatically.
cargo xtask build \
--arch {ARCH, e.g., x86-64} \
--platform {PLATFORM, e.g., qemu} \
[--enable-smp] \
--{release|debug}For example, the AArch64 QEMU build is:
cargo xtask build --arch aarch64 --platform qemu --releaseThe Raspberry Pi 4 Model B build is:
cargo xtask build --arch aarch64 --platform rpi4b --releaseThis builds A9N, the Nun OS payload, and U-Boot, then creates:
out/aarch64-qemu-release/
├── a9n/
│ ├── kernel.elf
│ └── kernel.img
├── u-boot/
│ └── u-boot.bin
└── spencer.img
The AArch64 spencer.img contains U-Boot legacy boot scripts, u-boot.bin,
kernel.img, and the Init ELF. QEMU virt still receives the generated
u-boot.bin separately as firmware because firmware loading and block-device
loading are distinct QEMU interfaces.
cargo xtask run \
--arch {ARCH, e.g., x86-64} \
--platform qemu \
--{release|debug}The default run exposes one virtual CPU. A multi-core A9N run requires both the Kernel build switch and the QEMU CPU count:
cargo xtask run \
--arch aarch64 \
--platform qemu \
--release \
--enable-smp \
--smp 4--enable-smp configures A9N with A9N_CONFIG_ENABLE_SMP=ON. --smp <N>
sets QEMU's virtual CPU count and defaults to 1. A value greater than 1 is
rejected unless --enable-smp is also present. This applies to both x86_64
and aarch64 QEMU runs. AArch64 QEMU discovers the CPUs from the U-Boot DTB and
starts secondary CPUs with PSCI.
Hardware acceleration is controlled with:
--accel auto|on|off
The default auto mode uses the host's QEMU hardware accelerator when the host
and guest architectures match, and otherwise uses TCG. on requires a usable
hardware accelerator and reports an error when none is available. off always
uses TCG. Spencer does not retry with TCG when a selected hardware accelerator
fails; rerun with --accel off to fall back explicitly.
For an AArch64 build, SPENCER resolves U-Boot in the following order:
UBOOT_QEMU_BINorUBOOT_RPI4B_BIN, as a platform-specific prebuilt override.UBOOT_BIN, as a generic explicit prebuilt override.UBOOT_SOURCE, as an explicit U-Boot source tree.tools/u-boot, when it is a source tree; QEMU also accepts the legacytools/u-boot/u-boot.bin, while Raspberry Pi acceptstools/u-boot/rpi4b/u-boot.bin.- A shallow checkout of U-Boot
v2025.10from the official repository.
Source builds run in a Linux Docker container and use an out-of-tree build
directory. SPENCER builds the container from
xtask/docker/u-boot.Dockerfile, which provides the AArch64 cross compiler and
all U-Boot host dependencies. No U-Boot compiler, GNU Make, OpenSSL, or GnuTLS
installation is required on the host. SPENCER selects qemu_arm64_defconfig
for --platform qemu and rpi_4_defconfig for --platform rpi4b.
UBOOT_SOURCE=/path/to/u-boot \
cargo xtask build --arch aarch64 --platform qemu --releaseUBOOT_DOCKER overrides the Docker-compatible executable and UBOOT_JOBS
overrides the parallel job count. UBOOT_DOCKER_IMAGE selects an existing
builder image and skips building SPENCER's default image.
The generic UBOOT_BIN remains available and takes precedence over source
builds, so Docker is not required when a prebuilt U-Boot binary is supplied.
For --arch aarch64 --platform rpi4b, SPENCER builds U-Boot with
rpi_4_defconfig and creates:
out/aarch64-rpi4b-release/
├── a9n/
│ ├── kernel.elf
│ └── kernel.img
├── raspberrypi-firmware/
│ └── boot/
├── u-boot/
│ └── u-boot.bin
└── spencer.img
SPENCER automatically makes a sparse clone of the official
raspberrypi/firmware repository at the pinned 1.20260521 release and caches
it under build/raspberrypi-firmware/. Set RPI_FIRMWARE_DIR to an existing
firmware repository root or its boot directory to override the automatic
checkout.
The first FAT32 partition in spencer.img contains start4.elf, fixup4.dat,
the BCM2711 device tree, U-Boot as kernel8.img, the
U-Boot boot script, A9N, and the Init ELF. Write spencer.img to the whole SD
card with Raspberry Pi Imager or another raw-image writer; copying the image as
a regular file does not create bootable media.
Serial output uses the BCM2711 PL011 on GPIO14 (TX) and GPIO15 (RX) at
115200 8N1. The generated config.txt applies disable-bt so that PL011 is
routed to the GPIO header and fixes its input clock at 48 MHz. Use
a 3.3 V USB-to-UART adapter and connect a common ground. Do not connect a 5 V
serial signal to the Raspberry Pi GPIO header. cargo xtask run remains a QEMU
command, so physical Raspberry Pi targets use cargo xtask build followed by
writing the image to removable media.
Build an SMP-enabled Raspberry Pi 4 image with:
cargo xtask build \
--arch aarch64 \
--platform rpi4b \
--release \
--enable-smpThe AArch64 HAL reads the four BCM2711 CPU nodes and their
cpu-release-addr values from the firmware DTB, releases the three secondary
CPUs through the spin table, and initializes a private GICv2 CPU interface,
generic timer, CPU-local state, scheduler, IDLE context, and kernel stack on
each core.
cargo xtask gdb \
--arch {ARCH, e.g., x86-64} \
--platform qemu \
--{release|debug} \
--gdb --stopBy default, SPENCER builds the template OS payload from ./core/Cargo.toml
and installs the produced core binary as /kernel/init.elf in the generated
FAT image.
For projects that use SPENCER as a build/image/run toolchain, the OS payload can be supplied from outside the SPENCER repository:
cargo xtask build \
--arch x86-64 \
--platform pc99 \
--release \
--os-manifest /path/to/os/Cargo.toml \
--os-target-json /path/to/x86_64-unknown-a9n.json \
--os-binary my_osThe same options are accepted by cargo xtask run:
cargo xtask run \
--arch x86-64 \
--platform pc99 \
--release \
--os-manifest /path/to/os/Cargo.toml \
--os-target-json /path/to/x86_64-unknown-a9n.json \
--os-binary my_osOptions:
--os-manifest: Cargo manifest of the external OS payload. If omitted, SPENCER uses./core/Cargo.toml.--os-target-json: custom Rust target JSON for the OS payload. If omitted, SPENCER uses./Nun/arch/<arch>-unknown-a9n.json.--os-binary: output binary name produced by the OS payload package. If omitted, SPENCER usescore.
The external payload is built into SPENCER's normal output directory:
out/<arch>-<platform>-<profile>/nun_os_target_dir/<target>/<profile>/<os-binary>
The image builder then writes that binary directly to:
/kernel/init.elf
No post-build image patching is required.
If the custom target JSON uses linker script paths in pre-link-args, prefer
absolute paths or paths that are valid from the external payload build context.
This avoids depending on Cargo/rust-lld's current working directory.
Currently supported architectures and platforms include:
x86_64aarch64
qemu(pc99for x86_64,virtfor aarch64)rpi4b(Raspberry Pi 4 Model B foraarch64)
-
riscv64(QEMU, real hardware) -
Non-specific embedded platforms
