Skip to main content

Firmware updates

Shipping a device and never touching it again is not realistic — bugs and security issues have to be fixed in the field, and new features deployed to existing units. Remote firmware update is a requirement, not a nice-to-have.

This page sets up swupdate with a dual-copy strategy, continuing the Buildroot STM32MP157-DK2 system.

Choosing an approach

ApproachTools
Package-level (binary distro only)the distro's package manager
Whole-image, block-levelswupdate, Mender, RAUC
File-basedOSTree

swupdate runs on the target and applies an update image (a .swu file) from local media or a remote server. It can update the kernel, the root filesystem, extra partitions, FPGA bitstreams and more. Two strategies:

  • Dual copy — storage holds two full copies of the filesystem. Run from copy A, update copy B, reboot into B; the next update targets A. Needs the space, but the running system is never mid-update.
  • Single copy — reboot into a minimal RAM-only system that updates the one copy on storage. For tighter storage budgets.

The dual-copy strategy is used below.

Kernel support for USB storage

Updates arrive on a USB stick here, so the kernel needs USB mass storage. Reconfigure via make linux-menuconfig (Buildroot) or bitbake virtual/kernel -c menuconfig (ST Distribution Package):

  • CONFIG_SCSI — required by USB mass storage
  • CONFIG_BLK_DEV_SD — SCSI disk support
  • CONFIG_USB_STORAGE
  • CONFIG_VFAT_FS — FAT filesystem (usually already on)
  • CONFIG_NLS_CODEPAGE_437, CONFIG_NLS_ISO8859_1 — to decode FAT filenames

Persist the change with make linux-update-defconfig, which writes it back into board/stmicroelectronics/stm32mp157-dk/linux.config. The ST SDK image enables all of these by default. See Kernel configuration.

Enable swupdate

In Buildroot menuconfig, under Target packages ▸ System tools, enable swupdate (you can disable install default website — the internal web server is not used here). Also enable gptfdisk and its sgdisk sub-option under Hardware handling; the post-update step needs sgdisk.

On Yocto, add the swupdate layer:

cd "$HOME/STM32MPU_workspace/STM32MP1-Ecosystem-v5.0.0/Distribution-Package"
git clone https://github.com/sbabic/meta-swupdate.git ../My-Package/meta-swupdate
( cd ../My-Package/meta-swupdate && git checkout mickledore )

bitbake-layers add-layer ../My-Package/meta-swupdate
bitbake -c menuconfig swupdate
bitbake st-image-weston

Rebuild and flash. On the target:

swupdate -h # Swupdate v2018.11.0
mount /dev/sda1 /mnt # a FAT USB stick should mount

Build the update image

A .swu is a CPIO archive containing an sw-description manifest plus the images to install.

board/stmicroelectronics/stm32mp157-dk/sw-description — one rootfs component, present as two collections for the two copies:

software = {
version = "0.1.0";
rootfs = {
rootfs-1: {
images: ( {
filename = "rootfs.ext4.gz";
compressed = true;
device = "/dev/mmcblk0p4";
} );
}
rootfs-2: {
images: ( {
filename = "rootfs.ext4.gz";
compressed = true;
device = "/dev/mmcblk0p5";
} );
}
}
}

A script to package it, at board/stmicroelectronics/stm32mp157-dk/gen-swupdate-image.sh:

#!/bin/sh
BOARD_DIR=$(dirname "$0")
cp "${BOARD_DIR}/sw-description" "${BINARIES_DIR}"

IMG_FILES="sw-description rootfs.ext4.gz"
( cd "${BINARIES_DIR}" \
&& for f in ${IMG_FILES}; do echo "${f}"; done | cpio -ovL -H crc > buildroot.swu )

Make it executable, then wire it into Buildroot via make menuconfig:

  • System configuration ▸ Custom scripts to run after creating filesystem images — append the script after the existing support/scripts/genimage.sh.
  • Filesystem images — enable gzip compression for the ext4 rootfs, so rootfs.ext4.gz is produced.

make then leaves output/images/buildroot.swu:

cat output/images/buildroot.swu | cpio -it
# sw-description
# rootfs.ext4.gz

Dual-copy partitioning

Give the SD card two rootfs partitions. In board/stmicroelectronics/stm32mp157-dk/genimage.cfg:

image sdcard.img {
hdimage { gpt = "true" }

partition fsbl1 { image = "tf-a-stm32mp157c-dk2.stm32" }
partition fsbl2 { image = "tf-a-stm32mp157c-dk2.stm32" }
partition ssbl { image = "u-boot.stm32" }

partition rootfs1 {
image = "rootfs.ext4"
partition-type = 0x83
bootable = "yes"
size = 256M
}
partition rootfs2 {
partition-type = 0x83
size = 256M
}
}

The kernel command line must not hard-code which partition is root, since it changes between copies. Use U-Boot's devplist variable — the partition extlinux.conf was read from — in overlay/boot/extlinux/extlinux.conf:

label stm32mp15-buildroot
kernel /boot/zImage
devicetree /boot/stm32mp157c-dk2.dtb
append root=/dev/mmcblk0p${devplist} rootwait console=ttySTM0,115200 vt.global_cursor_default=0

Because the running rootfs is always the one U-Boot booted from, root=/dev/mmcblk0p${devplist} resolves to the correct copy automatically — there is no extlinux.conf to rewrite on each update.

Switching copies is done by toggling the GPT bootable flag between partitions 4 and 5, in a post-update script at overlay/etc/swupdate/postupdate.sh:

#!/bin/sh
sgdisk -A 4:toggle:2 -A 5:toggle:2 /dev/mmcblk0
reboot

Make it executable. After make, the image carries the new layout:

sgdisk -p output/images/sdcard.img
# 1 ... fsbl1
# 2 ... fsbl2
# 3 ... ssbl
# 4 ... rootfs1
# 5 ... rootfs2

Test the update

Boot the system, mount the stick holding buildroot.swu, and trigger the update into slot 2:

mount /dev/sda1 /mnt/
swupdate -i /mnt/buildroot.swu -e rootfs,rootfs-2 -p /etc/swupdate/postupdate.sh
  • -i — the update file.
  • -e rootfs,rootfs-2 — which component and which collection: the rootfs in slot 2 (/dev/mmcblk0p5).
  • -p — the post-update script, run on success. It toggles the bootable flag and reboots.

A successful run ends with SWUPDATE successful and reboots. At the next boot the log confirms the switch:

Scanning mmc 0:5...
Found /boot/extlinux/extlinux.conf
append: root=/dev/mmcblk0p5 rootwait console=ttySTM0,115200 vt.global_cursor_default=0

U-Boot loaded extlinux.conf from partition 5, set root=/dev/mmcblk0p5, and the kernel now runs from the second copy. A later update targets slot 1, and the system ping-pongs between the two.

From here, a script triggered on USB insertion — mount, check for a .swu, run swupdate, reboot — gives hands-off local updates. Pointing swupdate at a remote server instead is the same mechanism with a different image source.


Adapted from Bootlin's "Building a Linux system for the STM32MP1" blog series. The explanations are rewritten; the configuration snippets and logs are from running the steps.