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
| Approach | Tools |
|---|---|
| Package-level (binary distro only) | the distro's package manager |
| Whole-image, block-level | swupdate, Mender, RAUC |
| File-based | OSTree |
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 storageCONFIG_BLK_DEV_SD— SCSI disk supportCONFIG_USB_STORAGECONFIG_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 existingsupport/scripts/genimage.sh.Filesystem images— enable gzip compression for the ext4 rootfs, sorootfs.ext4.gzis 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.