U-Boot
Introduction
U-Boot (Universal Bootloader) is the most widely used bootloader in Embedded Linux. Originally developed for PowerPC-based boards, it has grown to support ARM, MIPS, RISC-V, x86, Nios II, MicroBlaze, and many other architectures. U-Boot initializes hardware, loads the operating system kernel, and passes control to it with the necessary parameters and device tree.
U-Boot is both a bootloader and a command-line environment for hardware debugging, firmware updates, and boot configuration. Understanding U-Boot is essential for anyone working with Embedded Linux.
Boot Flow
sequenceDiagram
participant ROM as Boot ROM
participant SPL as SPL (Secondary Program Loader)
participant TPL as TPL (Tertiary Program Loader, optional)
participant UBOOT as U-Boot Proper
participant KERNEL as Linux Kernel
participant DTB as Device Tree
participant INIT as Init
ROM->>ROM: Reset vector (SoC-specific)
ROM->>SPL: Load SPL from boot media
Note over ROM,SPL: SPL runs in SRAM (limited memory)
SPL->>SPL: Initialize DRAM controller
SPL->>SPL: Set up clocks, basic peripherals
SPL->>UBOOT: Load U-Boot from boot media to DRAM
Note over UBOOT: U-Boot runs in DRAM
UBOOT->>UBOOT: Initialize all peripherals
UBOOT->>UBOOT: Load environment variables
UBOOT->>UBOOT: Run boot scripts (bootcmd)
UBOOT->>KERNEL: Load kernel image to RAM
UBOOT->>DTB: Load device tree to RAM
UBOOT->>UBOOT: Set up kernel parameters (bootargs)
UBOOT->>KERNEL: bootm / booti / bootz (transfer control)
KERNEL->>KERNEL: Decompress (if compressed)
KERNEL->>KERNEL: Initialize subsystems
KERNEL->>DTB: Parse device tree
KERNEL->>KERNEL: Mount root filesystem
KERNEL->>INIT: Execute /sbin/init
SPL (Secondary Program Loader)
# SPL is a minimal bootloader that runs before U-Boot proper
# It runs in SRAM (limited memory, e.g., 128KB)
# SPL responsibilities:
# 1. Initialize DRAM (most critical task)
# 2. Set up clock tree
# 3. Configure boot media (eMMC, SD, SPI flash)
# 4. Load U-Boot proper into DRAM
# SPL is built as part of U-Boot
# For some SoCs, SPL is called:
# - MLO (Texas Instruments)
# - SPL (generic U-Boot)
# - TPL + SPL (if TPL is needed for very early init)
# SPL output files:
# spl/u-boot-spl.bin — SPL binary
# spl/u-boot-spl-spl.bin — combined SPL + U-Boot
Environment Variables
U-Boot uses environment variables to control its behavior:
Key Environment Variables
# View all environment variables (in U-Boot console)
=> printenv
# Key variables:
baudrate=115200
bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait
bootcmd=run mmcboot
bootdelay=3
loadaddr=0x82000000
fdtaddr=0x88000000
mmcdev=0
mmcpart=1
# Custom boot script
mmcboot=mmc dev ${mmcdev}; mmc read ${loadaddr} ${kernel_offset} ${kernel_size}; mmc read ${fdtaddr} ${fdt_offset} ${fdt_size}; bootm ${loadaddr} - ${fdtaddr}
Environment Storage
# Environment can be stored in:
# 1. SPI flash (persistent, survives reflash)
# 2. eMMC partition (persistent)
# 3. NAND flash (persistent)
# 4. Default compiled into U-Boot (fallback)
# Save environment changes
=> setenv bootargs "console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait earlycon"
=> saveenv
# Writing to SPI flash...
# Reset to defaults
=> env default -a
=> saveenv
# Environment redundancy (CONFIG_ENV_IS_IN_FAT, etc.)
# Two copies of environment, CRC-checked
# If primary is corrupted, U-Boot uses backup
U-Boot Commands
Memory Commands
# Memory display
=> md 0x80000000 16
# 80000000: ea000012 e59ff014 e59ff014 e59ff014 ................
# 80000010: e59ff014 e59ff014 e59ff014 e59ff014 ................
# ...
# Memory write
=> mw 0x80000000 0xdeadbeef 1
# Memory copy
=> cp 0x80000000 0x90000000 0x1000
# Memory compare
=> cmp 0x80000000 0x90000000 0x1000
# Total of 4096 words were the same
Storage Commands
# MMC/eMMC
=> mmc list
# FSL_SDHC: 0 (SD)
# FSL_SDHC: 1 (eMMC)
=> mmc dev 0
# switch to partitions #0, OK
# mmc0 is current device
=> mmc info
# Device: FSL_SDHC
# Manufacturer ID: 03
# OEM: 5344
# Name: SD16G
# Bus Speed: 50000000
# Mode: SD High Speed (50MHz)
# Capacity: 14.8 GiB
=> mmc read 0x80000000 0x800 0x4000
# read 16384 blocks from partition 0
# NAND
=> nand info
=> nand read 0x80000000 0x400000 0x800000
# SPI flash
=> sf probe 0
=> sf read 0x80000000 0x100000 0x400000
=> sf write 0x80000000 0x100000 0x400000
# USB
=> usb start
=> usb storage
=> usb read 0x80000000 0 0x4000
# Filesystem access (ext4, FAT, etc.)
=> ext4load mmc 0:2 ${loadaddr} /boot/Image
=> fatload mmc 0:1 ${loadaddr} Image
Network Commands
# Configure network
=> setenv ipaddr 192.168.1.100
=> setenv serverip 192.168.1.1
=> setenv netmask 255.255.255.0
=> setenv gatewayip 192.168.1.1
# TFTP download
=> tftp ${loadaddr} Image
# Using ethernet@ff540000 device
# TFTP from server 192.168.1.1
# Load address: 0x82000000
# Loading: ################################
# 10 MiB/s
# NFS mount
=> nfs ${loadaddr} 192.168.1.1:/exports/Image
# Ping
=> ping 192.168.1.1
# Using ethernet@ff540000 device
# host 192.168.1.1 is alive
# DHCP
=> dhcp
# BOOTP broadcast 1
# DHCP client bound to address 192.168.1.100
Boot Commands
# Load and boot a kernel
=> loadaddr=0x82000000
=> fdtaddr=0x88000000
# Load kernel from eMMC
=> ext4load mmc 0:2 ${loadaddr} /boot/Image
# 20971520 bytes read
# Load device tree
=> ext4load mmc 0:2 ${fdtaddr} /boot/board.dtb
# 32768 bytes read
# Set boot arguments
=> setenv bootargs "console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw"
# Boot the kernel
=> bootm ${loadaddr} - ${fdtaddr}
# or for raw Image (ARM64):
=> booti ${loadaddr} - ${fdtaddr}
# or for compressed zImage (ARM32):
=> bootz ${loadaddr} - ${fdtaddr}
# Automatic boot (bootcmd runs on startup)
=> setenv bootcmd "ext4load mmc 0:2 ${loadaddr} /boot/Image; ext4load mmc 0:2 ${fdtaddr} /boot/board.dtb; booti ${loadaddr} - ${fdtaddr}"
=> saveenv
FIT Images
FIT (Flattened Image Tree) is U-Boot’s image format that bundles kernel, device tree, and ramdisk into a single, verified image:
graph TB
subgraph FIT Image
FIT_HEADER["ITS Header<br>Description, timestamps"]
KERNEL["Kernel Image<br>Image.gz or Image"]
DTB1["Device Tree 1<br>board-v1.dtb"]
DTB2["Device Tree 2<br>board-v2.dtb"]
RAMDISK["Root Filesystem<br>rootfs.cpio.gz"]
CONFIG["Configurations<br>Select kernel + DTB + ramdisk"]
SIG["Digital Signatures<br>SHA256 + RSA"]
end
Creating FIT Images
# Image Tree Source (ITS) file
cat > image.its << 'EOF'
/dts-v1/;
/ {
description = "Embedded Linux FIT Image";
images {
kernel@1 {
description = "Linux Kernel";
data = /incbin/("Image.gz");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "gzip";
load = <0x82000000>;
entry = <0x82000000>;
hash@1 {
algo = "sha256";
};
};
fdt@1 {
description = "Device Tree";
data = /incbin/("board.dtb");
type = "flat_dt";
arch = "arm64";
compression = "none";
hash@1 {
algo = "sha256";
};
};
ramdisk@1 {
description = "Root Filesystem";
data = /incbin/("rootfs.cpio.gz");
type = "ramdisk";
arch = "arm64";
os = "linux";
compression = "gzip";
hash@1 {
algo = "sha256";
};
};
};
configurations {
default = "conf@1";
conf@1 {
description = "Boot Linux with DTB and ramdisk";
kernel = "kernel@1";
fdt = "fdt@1";
ramdisk = "ramdisk@1";
hash@1 {
algo = "sha256";
};
};
};
};
EOF
# Build FIT image
mkimage -f image.its fitImage
# Verify FIT image
dumpimage -l fitImage
# FIT description: Embedded Linux FIT Image
# Created: Mon Jul 21 10:00:00 2025
# Image 0 (kernel@1)
# Description: Linux Kernel
# Type: Kernel Image
# Compression: gzip
# Architecture: ARM64
# Load Address: 0x82000000
# Image 1 (fdt@1)
# Description: Device Tree
# Type: Flat Device Tree
# Compression: uncompressed
# Image 2 (ramdisk@1)
# Description: Root Filesystem
# Type: RAMDisk Image
# Compression: gzip
# Default Configuration: 'conf@1'
# Configuration 0 (conf@1)
# Description: Boot Linux with DTB and ramdisk
# Kernel: kernel@1
# FDT: fdt@1
# Ramdisk: ramdisk@1
Booting FIT Images
# Boot FIT image in U-Boot
=> load mmc 0:1 ${loadaddr} fitImage
=> bootm ${loadaddr}#conf@1
# U-Boot automatically:
# 1. Verifies hash/signature
# 2. Extracts kernel, DTB, ramdisk
# 3. Decompresses kernel if needed
# 4. Passes DTB and ramdisk addresses to kernel
Secure Boot
U-Boot supports verified boot using digital signatures:
sequenceDiagram
participant DEV as Device (U-Boot)
participant KEY as Signing Key (HSM/Offline)
participant IMG as FIT Image
Note over KEY,IMG: Build Time
KEY->>IMG: Sign kernel, DTB, ramdisk
IMG->>IMG: Embed signatures in FIT
Note over DEV,IMG: Boot Time
DEV->>IMG: Load FIT image
DEV->>DEV: Verify signatures against stored public key
alt Signatures valid
DEV->>DEV: Boot kernel
else Signatures invalid
DEV->>DEV: Abort boot, enter recovery
end
Implementing Secure Boot
# 1. Generate RSA key pair (offline/HSM)
openssl genrsa -F4 -out signing_key.pem 4096
openssl req -new -x509 -key signing_key.pem -out signing_key.crt \
-subj "/CN=My Embedded Device/"
# Extract public key for U-Boot
openssl rsa -in signing_key.pem -pubout -out signing_key.pub
# 2. Build U-Boot with signature verification
# CONFIG_FIT_SIGNATURE=y
# CONFIG_RSA=y
# CONFIG_RSA_VERIFY=y
# CONFIG_SECURE_BOOT=y
# 3. Sign the FIT image
mkimage -f image.its -K u-boot.dtb -k /path/to/keys -r fitImage
# -K: output public key DTB overlay
# -k: key directory
# -r: sign the image
# 4. U-Boot verifies on boot
# => bootm ${loadaddr}
# ## Loading kernel from FIT Image at 82000000 ...
# Verifying Hash Integrity ... OK
# Loading Flat Device Tree from FIT Image ...
# Verifying Hash Integrity ... OK
# Booting using the fdt configuration at 0x...
Chain of Trust
graph TB
subgraph Hardware Root of Trust
HAB["HAB (i.MX) / ATF (ARM64)<br>ROM verifies SPL"]
end
subgraph SPL
SPL_VER[SPL verifies U-Boot]
end
subgraph U-Boot
UB_VER[U-Boot verifies FIT image]
end
subgraph Kernel
K_VER[dm-verity verifies rootfs]
end
HAB --> SPL_VER --> UB_VER --> K_VER
U-Boot Device Tree
U-Boot has its own device tree and can modify the kernel device tree before booting:
# U-Boot can modify DTB before passing to kernel
# This is called "fixup" or "fdt apply"
# Set a property in the DTB
=> fdt addr ${fdtaddr}
=> fdt set /chosen bootargs "extra_param=1"
# Enable/disable a device
=> fdt set /soc/serial@9000000 status "okay"
# Add a node
=> fdt mknode /chosen my_node
=> fdt set /chosen/my_node my_prop "my_value"
# U-Boot fixup mechanism (automatic)
# CONFIG_OF_BOARD_FIXUP=y
# Board code can modify DTB automatically
Build System
# Cross-compile U-Boot
git clone https://source.denx.de/u-boot/u-boot.git
cd u-boot
# List available board configurations
make list-defs | grep -i beagle
# am335x_evm_defconfig
# beaglebone_defconfig
# Configure
make CROSS_COMPILE=aarch64-linux-gnu- rpi_4_defconfig
# Optional: customize
make CROSS_COMPILE=aarch64-linux-gnu- menuconfig
# Build
make CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
# Output:
# u-boot.bin — Raw binary
# u-boot.img — Image with header (for boot ROM)
# u-boot-spl.bin — SPL binary
# u-boot.dtb — U-Boot device tree (with public keys if secure boot)
# Build for multiple boards (with ATF)
make CROSS_COMPILE=aarch64-linux-gnu- BL31=../arm-trusted-firmware/build/release/bl31.bin -j$(nproc)
U-Boot Shell Scripts
# U-Boot supports scripting with hush shell
# Conditional boot (try SD card, fall back to network)
=> setenv bootcmd '
if mmc dev 0; then
run mmcboot;
else
run netboot;
fi
'
# Multiple boot targets
=> setenv bootcmd '
if test -e mmc 0:2 /boot/Image; then
run mmcboot;
elif test -e mmc 0:1 fitImage; then
run fitboot;
else
run netboot;
fi
'
# Recovery mode
=> setenv bootcmd '
if button_recovery; then
run recovery;
else
run normalboot;
fi
'
Debugging U-Boot
# Serial console (most common)
# Connect USB-to-serial adapter
# Baud rate: 115200, 8N1
screen /dev/ttyUSB0 115200
# Break into U-Boot console
# Press any key during "Hit any key to stop autoboot: 3"
# Debug with JTAG
# OpenOCD + GDB
openocd -f interface/ftdi/olimex-arm-usb-tiny-h.cfg \
-f board/my-board.cfg
# In another terminal
aarch64-linux-gnu-gdb u-boot
(gdb) target remote :3333
(gdb) break board_init_f
(gdb) continue
# U-Boot debug options
# CONFIG_DEBUG_UART=y
# CONFIG_DEBUG_UART_BASE=0xff1a0000
# CONFIG_DEBUG_UART_CLOCK=24000000
# Enables early serial output before console is initialized
References
- U-Boot Documentation. https://docs.u-boot.org/en/latest/
- U-Boot Source Code. https://source.denx.de/u-boot/u-boot
- U-Boot FIT Image Documentation. https://docs.u-boot.org/en/latest/usage/fit/
- Denx U-Boot Wiki. https://www.denx.de/wiki/U-Boot
Further Reading
Related Topics
- Embedded Linux Overview — Embedded Linux fundamentals
- Device Tree — Hardware description
- Cross-Compilation — Building U-Boot for target
- ARM Architecture — ARM-specific boot details