2. 理解MCUboot启动流程
任务说明
本案例目标是通过阅读MCUboot源码与调试手段,梳理从芯片复位到跳转应用镜像的完整启动流程。你需要明确MCUboot在启动各阶段执行的关键函数、数据结构与判定逻辑,并能在开发板上通过调试输出验证流程顺序。最终交付物为:一张启动流程阶段图、一份关键函数调用链说明,以及一组可执行的调试观测方法。
MCUboot启动流程总览
MCUboot的启动流程可划分为五个阶段:复位入口 → 平台初始化 → 启动前置检查 → 镜像选择与校验 → 跳转应用。整个流程的核心目标是在可信环境中完成对固件镜像的完整性/真实性验证,并决定启动哪个镜像(主镜像或恢复镜像)。
关键源码路径与函数调用链
以下梳理基于MCUboot v1.9+的典型实现(以Zephyr或裸机移植为例),核心代码位于boot/bootutil/src与boot/zephyr(或对应平台目录)。
| 阶段 | 关键函数 | 职责 |
|---|---|---|
| 复位入口 | Reset_Handler → boot_platform_init |
设置栈指针、调用系统初始化(时钟、调试串口) |
| 前置检查 | boot_prepare → boot_read_image_headers |
读取镜像头、检查升级标志(swap_type) |
| 镜像选择 | boot_select_image → boot_validate_image |
根据标志位选择主/恢复镜像,执行签名校验 |
| 跳转 | boot_jump_to_image |
设置VTOR、MSP,将PC指向应用入口 |
最小可复现调试工程
以下代码展示如何在MCUboot的main.c中添加阶段打印,用于观测启动流程。该实现基于Zephyr平台的MCUboot,使用printk输出阶段标记。
// 文件: boot/zephyr/main.c (添加调试打印)
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <bootutil/bootutil.h>
#include <bootutil/image.h>
/* 阶段标记宏 */
#define BOOT_DBG_STAGE(s) printk("[MCUBOOT] STAGE_%s\n", s)
void main(void)
{
/* 阶段1: 平台初始化完成 */
BOOT_DBG_STAGE("PLATFORM_INIT_DONE");
/* 阶段2: 读取镜像头与升级标志 */
struct boot_rsp rsp;
int rc = boot_go(&rsp);
if (rc != 0) {
BOOT_DBG_STAGE("BOOT_FAILED");
while (1) {}
}
/* 阶段3: 镜像校验通过,准备跳转 */
BOOT_DBG_STAGE("IMAGE_VALIDATED");
BOOT_DBG_STAGE("JUMP_TO_APP");
/* 跳转由 boot_go 内部完成,此处不会返回 */
while (1) {}
}
若使用裸机移植(如STM32),可在boot_prepare与boot_jump_to_image之间添加串口输出函数,逻辑相同。
验证方法与判定标准
验证步骤:
- 编译MCUboot并烧录至开发板(如nRF52840 DK或STM32F429)。
- 使用串口工具(115200-8-N-1)连接调试UART。
- 复位开发板,观察串口输出。
- 预期输出顺序(确定性输出,由代码逻辑唯一推出):
[MCUBOOT] STAGE_PLATFORM_INIT_DONE
[MCUBOOT] STAGE_IMAGE_VALIDATED
[MCUBOOT] STAGE_JUMP_TO_APP
判定标准:若串口依次出现上述三条打印且开发板应用镜像正常运行(如LED闪烁或应用串口输出),则证明启动流程正确。若缺少IMAGE_VALIDATED,说明镜像校验失败;若卡在PLATFORM_INIT_DONE后无输出,则问题在镜像选择或校验阶段。
补充观测点(可选)
如需更细粒度追踪,可在boot_validate_image内部添加打印,输出当前校验的镜像槽位(slot 0/1)与签名算法ID。但注意不要修改校验逻辑本身,仅添加观测代码。