引导加载程序
embassy-boot
是一个轻量级的引导加载程序,支持以电源故障安全的方式进行固件应用程序升级,具有试用启动和回滚功能。
引导加载程序既可以作为库使用,也可以在您对默认配置和功能感到满意时直接刷写。
根据设计,引导加载程序不提供任何网络功能。用于获取新固件的网络功能可以由用户应用程序提供,使用引导加载程序作为库来更新固件,或者使用引导加载程序作为库并自行添加此功能。
引导加载程序通过依赖 embedded-storage
特性来支持内部和外部闪存。引导加载程序可选地支持验证已进行数字签名的固件(推荐)。
硬件支持
引导加载程序支持
- nRF52 有和没有软设备
- STM32 L4、WB、WL、L1、L0、F3、F7 和 H7
- 树莓派:RP2040
一般来说,引导加载程序可以在任何为其内部闪存实现 embedded-storage
特性的平台上工作,但可能需要自定义初始化代码才能工作。
STM32L0x1 设备需要启用 flash-erase-zero
功能。
设计
引导加载程序将存储划分为 4 个主要分区,这些分区在创建引导加载程序实例时或通过链接器脚本配置:
- BOOTLOADER - 引导加载程序放置的位置。引导加载程序本身消耗大约 8kB 的闪存,但如果您需要调试它并且有可用空间,则将其增加到 24kB 将允许您使用 probe-rs 运行引导加载程序。
- ACTIVE - 主要应用程序放置的位置。引导加载程序将尝试在此分区的开头加载应用程序。此分区仅由引导加载程序写入。此分区所需的大小取决于您的应用程序的大小。
- DFU - 要交换的应用程序放置的位置。此分区由应用程序写入。此分区必须比 ACTIVE 分区至少大 1 页,因为交换算法使用额外的空间来确保数据电源安全复制:
Partition Sizedfu= Partition Sizeactive+ Page Sizeactive
+
所有值均以字节为单位指定。
- BOOTLOADER STATE - 引导加载程序存储当前状态的位置,描述是否需要交换活动分区和 dfu 分区。当新固件已写入 DFU 分区时,会写入一个魔术字段,指示引导加载程序应交换分区。此分区必须能够存储魔术字段以及分区交换进度。分区大小由下式给出:
Partition Sizestate = Write Sizestate + (2 × Partition Sizeactive / Page Sizeactive)
+
所有值均以字节为单位指定。
ACTIVE (+BOOTLOADER)、DFU 和 BOOTLOADER_STATE 的分区可以放置在单独的闪存中。引导加载程序使用的页面大小由 ACTIVE 和 DFU 页面大小的最小公倍数确定。 BOOTLOADER_STATE 分区必须足够大,以存储 ACTIVE 和 DFU 分区中每页一个字。
引导加载程序有一个平台无关的部分,它实现了电源故障安全交换算法,给定分区设置的边界。平台特定的部分是一个最小的垫片,提供额外的功能,例如看门狗或支持 nRF52 软设备。
注意:应用程序和引导加载程序的链接器脚本看起来相似,但引导加载程序的 FLASH 区域必须指向 BOOTLOADER 分区,应用程序的 FLASH 区域必须指向 ACTIVE 分区。
FirmwareUpdater
FirmwareUpdater
是一个对象,用于方便地将固件刷写到 DFU 分区,然后在下次重置时将其标记为准备好与活动分区交换。它的主要方法是 write_firmware
,它在闪存"写入块"大小(通常为 4KiB)时调用一次,以及 mark_updated
,这是最终调用。
验证
引导加载程序支持验证已刷写到 DFU 分区的固件。验证要求固件已使用 link:https://ed25519.cr.yp.to/[`ed25519`] 签名进行数字签名。启用验证后,将调用 FirmwareUpdater::verify_and_mark_updated
方法来代替 mark_updated
。需要公钥和签名,以及已刷写固件的实际长度。如果验证失败,则固件将不会被标记为已更新,因此将被拒绝。
签名通常与要更新的固件一起传递,而不是写入闪存。如何提供签名是固件的责任。
要启用验证,请在依赖 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 密钥可以通过多种工具生成。我们推荐 link:https://man.openbsd.org/signify[`signify`],因为它被广泛用于签名和验证 OpenBSD 发行版,并且使用起来很简单。
以下 Bash 命令集可用于在 Unix 平台上生成公钥和私钥,并生成本地 key.pub
文件,其中删除了 signify
文件头。在安全位置声明 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 | head -c128 | 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
密钥,因为泄露它意味着另一方可以签署您的固件。