OTA Guide

Introduction

OTA (Over-the-Air) is a method for delivering firmware updates to remote devices using a network connection. Although the name implies a wireless connection, updates received over a wired connection (such as Ethernet) are still commonly referred to as OTA updates.

Zephyr supports multiple OTA upgrade schemes, as shown in the following table:

Example

Path

Golioth

External repository

Eclipse hawkBit

zephyr/samples/subsys/mgmt/hawkbit/

UpdateHub

zephyr/samples/subsys/mgmt/updatehub/

SMP Server

zephyr/samples/subsys/mgmt/mcumgr/smp_svr/

Lightweight M2M (LWM2M)

zephyr/samples/net/lwm2m_client/

Among them, SMP Server provides a complete OTA solution, combining MCUboot bootloader with MCUmgr device management framework to achieve secure and reliable firmware upgrades. The OTA upgrade process will be introduced based on this example.

OTA Key Components

Implementing a secure OTA update primarily relies on the collaboration of three core components:

  • MCUboot: An open-source, secure bootloader. It is responsible for verifying the integrity and authenticity of application firmware during device boot, and managing firmware switching and update processes in Flash storage areas.

  • SMP Server: A Simple Management Protocol service built into your application. It is responsible for receiving and processing various commands from external management tool MCUmgr, including firmware upload, status query, etc.

  • MCUmgr: A client management tool running on your development host. You can use it to send commands to the SMP Server on the device side through various methods such as UART/BT/UDP, thereby triggering and controlling the entire OTA process.

OTA Module Hierarchy Diagram

Implementing secure OTA updates involves the following Zephyr submodules, with their hierarchical relationship shown in the diagram:

../_images/zephyr_ota_subsystem.svg

Upgrade Process

Using the above OTA Key Components, a typical upgrade flow diagram is shown below:

../_images/zephyr_ota_upgrade_flow.svg

Note

Firmware Storage Layout

Flash Partition Mapping

Flash map is a partition table that divides device flash memory into independent areas (flash areas), each assigned a unique numerical ID. MCUboot and applications use these IDs to find specific physical addresses and sizes for reading, writing, erasing, upgrading, and other operations.

Typically, we allocate two slots for each firmware: Primary slot and Secondary slot. The following is a Flash partition configuration diagram for a two-firmware scenario:

Flash Layout in Device Tree
&flash0 {
   reg = <0x103FF000 DT_SIZE_M(4)>;
   status = "okay";
   partitions {
      compatible = "fixed-partitions";
      #address-cells = <1>;
      #size-cells = <1>;

      /* flash area ID 0 */
      boot_partition: partition@0 {
         label = "bootloader";
         reg = <0x00000000 0x00014000>;
         read-only;
      };

      /* Allocate primary slot (slot0_partition) and secondary slot (slot1_partition) for AP (firmware 0) */
      /* flash area ID 1 */
      slot0_partition: partition@14000 {
         label = "image-0";
         reg = <0x00014000 0x00080000>;
      };

      /* flash area ID 2 */
      slot1_partition: partition@94000 {
         label = "image-1";
         reg = <0x00094000 0x00080000>;
      };

      /* Allocate primary slot (slot2_partition) and secondary slot (slot3_partition) for NP (firmware 1) */
      /* flash area ID 3 */
      slot2_partition: partition@114000 {
         label = "image-2";
         reg = <0x00114000 0x00080000>;
      };

      /* flash area ID 4 */
      slot3_partition: partition@194000 {
         label = "image-3";
         reg = <0x00194000 0x00080000>;
      };
   };
};

Firmware Format

In Zephyr’s OTA scenario, application firmware booted by MCUboot must conform to the firmware format defined by MCUboot, including: Firmware Header, Firmware Body, TLV Area (Optional) and Firmware Trailer. The schematic diagram is shown below:

Note

  • The diagram shows the format requirements for application firmware, not the firmware format of MCUboot itself.

Firmware Header

The Zephyr build system reserves 0x200 bytes at the start of the firmware for the header (ih_hdr_size = 0x200).

#define IMAGE_MAGIC                 0x96f3b83d
#define IMAGE_HEADER_SIZE           32

STRUCT_PACKED image_version {
   uint8_t iv_major;
   uint8_t iv_minor;
   uint16_t iv_revision;
   uint32_t iv_build_num;
};

/** Image header.  All fields are in little endian byte order. */
STRUCT_PACKED image_header {
   uint32_t ih_magic;
   uint32_t ih_load_addr;
   uint16_t ih_hdr_size;               /* Size of image header (bytes). */
   uint16_t ih_protect_tlv_size;       /* Size of protected TLV area (bytes). */
   uint32_t ih_img_size;               /* Does not include header. */
   uint32_t ih_flags;                  /* IMAGE_F_[...]. */
   struct image_version ih_ver;
   uint32_t _pad1;
};

TLV Area (Type-Length-Value)

The TLV area is located after the firmware body and is optional, typically storing fields such as hash, signature, and security counter.

When a protected TLV area exists, the IMAGE_TLV_PROT_INFO_MAGIC information header must be present, and this area is included in the hash calculation. If not present, the hash only covers “firmware header + firmware body”.

To ensure rollback protection is effective, IMAGE_TLV_SEC_CNT, 0x50 needs to be written to the protected TLV area. Using imgtool’s --security-counter option will place it in the protected TLV area.

TLV Format
#define IMAGE_TLV_INFO_MAGIC        0x6907
#define IMAGE_TLV_PROT_INFO_MAGIC   0x6908

/** Image TLV header.  All fields in little endian. */
STRUCT_PACKED image_tlv_info {
   uint16_t it_magic;
   uint16_t it_tlv_tot;                /* size of TLV area (including tlv_info header) */
};

/** Image trailer TLV format. All fields in little endian. */
STRUCT_PACKED image_tlv {
   uint16_t it_type;                   /* IMAGE_TLV_[...]. */
   uint16_t it_len;                    /* Data length (not including TLV header). */
};
Common TLV Types Related to OTA
#define IMAGE_TLV_KEYHASH           0x01   /* hash of the public key */
#define IMAGE_TLV_SHA256            0x10   /* SHA256 of image hdr and body */
#define IMAGE_TLV_RSA2048_PSS       0x20   /* RSA2048 of hash output */
#define IMAGE_TLV_ECDSA224          0x21   /* ECDSA of hash output - Not supported anymore */
#define IMAGE_TLV_ECDSA_SIG         0x22   /* ECDSA of hash output */
#define IMAGE_TLV_RSA3072_PSS       0x23   /* RSA3072 of hash output */
#define IMAGE_TLV_ED25519           0x24   /* ED25519 of hash output */
#define IMAGE_TLV_ENC_RSA2048       0x30   /* Key encrypted with RSA-OAEP-2048 */
#define IMAGE_TLV_ENC_KW            0x31   /* Key encrypted with AES-KW-128 or 256 */
#define IMAGE_TLV_ENC_EC256         0x32   /* Key encrypted with ECIES-P256 */
#define IMAGE_TLV_ENC_X25519        0x33   /* Key encrypted with ECIES-X25519 */
#define IMAGE_TLV_SEC_CNT           0x50   /* security counter */

Firmware Trailer

Used to record swap/status metadata at the end of the slot, and this space cannot be used to store firmware body.

The firmware Trailer structure is as follows (in bytes):

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~                                                               ~
~    Swap status (BOOT_MAX_IMG_SECTORS * min-write-size * s)    ~
~                                                               ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                 Encryption key 0 (16 octets) [*]              |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    0xff padding as needed                     |
|  (BOOT_MAX_ALIGN minus 16 octets from Encryption key 0) [*]   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                 Encryption key 1 (16 octets) [*]              |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    0xff padding as needed                     |
|  (BOOT_MAX_ALIGN minus 16 octets from Encryption key 1) [*]   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      Swap size (4 octets)                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    0xff padding as needed                     |
|        (BOOT_MAX_ALIGN minus 4 octets from Swap size)         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Swap info   |  0xff padding (BOOT_MAX_ALIGN minus 1 octet)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Copy done   |  0xff padding (BOOT_MAX_ALIGN minus 1 octet)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Image OK    |  0xff padding (BOOT_MAX_ALIGN minus 1 octet)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    0xff padding as needed                     |
|         (BOOT_MAX_ALIGN minus 16 octets from MAGIC)           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       MAGIC (16 octets)                       |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Parameter Description
BOOT_MAX_IMG_SECTORS:

Default value is the number of sectors occupied by slot0_partition.

min-write-size:

Flash minimum write granularity.

s:

s = 3 (Swap_using_move), s= 2 (Swap_using_offset)

Swap status:

Per-sector Swap Status record for recovery after power loss.

Encryption keys:

(Optional) Encryption keys, only present when encryption is enabled.

Swap size:

Total amount of data to be moved in this swap (covering firmware body + TLV area).

Swap info:

1 byte valid, BOOT_MAX_ALIGN aligned; lower 4 bits are Swap Type, upper 4 bits are firmware number.

Copy done:

1 byte valid, BOOT_MAX_ALIGN aligned; indicates copy phase completion status: 0x01=Set, 0xFF=Unset.

Image OK:

1 byte valid, BOOT_MAX_ALIGN aligned; indicates whether new firmware has been confirmed, unconfirmed will revert on next boot. 0x01=Set, 0xFF=Unset.

MAGIC:

16 bytes, marker for trailer area validity.

  • When BOOT_MAX_ALIGN=8, it is a fixed 16-byte pattern:

    const union boot_img_magic_t boot_img_magic = {
       .val = {
          0x77, 0xc2, 0x95, 0xf3,
          0x60, 0xd2, 0xef, 0x7f,
          0x35, 0x52, 0x50, 0x0f,
          0x2c, 0xb6, 0x79, 0x80
       }
    };
    
  • Otherwise, the first 2 bytes are the alignment value, and the last 14 bytes are a fixed pattern:

    const union boot_img_magic_t boot_img_magic = {
       .align = BOOT_MAX_ALIGN,
       .magic = {
          0x2d, 0xe1,
          0x5d, 0x29, 0x41, 0x0b,
          0x8d, 0x77, 0x67, 0x9c,
          0x11, 0x0f, 0x1f, 0x8a
       }
    };
    

Note

  • This area is located at the end of the firmware slot, not immediately following the firmware body. Therefore, the allocated slot needs to reserve Trailer space and be sector-aligned.

Upgrade Mechanism

MCUboot supports multiple firmware upgrade methods, as shown in the following table. For details, please refer to MCUboot official documentation .

Upgrade Method

Characteristics

Upgrade only

Directly overwrites old firmware with new firmware, no swap, no rollback

Swap using scratch

Uses scratch partition as intermediate storage to complete new/old firmware swap, rollback supported

Swap using offset

Relies on offset layout within partitions for swapping, no scratch partition needed, rollback supported

Swap using move

Completes swap by moving data, no scratch partition needed, rollback supported

Direct XIP

Execute in place, no swap needed

RAM load

Load firmware completely into RAM before execution

Firmware loader

Upgrade through dedicated loader

Currently, the following two upgrade methods are supported:

Swap_using_move

Swap_using_move is a firmware swap algorithm that does not require temporary storage (scratch), suitable for dual-slot (primary slot/secondary slot) OTA upgrade scenarios. Its core is to complete firmware swap by moving all primary slot sectors down by one position and alternately copying primary and secondary slot contents.

This upgrade method can be enabled by configuring the macro CONFIG_BOOT_SWAP_USING_MOVE. When using –sysbuild, configure SB_CONFIG_MCUBOOT_MODE_SWAP_USING_MOVE=y in the application’s sysbuild.conf

Working Principle

  1. Sector Move Down: First, move all primary slot sectors down by one sector position.

  2. Alternate Copy: Starting from the 1st sector, execute sequentially:

  3. Copy secondary slot sector N to primary slot sector N;

  4. Repeat until all sectors are swapped.

    ../_images/zephyr_ota_swap_using_move.svg

From the diagram, each sector swap has 3 states:

  1. Primary slot[N] → Primary slot[N+1] completed

  2. Secondary slot[N] → Primary slot[N] completed

  3. Primary slot[N+1] → Secondary slot[N] completed

Swap_using_offset

Swap_using_offset is a firmware swap algorithm that does not require temporary storage (scratch), an enhanced version of Swap_using_move. Its core is to use one sector of the secondary slot as a buffer and alternately copy primary and secondary slot contents to complete firmware swap.

Enable by configuring the macro CONFIG_BOOT_SWAP_USING_OFFSET; when using –sysbuild, configure SB_CONFIG_MCUBOOT_MODE_SWAP_USING_OFFSET=y in the application’s sysbuild.conf.

Working Principle

  1. Firmware Placement: The new firmware to be upgraded must be stored starting from the second sector of the secondary slot.

  2. Alternate Copy: Starting from the 1st sector, execute sequentially:

  3. Copy primary slot sector N to secondary slot sector N;

  4. Copy secondary slot sector N+1 to primary slot sector N;

  5. Repeat until all sectors are swapped.

    ../_images/zephyr_ota_swap_using_offset.svg

From the diagram, each sector swap has 2 states:

  1. Primary slot[N] → Secondary slot[N] completed

  2. Secondary slot[N+1] → Primary slot[N] completed

Swap Management

Swap Type

In non-recovery scenarios (no abnormal interruption during the upgrade process), MCUboot determines the swap type to execute based on the flag bits in the firmware trailer. Due to Flash hardware limitations, the trailer design is not intuitive, and directly reading its bytes makes it difficult to restore the device state. Therefore, MCUboot maps “various possible trailer combination states” to “swap types” and determines them in priority order.

State I scenario: During the risk window before executing REVERT in Swap_using_offset, recording magic and copy-done to the secondary slot, the next boot will continue to execute REVERT.

State I (swap using offset only)
                 | primary slot | secondary slot |
-----------------+--------------+----------------|
           magic | Any          | Good           |
        image-ok | Any          | Unset          |
       copy-done | Any          | Set            |
-----------------+--------------+----------------'
 result: BOOT_SWAP_TYPE_REVERT                   |
-------------------------------------------------'

State II scenario: Secondary slot firmware is valid and unconfirmed, execute “test swap”. The system will swap from secondary slot to primary slot and boot. The new firmware needs to set image-ok in the application for confirmation; if not confirmed, the next boot will revert.

State II
                 | primary slot | secondary slot |
-----------------+--------------+----------------|
           magic | Any          | Good           |
        image-ok | Any          | Unset          |
       copy-done | Any          | Any            |
-----------------+--------------+----------------'
 result: BOOT_SWAP_TYPE_TEST                     |
-------------------------------------------------'

State III scenario: Secondary slot firmware is pre-marked as image-ok, execute “permanent swap”, no rollback after upgrade.

State III
                 | primary slot | secondary slot |
-----------------+--------------+----------------|
           magic | Any          | Good           |
        image-ok | Any          | 0x01           |
       copy-done | Any          | Any            |
-----------------+--------------+----------------'
 result: BOOT_SWAP_TYPE_PERM                     |
-------------------------------------------------'

State IV scenario: Primary slot has completed copy (copy-done=Set), but new firmware is unconfirmed (image-ok=Unset), so revert is executed on this boot, restoring to the old firmware.

State IV
                 | primary slot | secondary slot |
-----------------+--------------+----------------|
           magic | Good         | Any            |
        image-ok | 0xff         | Any            |
       copy-done | 0x01         | Any            |
-----------------+--------------+----------------'
 result: BOOT_SWAP_TYPE_REVERT                   |
-------------------------------------------------'

If none of the above State I-IV match, MCUboot will not attempt to swap images, but will execute one of the three swap types in the State V table.

State V
                 | primary slot | secondary slot |
-----------------+--------------+----------------|
           magic | Any          | Any            |
        image-ok | Any          | Any            |
       copy-done | Any          | Any            |
-----------------+--------------+----------------'
 result: BOOT_SWAP_TYPE_NONE,                    |
         BOOT_SWAP_TYPE_FAIL, or                 |
         BOOT_SWAP_TYPE_PANIC                    |
-------------------------------------------------'

Note

  • Lower State number indicates higher priority. Once a state is matched, the corresponding swap type is immediately determined and matching stops.

  • Swap types:

    • BOOT_SWAP_TYPE_NONE: No upgrade, directly boot primary slot firmware.

    • BOOT_SWAP_TYPE_TEST: Test run; if not confirmed (write image-ok=0x01), revert on next boot.

    • BOOT_SWAP_TYPE_PERM: Permanent swap.

    • BOOT_SWAP_TYPE_REVERT: Previous TEST was not confirmed, revert to the previous old firmware.

    • BOOT_SWAP_TYPE_FAIL: Target firmware is invalid (verification failed, etc.).

    • BOOT_SWAP_TYPE_PANIC: Swap encountered unrecoverable error (abort).

Swap Status

Swap status is used for power-loss recovery and is recorded in the Trailer of the primary slot. When a reboot occurs during firmware swap, MCUboot can restore the state based on the recorded swap status and continue the unfinished swap operation. This area consists of a series of “single-byte records”, each written independently and aligned according to the device’s minimum write granularity. The normal swap process is as follows:

  1. Initialization: Erase both primary and secondary slot Trailers; then write swap_info, swap_size, and MAGIC markers in the primary slot Trailer.

  2. Per-sector Swap: Only swap sectors actually occupied by firmware (including TLV), and record the status of each sector according to the selected algorithm.

  3. Finalization: After completion, write copy done in the primary slot Trailer to mark the swap as complete.

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|sec127,state 0 |sec127,state 1 |sec127,state 2 |sec126,state 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|sec126,state 1 |sec126,state 2 |sec125,state 0 |sec125,state 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|sec125,state 2 |                                               |
+-+-+-+-+-+-+-+-+                                               +
~                                                               ~
~               [Records for indices 124 through 1              ~
~                                                               ~
~               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~               |sec000,state 0 |sec000,state 1 |sec000,state 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Note

  • The diagram assumes min-write-size = 1, where each “sector index” corresponds to 3 state records, applicable to Swap_using_move mode; whereas in Swap_using_offset mode, each sector has only 2 state records, requiring less total space.

Anti-Rollback

To prevent devices from being downgraded to firmware versions with known vulnerabilities, MCUboot supports anti-rollback policies, divided into software anti-rollback and hardware anti-rollback. Software anti-rollback can be further divided into version number-based and security counter-based approaches.

Software Anti-Rollback (Version Number Based)

Version number-based software anti-rollback only exists in !BOOT_DIRECT_XIP upgrade mode. This method is purely software-based anti-rollback and does not prevent physical attacks (such as Flash erase/rewrite).

  1. Principle:

    • MCUboot compares firmware version numbers at each boot.

    • If the new firmware version number is lower than the currently running firmware version number, boot is rejected.

    • If the new firmware version number is greater than or equal to the currently running firmware version, boot is allowed.

  2. Configuration:

    CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y
    
  3. Embed version number during firmware signing:

    # Example: Set in application prj.conf or board-specific configuration
    CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="0.0.2"
    

Software Anti-Rollback (Security Counter Based)

Security counter-based software anti-rollback only exists in (BOOT_SWAP_USING_MOVE || BOOT_SWAP_USING_SCRATCH || BOOT_SWAP_USING_OFFSET) mode. This method is purely software-based anti-rollback and does not prevent physical attacks (such as Flash erase/rewrite).

  1. Principle:

    • MCUboot compares security counter values at each boot.

    • If the new firmware’s security counter is less than the currently running firmware’s security counter, boot is rejected.

    • If the new firmware’s security counter is greater than or equal to the currently running firmware’s security counter, boot is allowed.

  2. Configuration:

    CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y        # security counter option depends on this configuration
    CONFIG_MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y
    
  3. Embed security counter during firmware signing:

    # Example: Set in application prj.conf or board-specific configuration
    CONFIG_MCUBOOT_IMGTOOL_SIGN_SECURITY_COUNTER=2
    

Note

  • Currently, NP firmware always uses the same security counter as AP.

Hardware Anti-Rollback (OTP Based)

  1. Principle:

    • MCUboot saves the currently accepted security counter to a hardware counter (OTP).

    • If the new firmware’s security counter is less than the OTP recorded value, boot is rejected.

    • If the new firmware’s security counter is greater than or equal to the OTP recorded value, boot is allowed.

  2. Configuration:

    CONFIG_MCUBOOT_HW_DOWNGRADE_PREVENTION=y
    
  3. Embed security counter during firmware signing:

    # Example: Set in application prj.conf or board-specific configuration
    CONFIG_MCUBOOT_IMGTOOL_SIGN_SECURITY_COUNTER=2
    

Note

  • Currently, NP firmware always uses the same security counter as AP.

  • Both NP and AP firmware hardware counters have upper limits; please only update when necessary, as reaching the upper limit may cause device boot failure.

Quick Start

Environment Setup and Configuration

1. Install MCUmgr Command Line Tool on PC Host

You can download the corresponding platform’s newtmgr tool package from the Apache Mynewt official website:

Add the extracted newtmgr tool package path to the environment variable of the corresponding platform. Execute newtmgr –help in the command line. If global options are displayed, the configuration is successful.

2. Zephyr Device-side Project Configuration

Sample path: zephyr/samples/subsys/mgmt/mcumgr/smp_svr.

Flash Partition

The flash partition layout used by the smp_svr sample can be configured with reference to Flash Partition Mapping.

Basic MCUboot and SMP Configuration

You need to ensure that the smp_svr sample’s configuration file (such as prj.conf or board-specific configuration file) contains the following key configurations:

# Enable MCUboot bootloader
CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_UPDATEABLE_IMAGE_NUMBER=2                            # AP (firmware 0) and NP (firmware 1)
CONFIG_MCUMGR_GRP_IMG_ALLOW_CONFIRM_NON_ACTIVE_IMAGE_ANY=y  # Allow confirmation of non-running firmware

# Enable MCUmgr necessary command groups
CONFIG_MCUMGR_GRP_IMG=y                                     # Firmware management command group
CONFIG_MCUMGR_GRP_OS=y                                      # Basic OS command group

# Enable MCUmgr and SMP services
CONFIG_MCUMGR=y
CONFIG_MCUMGR_SMP_UART=y                                    # Use UART as transport layer

# Configure UART buffer and MTU (Maximum Transmission Unit)
CONFIG_MCUMGR_TRANSPORT_NETBUF_SIZE=1024
CONFIG_UART_MCUMGR_RX_BUF_SIZE=256                          # Needs to match MTU set in MCUmgr tool

You need to specify the upgrade method in the application’s sysbuild configuration file sysbuild.conf, otherwise the top level will choose swap_using_move method by default.

# Configure upgrade method
SB_CONFIG_MCUBOOT_MODE_SWAP_USING_OFFSET=y

UART Transport Layer Configuration

You need to specify the UART pins used in the device tree (overlay file) and ensure the UART port is properly configured and enabled.

/ {
   chosen {
      zephyr,uart-mcumgr = &uart2;
   };
};

&uart2 {
   pinctrl-0 = <&uart2_default>;
   pinctrl-names = "default";
   current-speed = <115200>;
   status = "okay";
};

Build Command

./nuwa.py build -d rtl8721f_evb//mcuboot -a zephyr/samples/subsys/mgmt/mcumgr/smp_svr --sysbuild -p

Or

west build -b rtl8721f_evb//mcuboot zephyr/samples/subsys/mgmt/mcumgr/smp_svr  --sysbuild -p

After device power-on/reset, open a serial terminal and confirm that the sample has started and printed initialization logs:

*** Booting Zephyr OS build f50378385a85 ***
<inf> smp_sample: build time: Jan  7 2026 11:52:35

OTA Upgrade Operation Process

Assume the device is already running firmware containing the smp_svr sample, and the selected UART port is connected to the PC.

Establish MCUmgr Connection

Configure Connection

Command format: newtmgr conn add <connection_name> type=<type> connstring="<key=value[,key=value...]>"

Example:

$ newtmgr conn add myConn type=serial connstring=\"dev=<COM3>,baud=115200,mtu=256\"
Connection profile myConn successfully added

Note

  • Baud rate configuration needs to be consistent with the setting in device tree. MTU configuration needs to be less than or equal to CONFIG_UART_MCUMGR_RX_BUF_SIZE in application configuration.

  • For other transport methods of newtmgr conn configuration, please refer to official documentation.

View Firmware List

Command format: newtmgr image list -c <connection_profile>

Example:

$ newtmgr image list -c myConn
Images:
image=0 slot=0
   version: 0.0.0
   bootable: true
   flags: active confirmed
   hash: d0d1fe6e6f39a65d2442009e6a620736ea88e437c7bd297abdd9e9eaf1e3e257
image=1 slot=0
   version: 0.0.0
   bootable: true
   flags: active confirmed
   hash: 8ea9150f95da9c3f97134984b731928ae93e14752c54fe53b629d005ab94a948
Parameter Description
image:

Firmware number when there are multiple firmwares.

slot:

Slot (0=primary slot, 1=secondary slot)

version/hash:

Version number and hash

bootable:

Whether the slot contains valid firmware (firmware header is correct and verifiable)

flags:
  • active indicates the currently running firmware slot

  • pending indicates MCUboot will test this firmware slot on next reset

  • confirmed indicates this firmware slot has been confirmed and will not roll back

Note

  • When building new firmware, you can modify the version number CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION for easier version management and querying.

  • Currently NP firmware always uses the same version number as AP firmware.

Upload New Firmware

Use the image upload command to send the newly compiled and signed firmware to the device.

Command format: newtmgr image upload <image-file> -c <conn_profile> [-n <image_int>]

Example:

$ newtmgr -c myConn image upload -n 0 /path/to/your/image_0.bin
77.55 KiB / 77.55 KiB [=======================================================================] 100.00% 4.38 KiB/s 17s
Done

$ newtmgr -c myConn image upload -n 1 /path/to/your/image_1.bin
228.48 KiB / 228.48 KiB [=====================================================================] 100.00% 4.44 KiB/s 51s
Done
Parameter Description
-n:

Specify firmware number, defaults to firmware 0 when omitted.

Mark as Pending Test

After successful transfer, the new firmware will be stored in the secondary slot partition of the corresponding firmware. Use the following command to mark the new firmware as test, and MCUboot will try to run it on the next boot.

Command format: newtmgr -c <conn_profile> image test <hex-image-hash>

Example:

$ newtmgr -c myConn image test 99bb5f0867744a238693f522b31be146a74fca2ad587d878feebef70b7d1e812
Images:
image=0 slot=0
   version: 0.0.0
   bootable: true
   flags: active confirmed
   hash: d0d1fe6e6f39a65d2442009e6a620736ea88e437c7bd297abdd9e9eaf1e3e257
image=0 slot=1
   version: 0.0.1
   bootable: true
   flags: pending
   hash: 99bb5f0867744a238693f522b31be146a74fca2ad587d878feebef70b7d1e812
image=1 slot=0
   version: 0.0.0
   bootable: true
   flags: active confirmed
   hash: 8ea9150f95da9c3f97134984b731928ae93e14752c54fe53b629d005ab94a948
image=1 slot=1
   version: 0.0.1
   bootable: true
   flags:
   hash: 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe
Split status: N/A (0)

$ newtmgr -c myConn image test 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe
Images:
image=0 slot=0
   version: 0.0.0
   bootable: true
   flags: active confirmed
   hash: d0d1fe6e6f39a65d2442009e6a620736ea88e437c7bd297abdd9e9eaf1e3e257
image=0 slot=1
   version: 0.0.1
   bootable: true
   flags: pending
   hash: 99bb5f0867744a238693f522b31be146a74fca2ad587d878feebef70b7d1e812
image=1 slot=0
   version: 0.0.0
   bootable: true
   flags: active confirmed
   hash: 8ea9150f95da9c3f97134984b731928ae93e14752c54fe53b629d005ab94a948
image=1 slot=1
   version: 0.0.1
   bootable: true
   flags: pending
   hash: 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe
Split status: N/A (0)

Note

  • You can see in the information of the firmware slot specified for testing: flags: pending.

Reset Device

You can send a reset command through the MCUmgr tool, or manually reset the device.

After triggering reset, MCUboot detects that the secondary slot is marked as test, and based on the configured Upgrade Mechanism, decides whether to perform primary/secondary slot swap; when using swap_using_xxx strategy, the swap will be completed before the application starts.

Command format: newtmgr reset -c <conn_profile>

Example:

$ newtmgr -c myConn reset
Done

Confirm New Firmware

After the device restarts and successfully runs the new firmware, you must perform a confirmation operation. Zephyr provides two confirmation methods:

  1. Application self-confirmation

    After the device boots and verifies the new firmware successfully, the application actively calls the confirmation interface boot_write_img_confirmed() provided by MCUboot to mark the current firmware as confirmed.

  2. Send confirmation command through MCUmgr tool

    Command format: newtmgr image confirm [hex-image-hash] -c <conn_profile>

    Example:

    $ newtmgr -c myConn image confirm 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe
    Images:
    image=0 slot=0
       version: 0.0.1
       bootable: true
       flags: active confirmed
       hash: 99bb5f0867744a238693f522b31be146a74fca2ad587d878feebef70b7d1e812
    image=1 slot=0
       version: 0.0.1
       bootable: true
       flags: active confirmed
       hash: 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe
    

Note

  • You can see in the confirmed firmware slot information: flags: confirmed.

  • The current smp_svr sample uses the second confirmation method.