Beacon CM4 — Buildroot + RAUC

Buildroot BR2_EXTERNAL for a Raspberry Pi CM4 with A/B OTA updates via RAUC.

Partition layout

# Label Size Content
p1 boot_a 64 MiB FAT32 — firmware + U-Boot (slot A)
p2 boot_b 64 MiB FAT32 — firmware + U-Boot (slot B)
p3 data 256 MiB ext4 — persistent data (/data)
p4 (extended)
p5 rootfs0 250 MiB ext4 — rootfs slot A
p6 rootfs1 250 MiB ext4 — rootfs slot B
p7 upload 256 MiB ext4 — staging area for bundles (/upload)

U-Boot reads BOOT_ORDER/BOOT_x_LEFT env vars from eMMC and selects the active slot before loading the kernel. RAUC uses boot-mbr-switch to toggle between slots.


One-time setup

1. Generate RAUC signing certificates

Run once from the beacon-buildroot/ directory:

cd ~/repos/buildroot-beacon/beacon-buildroot
./openssl-ca.sh "Beacon" "Beacon RAUC CA"

This creates openssl-ca/dev/ with:

openssl-ca/dev/
  ca.cert.pem               ← keyring installed into target /etc/rauc/keyring.pem
  development-1.cert.pem    ← signing cert (build host)
  private/
    development-1.key.pem   ← signing key  (build host, keep secret)

Do not regenerate the CA once devices are flashed — bundles signed with a new CA will be rejected by devices that have the old keyring.

2. Initial full build

cd ~/repos/buildroot-beacon

make -C rpi-buildroot-fork \
     O=$(pwd)/output \
     BR2_EXTERNAL=$(pwd)/beacon-buildroot \
     beacon_cm4_rauc_defconfig

make -C rpi-buildroot-fork \
     O=$(pwd)/output \
     BR2_EXTERNAL=$(pwd)/beacon-buildroot \
     -j$(nproc)

Output artifacts in output/images/:

File Purpose
sdcard.img.xz Full eMMC image for initial flash
sdcard.img.bmap Block map for fast flash with bmaptool
rootfs.raucb OTA bundle — rootfs only
update.raucb OTA bundle — bootfs + rootfs (full system)

3. Initial flash (EMMC_DISABLE jumper bridged)

./beacon-buildroot/scripts/flash-cm4.sh

Remove the jumper and power-cycle after the script completes.


Creating an OTA update

What triggers a new bundle

Any source change that results in a different rootfs.ext4 or boot.vfat will produce a new bundle on the next build. Typical triggers:

  • Package version bump / new package in defconfig
  • File added/changed under rootfs-overlay/
  • post-build.sh changes
  • Kernel or U-Boot update

Build the update

Incremental build — only changed packages and the rootfs/image stage are rebuilt:

cd ~/repos/buildroot-beacon

# Optional: set a human-readable version string
export VERSION="1.2.0"

make -C rpi-buildroot-fork \
     O=$(pwd)/output \
     BR2_EXTERNAL=$(pwd)/beacon-buildroot \
     -j$(nproc)

post-image.sh runs automatically at the end and:

  1. Builds boot.vfat from U-Boot + firmware blobs
  2. Creates rootfs.raucb (rootfs-only bundle)
  3. Creates update.raucb (full bootfs+rootfs bundle)
  4. Signs both bundles with development-1.key.pem
  5. Assembles sdcard.img.xz + .bmap

Bundle contents

rootfs.raucb manifest (format=verity):

[update]
compatible=beacon-cm4
version=<VERSION>
[bundle]
format=verity
[image.rootfs]
filename=rootfs.ext4

update.raucb additionally contains [image.bootloader]boot.vfat.

Signing details

Signing is done by the host rauc binary during post-image.sh:

rauc bundle \
  --cert  openssl-ca/dev/development-1.cert.pem \
  --key   openssl-ca/dev/private/development-1.key.pem \
  --keyring openssl-ca/dev/ca.cert.pem \
  <bundle-dir>/ <output>.raucb

The target verifies the bundle signature against /etc/rauc/keyring.pem (= ca.cert.pem installed during build by post-build.sh).

To inspect a bundle without installing it:

output/host/bin/rauc \
  --keyring beacon-buildroot/openssl-ca/dev/ca.cert.pem \
  info output/images/rootfs.raucb

Deploying the update to the CM4

Find the CM4's IP

The CM4 gets a DHCP address on eth0 (changes on each reboot):

# By MAC address:
ip neigh show dev enp0s31f6 | grep e4:5f:01:e9:13:96

Transfer the bundle

Dropbear has no sftp-server — standard scp does not work. Use stdin pipe instead:

CM4=10.11.0.xx   # replace with actual IP

sshpass -p beacon ssh user@$CM4 \
  'sudo tee /upload/rootfs.raucb > /dev/null' \
  < output/images/rootfs.raucb

~51 MiB transfers in ~5 s on LAN.

Install

sshpass -p beacon ssh user@$CM4 'rauc install /upload/rootfs.raucb'

Expected output:

  0% Installing
 20% Checking bundle done.
 40% Determining target install group done.
 46% Checking slot rootfs.1 done.
 99% Copying image to rootfs.1 done.
100% Installing done.
Installing `/upload/rootfs.raucb` succeeded

RAUC automatically selects the inactive slot as the target.

Reboot into the new slot

sshpass -p beacon ssh user@$CM4 'rauc status && sudo reboot'

rauc status should show Activated: rootfs.1 (B) before the reboot.

Confirm and mark-good

After reboot the system boots into the new slot in trial mode (U-Boot decrements BOOT_x_LEFT). You must mark it good or it will roll back on the next reboot.

# Find new IP (DHCP address changes):
CM4_NEW=$(ip neigh show dev enp0s31f6 | awk '/e4:5f:01:e9:13:96/{print $1}')

sshpass -p beacon ssh user@$CM4_NEW 'rauc status mark-good && rauc status'

Expected final rauc status:

Booted from: rootfs.1 (B)
Activated:   rootfs.1 (B)
x [rootfs.1]  boot status: good   ← currently running, committed
o [rootfs.0]  boot status: good   ← fallback

Rollback behaviour

If mark-good is not called after a reboot, U-Boot decrements BOOT_x_LEFT. After 3 failed attempts it switches back to the previous slot automatically — no manual intervention needed.

To force an immediate rollback:

# On the CM4:
sudo fw_setenv BOOT_ORDER "A B"   # or "B A" depending on current slot
sudo reboot

Rescue partition

Short GPIO4 (pin 7) to GND (pin 9) on the 40-pin header during power-on. U-Boot detects this and boots the read-only rescue rootfs from /dev/mmcblk0p2.


Reference

Path Purpose
configs/beacon_cm4_rauc_defconfig Buildroot defconfig
board/beacon-cm4/genimage.cfg Partition layout
board/beacon-cm4/post-image.sh Bundle creation + signing
board/beacon-cm4/post-build.sh Target rootfs customisation
board/beacon-cm4/rootfs-overlay/etc/rauc/system.conf RAUC slot config
openssl-ca/dev/ Signing certificates (generated once)
scripts/flash-cm4.sh Automated initial flash script
Description
No description provided
Readme 78 KiB
Languages
Shell 84.8%
C 11.8%
Makefile 3.4%