升级机制
MCUboot 支持多种固件升级方式,如下表所示。详细说明可参考 MCUboot 官网介绍 。
升级方式 |
特点 |
Upgrade only |
将新固件直接覆盖旧固件,不进行交换,不可回滚 |
Swap using scratch |
通过 scratch 分区做中转,完成新旧固件交换,可回滚 |
Swap using offset |
依靠分区内的偏移布局进行交换,无需 scratch 分区,可回滚 |
Swap using move |
通过移动数据完成交换,无需 scratch 分区,可回滚 |
Direct XIP |
直接原地执行,无需交换 |
RAM load |
将固件完整加载到 RAM 后执行 |
Firmware loader |
通过专用加载器进行升级 |
当前支持以下两种升级方式:
Swap_using_move
Swap_using_move 是一种无需临时存储区(scratch)的固件交换算法,适用于 主 / 次双槽 OTA 升级场景。 其核心是通过将主槽扇区整体后移一位,再交替复制主槽与次槽内容,完成固件交换。
可通过配置宏 CONFIG_BOOT_SWAP_USING_MOVE 启用此升级方式。采用 --sysbuild 时需在应用程序 sysbuild.conf 中配置 SB_CONFIG_MCUBOOT_MODE_SWAP_USING_MOVE=y
工作原理:
从图示可知,每个扇区的交换过程包含 3 个状态:
主槽[N] → 主槽[N+1]已完成
次槽[N] → 主槽[N] 已完成
主槽[N+1] → 次槽[N] 已完成
Swap_using_offset
Swap_using_offset 是一种无需临时存储区(scratch)的固件交换算法,是 Swap_using_move 的增强版。 其核心是利用次槽中的一个扇区作为临时缓冲,交替复制主、次槽内容,完成固件交换。
可通过配置宏 CONFIG_BOOT_SWAP_USING_OFFSET 启用此升级方式;采用 --sysbuild 时需在应用程序 sysbuild.conf 中配置 SB_CONFIG_MCUBOOT_MODE_SWAP_USING_OFFSET=y 。
工作原理:
固件放置:待升级的新固件必须从次槽的第二个扇区开始存放。
交替复制:从第 1 个扇区开始,对每个扇区 N 依次执行:
将主槽第 N 个扇区复制到次槽第 N 个扇区;
将次槽第 N+1 个扇区复制到主槽第 N 个扇区。
逐扇区重复,直至全部交换完成。
从图示可知,每个扇区的交换过程包含 2 个状态:
主槽[N] → 次槽[N] 已完成
次槽[N+1] → 主槽[N] 已完成
交换类型与状态
交换类型
在正常升级流程中(未异常掉电 / 中断),MCUboot 会根据 固件尾部(Trailer) 中的标志位决定执行的交换类型(swap type)。交换类型有以下几种:
BOOT_SWAP_TYPE_NONE:无升级任务,直接启动主槽固件。BOOT_SWAP_TYPE_TEST:测试运行;若未通过设置 image-ok=0x01 确认,下次启动将触发回退。BOOT_SWAP_TYPE_PERM:永久交换,不需要设置 image-ok=0x01 确认。BOOT_SWAP_TYPE_REVERT:上次 TEST 未确认,回退至原有旧固件。BOOT_SWAP_TYPE_FAIL:目标固件无效(如校验失败)。BOOT_SWAP_TYPE_PANIC:交换过程出现不可恢复错误,启动终止。
受 Flash 硬件特性限制,Trailer 的设计并不直观,直接读取原始字节难以解析设备状态。因此,MCUboot 将“各种可能的 Trailer 组合状态”映射为上述交换类型,并按优先级顺序判定:State 编号越小优先级越高,一旦匹配某一状态,立即确定对应的交换类型并停止继续匹配。
State I 场景(仅 Swap_using_offset 模式):在执行 REVERT 前的风险窗口,记录 magic 和 copy-done 到次槽,下次启动会继续执行 REVERT。这是 offset 模式回退过程中的一个中间态,用于保证掉电后仍能续做回退。
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 场景:次槽固件有效但未确认,执行“测试交换”。系统会将次槽固件交换到主槽并启动。 新固件需在应用中设置 image-ok 完成确认;若未确认,下一次启动将自动 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 场景:次槽固件已预先标记 image-ok,执行“永久交换”,升级完成后不会回退。
State III
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Any | Good |
image-ok | Any | 0x01 |
copy-done | Any | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_PERM |
-------------------------------------------------'
State IV 场景:主槽复制已完成(copy-done=Set),但新固件未确认(image-ok=Unset),因此本次启动执行回退,恢复为旧固件。
State IV
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Good | Any |
image-ok | 0xff | Any |
copy-done | 0x01 | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_REVERT |
-------------------------------------------------'
若以上状态 State I~IV 均不匹配,MCUboot 不会执行固件交换,而是进入 State V,选择三种交换类型之一。
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 |
-------------------------------------------------'
交换状态
交换状态(Swap status)用于掉电恢复,记录在主槽的 固件尾部(Trailer) 中。 若固件交换过程中发生异常重启,MCUboot 可根据已记录的交换状态恢复现场,继续完成未结束的交换流程。 该区域由一系列单字节状态记录组成,每条记录独立写入,并按照设备的最小写入粒度进行对齐填充。 典型交换流程如下:
初始化: 同时擦除主、次槽 Trailer;然后在主槽 Trailer 写入 Swap info、Swap size 及 MAGIC 标记。
逐扇区交换:仅对固件实际占用的扇区(包含 TLV 区域)进行交换,并根据所选算法记录每个扇区的状态。
收尾:交换完成后,在主槽 Trailer 中写入 Copy done,标记交换流程结束。
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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
备注
图示假设 min-write-size = 1,每个“扇区索引”对应 3 个状态记录,适用于 Swap_using_move 模式;而在 Swap_using_offset 模式下,每个扇区仅有 2 个状态记录,所需总空间更小。