> ## Content Index
> Fetch the complete content index at: https://www.smallstepsystems.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Updating U-Boot with an A/B Strategy
- URL: https://www.smallstepsystems.com/updating-u-boot-with-an-a-b-strategy/
- Published: 2024-05-13T07:34:36.000Z
- Updated: 2026-05-26T12:52:23.000Z
- Description: By default, eMMC storage comes with two boot partitions. Two partitions enable an A/B strategy for U-Boot updates. Moreover, U-Boot can automatically start from the other partition, if the first is corrupt or empty.
- Author: Burkhard Stubert
- Tags: Embedded Linux, ota update, u-boot, cybersecurity

Unlike consumer devices, machines, ECUs or medical devices are in the field for 15 or more years. There inevitably comes the time in their life, when you must update their boot loader. I'll show you how to do this from an embedded Linux system running on an NXP iMX8M Plus. You can adapt the method to other SoCs and use it for OTA updates, as well.

## Introduction

If you don't have to update the boot loader over the lifetime of your device, be happy and don't touch the boot loader. This "strategy" might work for a consumer device with a lifetime of three years or less. However, it is very unlikely if your device is in the field for 10, 15 or even 20 years, which is normal for devices in many non-consumer industries. Fixing a bug, applying a new feature or finding a security issue in compression, encryption, communication or any other functionality used by the boot loader is almost certain. The do-nothing strategy won't work in this case. This leaves you with two updates options: *parallel boot loaders* and *eMMC boot partitions* (see Options 3 and 4 in Drew Moseley's excellent article [Considerations for Updating the Bootloader Over-the-Air (OTA)](https://embeddedcomputing.com/technology/iot/device-management/considerations-for-updating-the-over-the-air-bootloader?ref=smallstepsystems.com)).

Many boards can boot from multiple sources like a removable SD card, an internal eMMC storage (lovingly called "NAND flash" or simply "flash"), a USB drive and a network server. The SD card is often used for recovery, when you bricked your board (e.g., when u-boot or the Linux kernel fail to start) or when you install the boot loader and root file system in the eMMC storage for the very first time. Similarly, you boot your device from SD card and install a new boot loader in one of the eMMC boot partitions. Older devices support only one eMMC boot partition, whereas newer devices support two eMMC partitions (see *Section 7.2.1 Boot Partition* of the [eMMC Standard JESD84-A43](https://community.nxp.com/pwmxy87654/attachments/pwmxy87654/lpc/27039/1/JESD84-A43.pdf?ref=smallstepsystems.com) (PDF)). If the boot loader update fails, you still have a working system on the SD card and can try the update again.

In production, devices typically run from the internal eMMC storage. Access to the SD card slot may be tricky for users. Devices might not even have an SD card slot. With the now common two boot partitions, you can apply an A/B strategy for boot loader updates. If the device booted from partition A (the active partition), you install the new boot loader in partition B (the inactive partition) and vice versa. If the installation succeeds, you switch to the new partition and reboot the device from the new boot loader. Otherwise, you stay on the current boot partition.

You can also perform A/B updates of boot loaders over the air (OTA). I recently implemented this with an SwUpdate client on a Variscite iMX8M Plus SoM. In this post, I am going to teach you how to update u-boot with an A/B strategy from the Linux prompt. I offer the integration of u-boot updates with OTA update clients like [SwUpdate](https://sbabic.github.io/swupdate/index.html?ref=smallstepsystems.com), [RAUC](https://rauc.io/?ref=smallstepsystems.com) and [Mender](https://mender.io/?ref=smallstepsystems.com) as a service.

## Context

My device is powered by a [DART-MX8M-PLUS SoM](https://www.variscite.com/product/system-on-module-som/cortex-a53-krait/dart-mx8m-plus-nxp-i-mx-8m-plus/?ref=smallstepsystems.com) from Variscite (see Variscite's [Developer Guide](https://variwiki.com/index.php?title=DART-MX8M-PLUS%5FYocto&release=mx8mp-yocto-kirkstone-5.15-2.0.x-v1.2&ref=smallstepsystems.com)). I built a custom u-boot and Linux image with Yocto 4.0 Kirkstone following Variscite's instructions in [Build Yocto from Source Code](https://variwiki.com/index.php?title=Yocto%5FBuild%5FRelease&release=mx8mp-yocto-kirkstone-5.15-2.0.x-v1.2&ref=smallstepsystems.com) \- naturally with a couple of improvements from my side. I had to add back the packages `imx-boot` and `mmc-utils` to the image, after minimising the images a bit too aggressively.

```
IMAGE_INSTALL:append += " imx-boot mmc-utils"
```

The package `imx-boot` adds some iMX-specific features to u-boot. The package `mmc-utils` provides the utility `mmc` to switch between the two boot paritions or to find out the current u-boot version or the current boot partition.

Variscite provides [a recovery image for SD cards](https://variwiki.com/index.php?title=Yocto%5FRecovery%5FSD%5Fcard&release=mx8mp-yocto-kirkstone-5.15-2.0.x-v1.2&ref=smallstepsystems.com). For getting started, it's enough to download the pre-built image. This image contains the script `/usr/bin/install_yocto.sh` to partition the internal eMMC storage into two root partitions, one data partition and two boot partitions. The script installs the root file system `rootfs.ext4.gz` and the boot loader `imx-boot-sd.bin` from the directory `/opt/images/Yocto` in the first rootfs partition and in the first boot partition, respectively. The second rootfs partition and the second boot partition remain empty. You achieve this with the command

```
# install_yocto.sh -u
```

Option `-u` creates the second rootfs partition enabling an [A/B strategy](https://burkhardstubert.substack.com/i/140641650/double-copy-or-ab-strategy) for updating the rootfs. The two boot partitions will always be available without any help from the script, because they are mandatory from version 4.1 of the eMMC standard. The DART-MX8M-PLUS SoM, for example, implements version 5.1 of the eMMC standard. My partitioning of the internal eMMC storage looks like this:

```
# lsblk
NAME         MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
mmcblk2      179:0    0 14.6G  0 disk
|-mmcblk2p1  179:1    0    4G  0 part /
|-mmcblk2p2  179:2    0    4G  0 part /run/media/mmcblk2p2
`-mmcblk2p3  179:3    0  6.6G  0 part /data
mmcblk2boot0 179:32   0    4M  1 disk
mmcblk2boot1 179:64   0    4M  1 disk
```

Note that I modified the installation script and the image recipe to adapt the partitioning to my customer's requirement. By default, the data partition `mmcblk2p3` has a size of 200 MB. Each rootfs partition takes up half of the remaining size of `mmcblk2`. How to change the partition size will be the topic of an upcoming article.

For the remainder of this article, it's only important that the two boot partitions `mmcblk2boot0` and `mmcblk2boot1` exist. Following the instructions of the articles [Build Yocto from Source Code](https://variwiki.com/index.php?title=Yocto%5FBuild%5FRelease&release=mx8mp-yocto-kirkstone-5.15-2.0.x-v1.2&ref=smallstepsystems.com) and [Yocto Recovery SD Card](https://variwiki.com/index.php?title=Yocto%5FRecovery%5FSD%5Fcard&release=mx8mp-yocto-kirkstone-5.15-2.0.x-v1.2&ref=smallstepsystems.com) should get you to this point. The procedure for the boards of other vendors will vary. However, the way how to install u-boot in the boot partitions should be the same. Please let me know about your experience with other boards in the comments.

## How to Distinguish U-Boot Versions

When you install a new u-boot version in the inactive boot partition, you want to check whether the system really boots from this new version. The first messages in the serial console after power-up tell you the u-boot version.

```
U-Boot SPL 2022.04-lf_v2022.04_var01+gf6390c6805 (Jul 10 2023 - 12:45:06 +0000)
SEC0:  RNG instantiated
Normal Boot
Trying to boot from BOOTROM
image offset 0x0, pagesize 0x200, ivt offset 0x0

U-Boot 2022.04-lf_v2022.04_var01+gf6390c6805 (Jul 10 2023 - 12:45:06 +0000)
```

The last message gives the u-boot version as `2022.04-lf_v2022.04_var01+gf6390c6805`. This makes it easy to figure out from which version the system boots. Now, you could build a new u-boot version, say, `lf_2022.04_var02` or even `lf_v2023.04_var01`. This might be too big a change and might lead to long debugging sessions to figure out why u-boot or the kernel don't boot any more. This would only distract you from your task at hand. Just changing the version part `lf_v2022.04_var01` to, say, `lf_v2024.05_abc01` would be enough.

You are lucky. For your experiments, you can temporarily change `LOCALVERSION` in `meta-variscite-bsp/recipes-bsp/u-boot/u-boot-variscite.bb`:

```
LOCALVERSION ?= "-lf_v2024.05_abc01"
```

`LOCALVERSION` is not specific to Variscite SoMs, because the assignment in `u-boot-variscite.bb` overrides the earlier assignment in `meta-freescale/classes/fsl-u-boot-localversion.bbclass`. It should work for most SoCs of the iMX family.

The Yocto build produces a boot loader `imx-boot-imx8mp-var-dart-sd.bin-flash_evk` in [DEPLOY\_DIR\_IMAGE](https://docs.yoctoproject.org/ref-manual/variables.html?ref=smallstepsystems.com#term-DEPLOY%5FDIR%5FIMAGE) with the new version, where `imx8mp-var-dart` is the machine. A quick check confirms this:

```
$ cd $DEPLOY_DIR_IMAGE
$ strings imx-boot-imx8mp-var-dart-sd.bin-flash_evk | grep "U-Boot 20"
U-Boot 2022.04-lf_v2024.05_abc01+gf6390c6805
```

Similarly, you can check the u-boot version on the board. The boot partition `/dev/mmcblk2boot0` contains the u-boot binary with the old version `lf_v2022.04_var01`.

```
# strings /dev/mmcblk2boot0 | grep "U-Boot 20"
U-Boot 2022.04-lf_v2022.04_var01+gf6390c6805
```

You are now well-equipped to verify which u-boot version you installed in which boot partition.

## Installing U-Boot into a Boot Partition

The situation on the board looks like this:

- Boot partition 1 (`/dev/mmcblk2boot0`) contains u-boot version `lf_v2022.04_var01`. It is the active boot partition, from which the board boots.
- Boot partition 2 (`/dev/mmcblk2boot1`) contains any u-boot version or no u-boot at all. It is the inactive boot partition, in which you want to install the new u-boot version.
- You transferred the new u-boot binary to the directory `/home/root` on the device.

The boot messages don't give a clue about the active boot partition. From the Linux prompt, you can find out the active boot partition with this command:

```
# mmc extcsd read /dev/mmcblk2 | grep -A2 PARTITION_CONFIG
Boot configuration bytes [PARTITION_CONFIG: 0x08]
 Boot Partition 1 enabled
 No access to boot partition
```

The command before the pipe lists the configuration of the block device `/dev/mmcblk2`. The `grep` after the pipe picks out the boot configuration. The bits of `PARTITION_CONFIG` have the following meaning (see also [Boot configuration](https://developer.embeddedartists.com/docs-app/guides/emmc/?ref=smallstepsystems.com#boot-configuration)):

- Bits 0-2 - partition access: 0 = no access to boot partition; 1 = read/write access to boot partition 1; 2 = read/write access to boot partition 2\. Current value: 0.
- Bits 3-5 - partition enable: 0 = boot not enabled; 1 = boot partition 1 enabled; 2 = boot partition 2 enabled; 7 = user area enabled for boot. Current value: 1
- Bit 6 - acknowledge: 0 = no boot acknowledge; 1 = boot acknowledge during boot. Current value: 0.

The second and third output messages above are textual descriptions of the current values of "partition enable" and "partition access", respectively. In short, boot partition 1 is the active partition.

The next three commands install the new u-boot binary into the inactive boot partition 2:

```
# echo 0 > /sys/class/block/mmcblk2boot1/force_ro
# dd if=/home/root/imx-boot-imx8mp-var-dart-sd.bin-flash_evk of=/dev/mmcblk2boot1
2858+1 records in
2858+1 records out
1463488 bytes (1.5 MB, 1.4 MiB) copied, 0.085924 s, 17.0 MB/s
# echo 1 > /sys/class/block/mmcblk2boot1/force_ro
```

By default, boot partitions are read-only. So, the file `/sys/class/block/mmcblk2boot1/force_ro` contains `1`. The first command makes boot partition 2, `/dev/mmcblk2boot1`, writable. The second command writes the new u-boot binary `/home/root/imx-boot-imx8mp-var-dart-sd.bin-flash_evk` into boot partition 2, `/dev/mmcblk2boot1`. The third command makes boot partition 2 read-only again.

You can check that the new u-boot version ended up in boot partition 2 with this command:

```
# strings /dev/mmcblk2boot1 | grep "U-Boot 20"
U-Boot 2022.04-lf_v2024.05_abc01+gf6390c6805
```

The next command switches the boot partition from 1 to 2:

```
# mmc bootpart enable 2 0 /dev/mmcblk2
```

The general form of the command is:

```
mmc bootpart enable <boot-partition> <send-ack> <device>
```

`<boot-partition>` is the boot partition, which is always the number `X` from the device file name `/dev/mmcblk2bootX` plus 1\. `<send-ack>` specifies, whether the `<device>` shall acknowledge the change of the boot partition (1 for acknowledge and 0 for no acknowledge). `<device>` is the device file name of the block device and not of one of the boot partitions (see [Switch boot partition](https://developer.embeddedartists.com/docs-app/guides/emmc/?ref=smallstepsystems.com#switch-boot-partition) for more details).

In the running example, `<device>` must be `/dev/mmcblk2` and **not** `/dev/mmcblk2boot1`. Using the latter is a pretty sure way to brick the board. Your way out of this mess is to boot the board from the recovery SD card and re-install u-boot and rootfs in the eMMC storage.

A quick check confirms that boot partition 2 is now active.

```
# mmc extcsd read /dev/mmcblk2 | grep -A2 PARTITION_CONFIG
Boot configuration bytes [PARTITION_CONFIG: 0x10]
 Boot Partition 2 enabled
 No access to boot partition
```

After rebooting the device, the first messages now show the new version `lf_v2024.05_abc01`:

```
U-Boot SPL 2022.04-lf_v2024.05_abc01+gf6390c6 (Jul 10 2023 - 12:45:06 +0000)
SEC0:  RNG instantiated
Normal Boot
Trying to boot from BOOTROM
image offset 0x0, pagesize 0x200, ivt offset 0x0

U-Boot 2022.04-lf_v2024.05_abc01+gf6390c6 (Jul 10 2023 - 12:45:06 +0000)

```

Checking the boot configuration reveals that boot partition 2 is now active: `PARTITION_CONFIG = 0x10`. If you want to switch to boot partition 1, you replace `mmcblk2boot1` by `mmcblk2boot0` and `mmc bootpart enable 2` by `mmc bootpart enable 1` in the above commands.

This gives you an A/B strategy for updating u-boot. You update the inactive boot partition and switch to it only if the update succeeded. If you have tested the new u-boot version, the risk of bricking your board is nearly 0.