Bare-Metal Raspberry Pi 3B

Bare-metal AArch64 firmware for a Raspberry Pi 3B — custom boot, then AUX mini UART TX on GPIO 14 with no OS.

In progress Oct 2026 – Present
Raspberry Pi 3 Model B single-board computer
Raspberry Pi 3 Model B — the BCM2837 / Cortex-A53

Highlights

  • AArch64 boot stub that parks cores 1–3, sets up the stack, and zeroes .bss before calling main
  • Custom linker script producing a kernel8.img that the GPU firmware loads at 0x80000
  • AUX mini UART at 115200 8N1 on GPIO 14 (TXD1 / Alt5), driven through BCM2837 MMIO — no HAL, no libc

Overview

Starting from an SD card with only the Pi’s GPU firmware files, this project brings up a Raspberry Pi 3B without Linux. The boot stub reads MPIDR_EL1 so only core 0 continues, points the stack just below the 0x80000 load address, clears .bss, and jumps to C. main then enables the AUX mini UART, locks baud for 115200 at a 250 MHz core clock, puts GPIO 14 in Alt5 (TXD1), and loops transmitting a string over the wire.

Try the boot stub walkthrough and mini UART TX below — both follow the real boot.S and main.c from the repo.

Boot path

  1. GPU firmware on the FAT32 card (bootcode.bin, start.elf, fixup.dat) plus config.txt with arm_64bit=1, enable_uart=1, and core_freq=250.
  2. kernel8.img linked at 0x80000 and copied to the card with make flash.
  3. Core 0 alone runs _start → stack → BSS clear → main; cores 1–3 sit in WFE.

Mini UART

miniUartSetup enables the AUX mini UART, disables TX while baud and line control are programmed, sets GPIO 14’s GPFSEL1 field to Alt5 (TXD1), then enables transmit. Baud is locked with AUX_MU_BAUD = 270 and core_freq=250 in config.txt:

250 MHz / (8 × 271) ≈ 115,313 — about 0.1% off 115200.

miniUartPointer walks a char * until NUL and calls miniUartTransmit for each byte, waiting on AUX_MU_LSR bit 5 (TX empty) before writing AUX_MU_IO. main passes a local string into that helper and loops with a software delay. A host serial monitor (make monitor) reads the line at 115200 8N1 — Pi GPIO14 → adapter RX, shared GND.

Interactive bring-up

Walk the AArch64 boot stub from boot.S, then step the same AUX mini UART bring-up main.c does — enable AUX, set baud, put GPIO 14 in Alt5, and transmit bytes.

Boot stub

GPU firmware loads kernel8.img at 0x80000. Core 0 parks the others, sets the stack, clears .bss, and calls main.

 

Mini UART TX

Same MMIO as the firmware: enable AUX, program baud for 115200 at 250 MHz, set GPIO 14 to Alt5 (TXD1), then push bytes into AUX_MU_IO when the TX FIFO is empty.

115200 8N1 · GPIO 14 TX idle
 
AUX_ENABLES · 0x3F215004 0x00000000
AUX_MU_BAUD · 0x3F215068 0x00000000
GPFSEL1 · 0x3F200004 0x00000000
AUX_MU_CNTL · 0x3F215060 0x00000000