MCUBoot 简介
-
MCUBoot 是可配置的安全引导加载程序,由多个行业领导者维护。它可以作为 第一或第二阶段的引导加载程序运行,支持软件映像的加密验证, 支持的加密方式如下:
- ECDSA-P256
- RSA-2048
- RSA-3072
-
默认情况下,它支持映像回滚,下载的固件会被试验性地启动一次。 初次升级引导时,如果升级映像将自身标记为已确认,则其将被保留为主映像文件。如果升级映像未被确认,则后续引导将回退至 上一个被确认的映像。如果没有可用的有效映像,作为一种安全 预防措施,设备会将自己变砖。
组成
- MCUBoot 由两部分组成
- bootutil (boot/bootutil)
- boot application (每个平台在 boot目录下都有其对应的boot程序)
- bootutil 实现了bootloader的大部分功能,但是跳转到主映像程序,这一部分是缺失的,这部分由boot application 替代实现,bootloader的功能是分离的,这样做的目的是为了方便对bootloader进行单元测试。
前提条件
-
Zephyr所需的第一步是确保您的板子在其设备树中定义了闪存分区,这些分区包括:
- boot_partition: for MCUboot itself
- slot0_partition: the primary slot of Image 0
- slot1_partition: the secondary slot of Image 0
-
就目前而言,两个镜像分区必须是连续的。如果MCUboot作为您的第一阶段引导加载程序,则必须配置boot_partition,以便您的SoC从复位时运行它。如果有多个可更新的映像,则其余映像的相应主分区和备用分区也必须定义(例如,Image 1的slot2_partition和slot3_partition)。
-
闪存分区通常在zephyr/boards文件夹中名为boards/<arch>/<board>/<board>.dts的文件中定义。
构建Bootloader
- 从Zephyr的角度来看,botloader也是一个普通的Zephyr应用程序,虽然CMakeLists.txt中已经完成了绝大部分操作,但是在编译之前仍然需要对一些配置进行修改,例如签名算法和主映像文件是否在每次启动之后都要对其进行验证,设置完成之后跳转到bootloader/mcuboot/boot/zephyr 目录下,然后进行编译:
cd bootloader/mcuboot/boot/zephyr
west build -b <board>
- 除了定义在设备树中的分区表,构建bootloader还需要一些和flash 布局有关的信息,这些信息是通过 boot/zephyr/include/target.h 进行收集的,信息源自和board相关的头文件,设备树,以及MCUBoot对不同SOC的配置。
为Bootloader构建APP
- 配置文件中启用 MCUboot 组件:打开 Zephyr 的配置文件 prj.conf 或 prj.{board}.conf(其中 {board} 是您所使用的开发板名称),并添加以下选项(选项开启后会自动从设备树中读取主映像分区信息,详细描述请查看partitions章节):
CONFIG_BOOTLOADER_MCUBOOT=y
- 如果需要使用 MCUboot 的其他特性,如加密或签名,还需要配置相应的选项,MCUBOOT_GENERATE_UNSIGNED_IMAGE 选项未使能时,会开启镜像签名机制,MCUBOOT_SIGNATURE_KEY_FILE 用于设置签名文件的路径,MCUBOOT_ENCRYPTION_KEY_FILE 用于设置加密文件路径,如果没有设置签名和加密文件的路径,最后必须要手动为镜像签名。
- 为了让app能够正确的运行,chosen节点下的zephyr,code-partition属性必须设置,否则最终生成的程序会链接出错无法运行:
/{
chosen{
zephyr,code-partition = &slot0_partition;
};
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* 64KB for bootloader */
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 DT_SIZE_K(64)>;
read-only;
};
/* storage: 64KB for settings */
storage_partition: partition@10000 {
label = "storage";
reg = <0x00010000 DT_SIZE_K(64)>;
};
/* application image slot: 256KB */
slot0_partition: partition@20000 {
label = "image-0";
reg = <0x00020000 DT_SIZE_K(256)>;
};
/* backup slot: 256KB */
slot1_partition: partition@60000 {
label = "image-1";
reg = <0x00060000 DT_SIZE_K(256)>;
};
/* swap slot: 128KB */
scratch_partition: partition@a0000 {
label = "image-scratch";
reg = <0x000a0000 DT_SIZE_K(128)>;
};
};
};
};
- 构建 Zephyr 应用程序:使用 west build 命令构建 Zephyr 应用程序,其中<board> 是您所使用的开发板名称,<app> 是您的应用程序名称:
west build -b <board> <app>
签名
- 为了对程序进行升级,或者是引导应用程序(当 MCUBOOT_VALIDATE_PRIMARY_SLOT 使能后),必须要对程序进行签名,在MCUBoot中提供了一些用于功能测试的例程,但一定不要将其运用到产品中,因为这些密钥已经被公开到网络上。
密钥创建
./scripts/imgtool.py keygen -k mykey.pem -t rsa-2048
- 其中-t选项用于指定加密算法
公钥提取
- 生成的密钥对中包含了公钥和私钥,需要从密钥对中提取公钥并插入bootloader和APP中,CONFIG_BOOT_SIGNATURE_KEY_FILE 选项用于设置生成的密钥对的路径,编译系统会从中提取出C编译器可以使用的公钥,提取出的公钥会被放到 build/zephyr/autogen-pubkey.h 中,然后被编译到程序中。
程序烧录
- 先将bootloader烧录到芯片中,然后将主程序烧录到 flash 对应分区中,
- 主镜像程序烧录时不可以进行全片擦除,这样会导致bootloader被擦掉。
APP中使用MCUBoot
- 在应用程序中使用 MCUboot 库:在应用程序代码中包含 mcuboot.h 头文件,例如:
#include <mcuboot.h>
- 然后使用其中的 API 来进行固件升级和回滚。例如,以下代码将触发一个固件升级:
int rc = boot_request_upgrade(0);
if (rc) {
printk("Failed to request upgrade (%d)\n", rc);
}