启动加载器(Bootloader)
embassy-boot 是一个轻量级启动加载器,支持断电安全的固件应用升级,具备试运行和回滚功能。
该启动加载器既可以作为库使用,也可以直接烧录(如果你对默认配置和功能满意)。
设计上,启动加载器不提供任何网络功能。获取新固件的网络能力应由用户应用实现,可以将启动加载器作为库来更新固件,或在库的基础上自行添加网络功能。
该启动加载器通过依赖 embedded-storage traits 支持内部和外部 flash。还可选支持对已数字签名固件的验证(推荐开启)。
硬件支持
启动加载器支持:
-
nRF52(支持/不支持 softdevice)
-
STM32 L4、WB、WL、L1、L0、F3、F7 和 H7
-
树莓派 RP2040
一般来说,只要平台为其内部 flash 实现了 embedded-storage traits,启动加载器都能工作,但可能需要自定义初始化代码。
设计
启动加载器将存储空间分为 4 个主要分区,可在创建实例或通过链接脚本配置:
-
BOOTLOADER —— 启动加载器本身存放位置。大约占用 8kB flash,如果需要调试且空间充足,建议增大到 24kB 以便用 probe-rs 调试。
-
ACTIVE —— 主应用存放位置。启动加载器会尝试从该分区起始处加载应用。该分区仅由启动加载器写入,所需大小取决于应用体积。
-
DFU —— 待切换的新应用存放位置。该分区由应用写入,必须比 ACTIVE 分区至少大 1 页,因为交换算法需要额外空间保证断电安全:
分区大小dfu = 分区大小active + 页大小active
所有值均为字节。
-
BOOTLOADER STATE —— 启动加载器存储当前状态(如是否需要交换 active 和 dfu 分区)。当新固件写入 DFU 分区后,会写入一个 magic 字段,指示启动加载器进行分区交换。该分区需能存储 magic 字段和交换进度,大小为:
分区大小state = 写入大小state + (2 × 分区大小active / 页大小active)
所有值均为字节。
ACTIVE(+BOOTLOADER)、DFU 和 BOOTLOADER_STATE 分区可放在不同 flash。启动加载器使用的页大小为 ACTIVE 和 DFU 页大小的最小公倍数。 BOOTLOADER_STATE 分区需足够大,能为 ACTIVE 和 DFU 分区的每一页存储一个字。
启动加载器有平台无关部分,实现断电安全交换算法;平台相关部分为最小 shim,提供看门狗等功能或支持 nRF52 softdevice。
注意:应用和启动加载器的链接脚本类似,但 FLASH 区域需分别指向 BOOTLOADER(启动加载器)和 ACTIVE(应用)分区。
FirmwareUpdater
FirmwareUpdater 对象用于方便地将固件写入 DFU 分区,并在下次重启时标记为可与 active 分区交换。主要方法有 write_firmware(每个 flash 写块调用一次,通常为 4KiB)和 mark_updated(最后调用)。
验证
启动加载器支持对 DFU 分区已写入固件的验证。验证要求固件使用 ed25519 数字签名。启用验证后,需用 FirmwareUpdater::verify_and_mark_updated 替代 mark_updated,并提供公钥、签名和固件实际长度。验证失败则不会标记为已更新,固件会被拒绝。
签名通常随固件一起传递,不写入 flash。如何传递签名由固件自行决定。
如需启用验证,依赖 embassy-boot crate 时启用 ed25519-dalek 或 ed25519-salty 特性。推荐使用 ed25519-salty,因其体积更小。
关于 Ed25519 密钥与签名的提示
Ed25519 是一种公钥签名系统,需妥善保管私钥。建议将*公钥*嵌入程序,便于传递给 verify_and_mark_updated。例如:
static PUBLIC_SIGNING_KEY: &[u8] = include_bytes!("key.pub");
签名通常与固件一起传递,可直接追加。
Ed25519 密钥可用多种工具生成。推荐 signify,广泛用于 OpenBSD 发行版签名与验证,且易用。
以下 Bash 命令可在 Unix 平台生成公私钥,并生成无头部的 key.pub 文件。请将 SECRETS_DIR 环境变量设为安全目录。
signify -G -n -p $SECRETS_DIR/key.pub -s $SECRETS_DIR/key.sec
tail -n1 $SECRETS_DIR/key.pub | base64 -d -i - | dd ibs=10 skip=1 > key.pub
chmod 700 $SECRETS_DIR/key.sec
export SECRET_SIGNING_KEY=$(tail -n1 $SECRETS_DIR/key.sec)
签名固件时,假设已声明 FIRMWARE_DIR,固件文件名为 myfirmware:
shasum -a 512 -b $FIRMWARE_DIR/myfirmware > $SECRETS_DIR/message.txt
cat $SECRETS_DIR/message.txt | dd ibs=128 count=1 | xxd -p -r > $SECRETS_DIR/message.txt
signify -S -s $SECRETS_DIR/key.sec -m $SECRETS_DIR/message.txt -x $SECRETS_DIR/message.txt.sig
cp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed
tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed
请务必妥善保管 $SECRETS_DIR/key.sec,一旦泄露,其他人即可签名你的固件。