Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.

harpertoken/barepi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ARM Raspberry Pi Bare-Metal Firmware

Learn how to build and deploy bare-metal firmware for Raspberry Pi 3/4 using ARM64 assembly and C.

In this article

  • About this firmware
  • Prerequisites
  • Building the firmware
  • Testing with QEMU
  • Deploying to hardware
  • API reference
  • Troubleshooting

About this firmware

This is enterprise-grade bare-metal firmware for Raspberry Pi 3/4, designed with production standards and ARM best practices. The firmware implements multi-core awareness, memory safety, and modular architecture with comprehensive error handling.

The firmware provides:

  • Multi-core support: Proper CPU core initialization and management
  • Memory safety: Comprehensive memory layout and stack management
  • Driver abstraction: Clean GPIO and system utilities interface
  • Error handling: Robust error checking and panic handling
  • Professional build system: Makefile with multiple targets and configurations

Prerequisites

This guide assumes that you are familiar with ARM64 assembly, C programming, and embedded systems development. For more information about ARM architecture, see the ARM Architecture Reference Manual.

You must install the ARM64 cross-compilation toolchain and QEMU emulator to build and test the firmware. This guide uses standard GNU toolchain conventions.

Required tools:

  • aarch64-linux-gnu-gcc (ARM64 cross-compiler)
  • GNU Make 4.0+
  • QEMU system emulator
  • Linux/macOS/WSL environment

Installation

macOS:

brew install aarch64-elf-gcc qemu

Ubuntu/Debian:

sudo apt-get install gcc-aarch64-linux-gnu qemu-system-arm

Building the firmware

Basic build

To build the firmware image, run the make command in the project root:

# Build firmware image
make

The build system will compile all source files and generate dist/kernel8.img - the bootable firmware image.

Build targets

# Show build information
make info

# Clean build artifacts  
make clean

# Remove all generated files
make distclean

Project structure

firmware/
β”œβ”€β”€ include/           # Public headers
β”‚   β”œβ”€β”€ platform.h     # Platform-specific definitions
β”‚   β”œβ”€β”€ gpio.h         # GPIO driver interface
β”‚   └── system.h       # System utilities
β”œβ”€β”€ src/               # Source implementations
β”‚   β”œβ”€β”€ gpio.c         # GPIO driver
β”‚   └── system.c       # System utilities
β”œβ”€β”€ boot.S             # ARM64 boot assembly
β”œβ”€β”€ main.c             # Application entry point
β”œβ”€β”€ linker.ld          # Memory layout script
└── Makefile           # Build system

Testing with QEMU

Warning

The firmware requires proper SD card emulation to boot correctly. Using QEMU's -kernel flag bypasses the Raspberry Pi boot sequence and causes the firmware to load at the wrong memory address.

Creating a boot image

First, create a proper SD card image with the firmware and boot configuration:

# Create SD card image with proper boot setup
dd if=/dev/zero of=boot.img bs=1M count=64
mkfs.fat -F32 boot.img
mkdir -p mnt
sudo mount -o loop boot.img mnt
cp dist/kernel8.img mnt/
echo "arm_64bit=1" > mnt/config.txt
echo "kernel=kernel8.img" >> mnt/config.txt
sudo umount mnt

Running the emulation

# Standard emulation
qemu-system-aarch64 -M raspi3b -drive file=boot.img,if=sd,format=raw -nographic

# With debugging monitor
qemu-system-aarch64 -M raspi3b -drive file=boot.img,if=sd,format=raw -nographic -monitor stdio

Using the QEMU monitor

When using -monitor stdio, you can access the QEMU monitor for debugging. The monitor allows you to inspect the system state and control execution:

(qemu) info registers    # View CPU register state
(qemu) info mtree       # Show memory layout
(qemu) c                # Continue execution
(qemu) q                # Quit QEMU

Expected boot state:

  • PC should be at 0x80000 (firmware load address)
  • SP should be initialized to stack top
  • PSTATE should show EL1 execution level

Deploying to hardware

Preparing the SD card

  1. Format SD card: Use FAT32 filesystem
  2. Copy firmware: cp dist/kernel8.img /path/to/sdcard/
  3. Configure boot: Create config.txt with:
    arm_64bit=1
    kernel=kernel8.img
    disable_overscan=1
    
  4. Boot: Insert SD card and power on Raspberry Pi

Hardware configuration

Modify include/platform.h for hardware-specific settings:

#define LED_PIN             18U      // GPIO pin for LED
#define LED_ON_TIME_MS      500U     // LED on duration  
#define LED_OFF_TIME_MS     500U     // LED off duration

API reference

GPIO driver

The GPIO driver provides a clean interface for pin control:

// Initialize GPIO pin
gpio_result_t gpio_init(uint32_t pin, gpio_function_t function);

// Set pin high/low
void gpio_set(uint32_t pin);
void gpio_clear(uint32_t pin);

System utilities

System utilities handle initialization and timing:

// System initialization
void system_init(void);

// Precise delay
void system_delay_ms(uint32_t milliseconds);

// Panic handler
void system_panic(const char* message);

Memory layout

Region Start Address Size Purpose
Code 0x80000 ~64KB Firmware image
Data ~0x90000 ~16KB Initialized data
BSS ~0x94000 ~16KB Uninitialized data
Stack ~0x98000 16KB System stack

Troubleshooting

Common QEMU issues

Problem: PC starts at 0x200 instead of 0x80000
Solution: Use SD card emulation instead of -kernel flag. The -kernel option bypasses the normal Raspberry Pi boot sequence.

Problem: Firmware doesn't execute
Solution: Ensure proper boot configuration in config.txt and verify the SD card image was created correctly.

Problem: Stack pointer is zero
Solution: Verify linker script defines _stack_top and boot.S initializes the stack pointer correctly.

Handling boot errors

If you encounter boot issues, use the QEMU monitor to inspect the system state:

qemu-system-aarch64 -M raspi3b -drive file=boot.img,if=sd,format=raw -nographic -monitor stdio

Then check the registers:

(qemu) info registers

Look for:

  • PC at 0x80000 (correct firmware load address)
  • Non-zero SP (stack pointer initialized)
  • PSTATE showing EL1 (correct execution level)

Testing verification

The firmware provides a simple LED blink test to verify:

  • Boot sequence initialization
  • GPIO driver functionality
  • System timer operation
  • Memory layout correctness

Example usage

Here is a complete example of building and testing the firmware:

# Clone and build
git clone <repository-url>
cd firmware
make

# Create boot image
dd if=/dev/zero of=boot.img bs=1M count=64
mkfs.fat -F32 boot.img
mkdir -p mnt
sudo mount -o loop boot.img mnt
cp dist/kernel8.img mnt/
echo "arm_64bit=1" > mnt/config.txt
echo "kernel=kernel8.img" >> mnt/config.txt
sudo umount mnt

# Test in QEMU
qemu-system-aarch64 -M raspi3b -drive file=boot.img,if=sd,format=raw -nographic

Note

This is a basic example. In production, you may want to add additional error handling, logging, and hardware-specific optimizations.

License

Copyright (c) 2026 ARM Limited. All rights reserved.

Licensed under the BSD-3-Clause License. See LICENSE for details.

About

πŸ“

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors