
硬件安全这件事做嵌入式的朋友应该都清楚只要产品里出现过一次固件被逆向、被篡改、OTA升级被劫持的事故整个项目的信任度就崩了。X-CUBE-SBSFU正是ST官方为解决这个痛点推出的安全启动与安全固件更新扩展包配合应用笔记AN5056给出的集成路径能把安全启动链、签名校验、防回滚这些底层的信任根机制相对平滑地接入到STM32平台上。这篇笔记我就围绕AN5056的集成流程从上手思路、工程配置、烧录验证到踩坑排查完整梳理一遍实操过程希望对正在规划固件安全方案的朋友有实际帮助。1. 安全启动项目整体设计与思路拆解1.1 SBSFU解决的核心问题是什么在聊ST这套安全方案之前先看一个最常见的场景你的设备跑着STM32主控里是编译好的App固件。产品发出去之后别人用JTAG/SWD调试器直接连上芯片用读保护绕过工具或者干脆花几十块钱买个离线烧录器把Flash里的固件bin完整读出来反汇编之后要么抄走你的业务逻辑要么改掉某个启动条件再刷回去。更狠的是如果设备支持OTA升级攻击者可以自己伪造一个升级包推给设备让设备跑上恶意固件整个设备就沦陷了。SBSFUSecure Boot and Secure Firmware Update就是针对这类威胁设计的一套软件方案。它由ST官方提供通过X-CUBE-SBSFU扩展包集成到STM32CubeMX或STM32CubeIDE里核心目标有三块安全启动Secure Boot芯片上电后先运行一段被设为信任根的Bootloader这段代码对App区域做完整性校验和签名验证验证通过才把控制权交给App。验证失败就停在安全引导状态等待恢复固件。安全固件更新Secure Firmware Update固件升级包在编译阶段就按签名加密的方式打包设备在接收升级包后先解密、再验签确认来源可信才写入App区写入过程还带断电保护和回滚控制。密钥与存储保护整套机制的信任根建立在芯片内部的OTP一次性可编程区域和隐藏Flash区域关键密钥不会以明文散布在普通Flash里。这套思路的底层逻辑类比一下就是小区门禁。传统的单层门禁是门锁本身可靠而SBSFU是入口处有一个保安先核对你的工牌签名验证再决定放不放你进电梯启动App。而且工牌的有效期、权限级别也是动态校验的不是看一眼就完事。1.2 SBSFU在AN5056方案中的定位AN5056是ST发布的应用笔记全称是Getting started with X-CUBE-SBSFU官方代号AN5056。它并不只是一篇简单的How to run the demo更像是一份集成路线图描述了SBSFU代码包的整体结构、内存布局规划、以及从下载扩展包到最终产品烧录的完整链路。我在项目里用到的版本是X-CUBE-SBSFU V3.x其中支持了ST最新推出的Secure ManagerSM框架以及传统的SBSFU两种模式。模式选择是整个集成工作里的第一个分叉路口。简单说SBSFU传统模式安全引导栈和App区域按照MFWMonitored Firmware SFUSecure Firmware Update的分区来部署Bootloader负责验签启动SFU模块负责接收、解密、校验并写入新固件。适合需要完全自主掌控安全流程的团队。Secure Manager模式ST提供一套预封包的安全组件应用层通过统一PSA API调用安全服务Boot层和隔离机制由ST维护集成成本更低但灵活度也相对小一些。在AN5056里ST对这两种模式的适配条件和内存占用量都给了明确说明。我的建议是如果你的芯片资源够用、团队对安全机制有比较深的理解优先选传统SBSFU模式因为你能看清楚每一个校验步骤在哪里发生排查问题时也更有掌控感。如果项目交付周期紧安全需求相对标准用Secure Manager模式能省不少工作量。做技术选型不能盲目追新适合团队排期和技术储备的才是好方案。1.3 信任链模型与启动流程剖析先看一下SBSFU系统整体的信任链自上而下有几级芯片出厂时通过烧录工具写入SBSFU Boot和SFU固件同时在OTP区域烧入公钥哈希用于校验固件签名公钥本身或者直接在安全Flash里烧入开发者的公钥。上电执行芯片复位后从Flash起始地址开始执行SBSFU Boot代码。Boot读取自身区域完整性信息如果被篡改或异常会进入安全失败状态。Boot验证应用固件它从FAFirmware Area读取App镜像头镜像头中包含版本号、固件大小、签名算法标识、签名值等关键信息。Boot先计算App镜像的摘要再用内置公钥验证签名。验签通过清理栈和中断向量表跳转到App验签失败则进入固件接收状态。固件更新阶段运行中的App如果通过网络或外部接口收到新固件包将其暂存在交换区Scratch Area。在固件升级时触发系统复位Boot接管后读取暂存区的升级包执行解密和验签写入App区更新版本号再次复位后才跳转到新App。防回滚机制OTP区域记录了一个版本计数只要新固件版本不高于当前版本Boot/Update模块会拒绝写入。这样攻击者就没办法用旧版本固件的漏洞做降级攻击。这个启动链路的每一级都只信任上一级签发给自己的合法内容环环相扣。哪怕App区域被完全改写Boot层也不会把控制权交给非法代码。这也是安全启动最核心的价值不保证App不会被攻击者破解而是保证攻击者做不到无声无息换掉固件。2. 环境准备与X-CUBE-SBSFU扩展包集成2.1 软硬件环境说明我这边实际搭建的参考环境如下供大家对照环境项建议配置集成开发环境STM32CubeIDE 1.13或STM32CubeMX 6.8扩展包X-CUBE-SBSFU V3.0.0及以上目标芯片STM32L476RG、STM32L552ZE、STM32H743ZI等以AN5056支持的系列为准调试/烧录器ST-LINK/V2或V3串口终端任意支持波特率115200的工具如PuTTY、MobaXterm原厂评估板NUCLEO-L476RG / NUCLEO-L552ZE / STM32H743I-EVAL关于AN5056的适用范围涉及同一个系列下不同Flash容量时ST在应用笔记里明确说明内存布局可能会不同。所以做项目之前一定要先确定你用的是哪个具体芯片型号不要拿同系列的兄弟型号盲目套用。比如L476RG是1MB FlashL476RE是512KB Flash两者在SBSFU内存布局上的可用区域就不一样镜像签名和地址偏移差异会直接导致启动失败。2.2 通过STM32CubeMX集成扩展包的步骤第一步打开STM32CubeMX在Help - Manage embedded software packages里检查X-CUBE-SBSFU是否已安装。如果没装切换到From Internet找到STMicroelectronics分类勾选X-CUBE-SBSFU并安装。安装过程比较久因为涉及到多个系列的固件包依赖。第二步新建项目选择芯片型号。在Pinout Configuration界面左侧菜单拉到Middleware and Software Packs可以看到X-CUBE-SBSFU的配置入口。点击进入后需要先选择激活的软件包再按你需要配置的模式SBSFU或Secure Manager。第三步按应用笔记AN5056的示例配置把下面几个关键项设置好模式选择选SBSFU签名算法RSA-2048或ECDSA P-256二选一散列算法SHA-256固件版本号初始建议用0.0.1调试接口根据量产需求配置如果计划开RDP Level 2则调试口会被禁用需要提前规划烧录策略这里有一个细节容易忽略在AN5056示例中默认配置的UART日志输出是放在某个特定引脚的。开发调试期大家习惯用ST-LINK虚拟串口但如果你接的是自己板子上的UART需要提前确认串口引脚和中断优先级配置不然你会看到SBSFU日志一片空白还以为是固件没烧进去。2.3 踩过的坑扩展包与固件包依赖的关系做集成时最容易遇到的卡点其实是版本依赖问题。热搜词里还出现了一条the firmware package (stm32cube fw_f1 v1.8.7) or one of its dependencies requires...的错误提示这个我在实战里也踩过所以单独拿出来说。当你在CubeMX里添加X-CUBE-SBSFU扩展包时它通常会自动拉取对应芯片系列的固件包比如STM32Cube FW_F1、FW_H7、FW_L4等。如果使用CubeMX的自动安装一般能处理好依赖。但如果你的网络环境不好或者公司内网离线开发扩展包安装不完整编译时就会出现缺少头文件、缺失函数的报错错误信息里恰恰提示你需要安装某个特定版本的固件包。我的排查建议到STM32CubeMX/Repository目录下检查固件包是否存在目录层级是STM32Cube/Repository/STM32Cube_FW_L4_V1.x.x如果某个版本缺失手动去ST官网下载对应固件包并解压到Repository目录然后在CubeMX里重新刷新SBSFU本身对固件包版本有最低要求比如AN5056里会注明requires STM32Cube FW_L4 V1.17.0 or higher版本不合编译阶段会出现很奇怪的宏定义冲突这个依赖问题看似简单其实很考验耐心。我在接手一个半中途项目时对方报错说编译器找不到stsafe_lib.h一开始以为他们没拷贝库文件后来发现是固件包版本太低连SBSFU的库文件接口都不兼容。所以升级扩展包之前建议先看release note不要一路latest。3. 核心机制与实操配置梳理3.1 密钥管理整个安全链路的根基顺着AN5056的内容往下走密钥管理是绕不开的核心板块这里拿传统SBSFU模式展开讲。X-CUBE-SBSFU在生成工程时会默认分配一套密钥存放在工程目录的2_Images_SECoreBin\Binary\keys或者类似路径下。其中包括密钥文件用途存放位置固件签名私钥开发侧对固件镜像签名仅存于开发环境严禁泄漏固件签名公钥烧录进芯片Boot运行时验证签名烧入安全Flash区域加密密钥固件镜像传输阶段做AES-GCM解密密钥加密后存放在安全区在实际项目中默认密钥绝不能用于量产。AN5056明确要求量产流程里必须把开发密钥替换掉并保证替换后的私钥只掌握在签名服务器上。一个项目里我见过最危险的操作是——把ST示例工程里的默认私钥一路用到了产品发布。这意味着任何拿到过这个公开示例包的人都可以签一个你的芯片认账的固件。这是安全启动里最隐蔽也最致命的漏洞。密钥替换的方法可以用ST提供的工具KeysGen.py在本地重新生成密钥对然后把生成的公钥写入工程的Boot配置中私钥保存在离线环境里用于CI/CD签名。需要注意生成密钥时位数至少用2048我个人更推荐3072位RSA或ECDSA P-256前者性能开销大但生态兼容好后者在资源受限设备上更占优。3.2 内存分区与区域布局规划继续看AN5056里很有参考价值的部分SBSFU对Flash内存的规划。以STM32L4系列为例安全启动方案会把Flash大致划分为以下几个区域区域名功能大小参考Boot区SecureBoot存放安全启动代码上电后最先执行32KB-48KBSECoreBin区存放安全核心服务、密钥存储服务16KB-32KBSFU区安全固件更新服务32KB左右主App区用户业务固件取决于Flash总容量交换/暂存区接收新固件包后临时存放与App区大小相当需要注意的是不同芯片系列的区域大小差异很大AN5056给出了每个系列的Flash布局参考表但实际还要根据你的App大小做调整。例如STM32F1系列的Flash只有128KB或256KB划给SBSFU的功能分区之后留给用户App的空间可能不到100KB这会让很多大软件栈比如带TLS的协议栈放不下。所以在选型阶段一定要先做Flash容量预算再用SBSFU的分区工具调整。不要等代码写完了再考虑安全启动分区不够时会非常被动。我习惯在项目需求阶段就画一个Flash分布表。比如计划用L476RG的1MB FlashApp业务代码预期300KB那我就会把App区预留在512KB左右Boot加SECoreBin加SFU合计约200KBSwap区留200KB。这样余量充足后续迭代不会动不动碰到边界。3.3 编译配置与生成代码在使用STM32CubeMX生成工程后需要仔细检查生成的代码文件结构重点看以下两类文件boot_开头的目录安全启动代码、加密算法库、密钥存储驱动app_secure目录用户App示例以及SBSFU的接口调用示例sfu_及se_相关目录安全固件更新核心逻辑和平台接口AN5056里的标准流程编译时需要按顺序做两步编译安全Boot工程1_Images_SECoreBin将生成的SECoreBin镜像与Boot代码合并烧录到芯片起始地址。编译用户App工程2_Images_SBSFU生成经过签名加密的App镜像。有些集成场景还会有一个3_Images_App目录对应的是只包含用户App的工程你需要把App的链接脚本里的Flash起始地址和大小按照SBSFU的布局来修改。这一步很容易出错因为链接脚本里写的地址要和Boot里配置的App地址严格一致错一个字节都会导致跳转失败。我建议编译时先在调试配置里打开-Wall -Werror如果有一堆未定义变量或者隐式声明先解决掉再说。安全代码对编译干净度要求很高因为启动阶段一旦发生未定义行为不像应用层崩溃那样能看到日志整个系统会直接表现为上电后没有任何反应。4. 完整实操从烧录到验证安全更新4.1 安全固件的烧录流程这一节给出一个可复现的烧录步骤基于STM32CubeProgrammer命令行工具。假设你已经用CubeMX生成了工程并且在Binary目录下生成了三个文件SECoreBin_xxxx.bin安全核心二进制SBSFU_xxxx.bin安全启动Boot二进制App_xxxx.bin用户应用签名加密镜像首先擦除整个芯片并设置选项字节。这一步需要谨慎执行因为部分选项字节如RDP级别一旦更改后续就无法用调试器直接读取Flash内容。STM32_Programmer_CLI.exe -c portSWD modeHOTPLUG -e all -ob RDP0然后烧录SECoreBin和Boot。根据AN5056推荐可以先烧录Boot区域再烧录SECoreBin接着按地址烧录App。这里我用的是STM32H7系列举例地址要按你自己工程的FlashLayout文件为准。STM32_Programmer_CLI.exe -c portSWD modeHOTPLUG -w ./Binary/SECoreBin_xxxx.bin 0x08000000 STM32_Programmer_CLI.exe -c portSWD modeHOTPLUG -w ./Binary/SBSFU_xxxx.bin 0x0800C000 STM32_Programmer_CLI.exe -c portSWD modeHOTPLUG -w ./Binary/App_xxxx.bin 0x08020000烧录完成后打开串口终端波特率115200复位板子正常日志里会看到类似这样的输出[SUCCESS] SBSFU image verification OK [INFO] Valid application found [INFO] Jump to application...这个时候说明Boot已经成功验证了App签名并跳转执行。这里要特别提醒第一次烧录时不要着急把RDP级别调到Level 2。量产前做完整功能验证、确保密钥正确之后再调。一旦调成Level 2调试口读写关闭再想用ST-LINK去读Flash需要先做芯片全擦除mass erase代价是里面的固件全部清空。我在早期做实验时手一抖把RDP调成了Level 2结果整晚都在跟烧录器较劲这个坑希望你们绕开。4.2 模拟一次完整的固件安全更新固件更新的验证是整个安全机制真正发挥作用的地方。SBSFU支持多种升级通道UART、USB、BLE/OTA等。AN5056的示例工程里一般是基于UART或USB的本地升级用配套的SBSFU_Updater工具来推送升级包。我们先模拟一次本地升级修改App代码版本号比如从0.0.1改为0.0.2。编译App工程生成新的签名加密镜像。打开SBSFU_Updater工具选择串口、固件包路径点击Download。设备收到升级包后App会调用SBSFU接口把固件暂存到Swap区。触发复位后Boot接管校验新固件版本号大于当前版本才允许升级。升级完成日志会出现[SUCCESS] Firmware upgrade successful [INFO] New version detected: 0.0.2 [INFO] Jump to application...如果你故意用旧版本号比如又去刷0.0.1日志会明确告诉你[ERROR] Firmware version check failed, image rejected这正是防回滚机制生效的表现。注意防回滚的实现依赖OTP中记录的版本计数所以调试时乱刷低版本固件很容易把OTP区域的计数写死导致后续所有低版本固件都升级不了。此时唯一的恢复手段是重新烧录SECoreBin并重置OTP计数但OTP是一次性的某些区域烧了就回不来。所以调试期间建议在代码里禁用或者放宽版本检查到量产前再开启。4.3 安全日志与状态检查AN5056里把SBSFU的日志输出接口做成了系统级服务在调试时你可以通过日志判断当前系统处于哪个状态。常见状态包括STATE_IDLE正常运行等待指令STATE_IMAGE_VALIDATION正在校验固件STATE_IMAGE_APPLICATION正在写入固件STATE_UNDEFINED异常状态这些日志是通过SBSFU内部的SBSFU_Log模块发出来的如果日志没有输出优先检查UART引脚与波特率配置其次是目标芯片的时钟配置是否异常。很多人在调试SBSFU时直接改了时钟源结果系统时钟不对Boot里依赖的延时、串口波特率全部错乱表现出来就是毫无反应。5. 常见问题与排查技巧实录5.1 启动校验失败的典型场景下面这些是我在集成SBSFU过程中真实遇到过、以及社群朋友反馈过的高频问题整理成表方便快速对照现象可能原因排查方法上电后无日志串口调试引脚配置错误、时钟未起振用示波器或逻辑分析仪看UART TX引脚是否有波形日志输出Authentication failed公钥与签名私钥不匹配或App镜像的签名算法与Boot配置不一致重新生成密钥对确认Boot和App编译时传入的密钥ID一致升级包下载完成后复位固件没更新Swap区大小不足或App区地址覆盖了Swap区检查链接脚本和FlashLayout确认Swap区域大小大于升级包大小编译报错缺少stsafe_a200.h固件包依赖缺失或版本过低按前文所述检查CubeMX Repository里固件包版本并安装匹配版本RDP Level 2后ST-LINK无法连接这是正常的安全限制想恢复调试只能全片擦除建议在调试阶段保留RDP Level 0/1使用芯片内部HSI时钟Boot正常但是App启动后卡死App工程中未正确配置Flash延迟、等待状态数检查App工程的FLASH-ACR配置是否与主频匹配参考芯片参考手册排查的思路多说一句SBSFU的启动过程比较靠前一旦出问题你连个操作系统日志都看不到。所以推荐大家在Boot里加一个GPIO指示状态——上电时点亮LED验签通过后翻转一次。这种最土的办法在定位问题的时候比任何高级调试器都直观。5.2 编译警告与链接脚本调整链接脚本是另一个常见翻车点。SBSFU的Boot、SECoreBin、App三个工程的链接脚本是不同的每个工程定义的Flash起始地址和RAM布局也不一样。在AN5056的框架里用户App的链接脚本应定义Flash起始地址对应Boot配置中App区域起始地址Flash长度App区大小RAM起始地址从固定偏移开始避免与安全服务使用的RAM重叠如果App工程没有按这个布局编写链接脚本即使烧录成功跳转App后也会跑飞。判断标准就是日志显示Jump to application...之后就没了下文。用调试器看PC指针会发现PC跳到了非法地址或者0xFFFFFFFF。我的建议是先用ST官方示例工程里对应芯片的链接脚本做模板只改业务代码不要自己从头排版链接脚本。没有把握的情况下不要动RAM起始地址的偏移值因为SBSFU的核心模块如AES-GCM解密在更新过程中需要在RAM里运行它占用的是固定地址区域。RAM重叠的后果非常隐蔽可能正常跑一次升级没问题但第二次升级就随机死机。5.3 性能与功耗预算评估SBSFU不是免费的它占用Flash空间、运行时占用一定的CPU时间还可能影响低功耗唤醒流程。AN5056中给出了一个大致的性能参考其中RSA-2048的验签过程在Cortex-M4上大约需要几百毫秒ECDSA P-256会快一些但也要注意不同芯片是否有硬件加速器如STM32L5、H7系列支持硬件加密加速。在项目规格里如果要求冷启动后100ms内进入App那基于软件RSA验签的SBSFU可能会吃紧。这时有两个优化思路换用支持硬件加密加速的芯片型号让验签运算在加密外设中完成CPU只做数据搬运和流程控制。对Boot做分级加载先验签一个最小启动镜像快速拉起业务框架再异步加载完整应用。但SBSFU标准方案的启动链路不推荐这种方式因为会引入新的攻击面。功耗方面SBSFU在深度睡眠模式下的待机电流几乎可以忽略因为安全核心长时间处于时钟关闭状态。但需要注意在低功耗唤醒后Boot若重新执行完整验签会造成短暂的电流尖峰。如果产品对功耗敏感建议将SBSFU配置为从睡眠唤醒时跳过验签模式。但前提是睡眠唤醒时没有加载新固件不存在被替换的风险。这一步要结合产品实际威胁模型来取舍不能为了省电把安全底线去掉。6. 扩展应用与实际工程落地思考AN5056的官方示例定位是演示级真要把它落到量产项目里建议在几个方向上做工程化改造。第一是密钥的生命周期管理。开发、测试、试产、量产用的密钥应该分成多套每一套密钥都要有专人保管签名服务器不能放在公网。合理的架构是CI服务器只负责生成待签名的镜像并提交给签名机签名机通过内部接口返回签名结果私钥永远不离开签名机的加密存储。我们用了一套简单的方案签名机是一个不联网的Ubuntu小主机存放私钥的U盘只有在签名时才插入签名结果通过局域网传到CI服务器。整体成本不高但安全性提升了一个档次。第二是升级通道的健壮性设计。SBSFU处理的是安全启动和更新这一层但升级包从哪里来、怎么传输属于应用层的职责。推荐的做法是在应用层设计一套升级包描述块放在安全升级包的前部内容包含固件版本号、目标设备型号、最小电池电量要求等应用层收到后先做这些预检查再触发SBSFU的升级流程。这样可以避免在电池电量过低时执行升级否则升级一半断电虽然SBSFU有恢复机制但每次恢复都会让用户多等一段时间。第三是多镜像支持。某些产品可能有多个可执行镜像比如一个通信协议栈镜像和一个用户业务镜像需要独立更新。X-CUBE-SBSFU较新的版本支持多镜像安全更新配置路径是在CubeMX的SBSFU配置页里打开Multi-image support。多镜像带来的复杂度在于内存布局和复用关系两个镜像都要有独立的签名、独立的回滚计数升级时还要保证镜像之间的版本兼容性。这块文档较少我建议对照官方示例一步步来不要一上来就改分区表。第四是产线烧录流程。SBSFU方案的量产烧录不会像普通固件那样直接灌bin文件因为每台设备的密钥存储区域可能不同有的是使用每台芯片独有的唯一ID派生密钥。常见的产线方案是先用离线烧录器烧录SBSFU Boot SECoreBin 初始App。开机后产测程序通过串口/工厂测试接口触发设备唯一密钥的注入和安全状态切换。完成安全状态设置后产测程序校验系统状态再把RDP级别升到Level 2。这套流程里每一步都要有明确的成功/失败判定不能烧进去就算完。产线工人不懂安全所以上位机软件要把错误信息写得足够明确比如option byte write failed或者signature verification failed, please re-flash。7. 工具链与调试环境补充说明调试安全启动系统与普通应用调试有很大区别因为启动阶段早于任何调试器初始化常规的breakpoint at main方法基本失效。我在项目里摸索了一套比较顺手的方式用SEGGER Ozone配合J-Link在Boot的复位向量处设置断点逐步观察PC指针的运行轨迹。有条件的话给Boot和App分别创建两个调试配置按需连接不要在一个会话里来回切换。在Boot代码的关键节点添加LED翻转逻辑输出正在验签验签结束跳转App三个状态。这在现场联调时比任何调试器都可靠因为不需要外接电脑工人的手机拍个视频就能反馈状态。另外提一下STM32CubeMX生成工程后代码的可读性比手工编写工程好很多但也有一些代码是需要手动修改的比如自定义密钥的存放路径、定义自己的app_entry.c入口逻辑。每次用CubeMX重新生成工程时这些手动修改会被覆盖所以我强烈建议把修改过的文件纳入版本管理并做好生成工程后需要重新打补丁的文档记录。8. 一些心得和注意事项回到AN5056本身它虽然是一篇应用笔记但涵盖的内容深度已经接近一份小型SDK使用手册。建议第一次接触SBSFU的朋友先不要动自己的业务代码老老实实照着AN5056的demo跑一遍把整个流程走通再开始往实际项目里迁移。我自己第一次跑demo时光是弄明白三个工程烧录顺序就花了几个小时后来发现只要把编译生成的bin文件按地址烧录进去剩下的问题其实都是配置层面的细节。还有一点SBSFU的日志系统默认是英文缩写风格对新人不太友好。但别急着改日志代码先学会从这些简称里反推系统状态。比如SBSFU_IMG_AUTH_START表示开始校验镜像SBSFU_IMG_AUTH_OK表示校验通过SBSFU_JUMP_TO_APP表示准备跳转。理解了这一串状态流转整个安全启动的流程就清晰了。最后再分享一个小技巧做SBSFU集成时无论如何都要保留一个可以通过Boot进入的救援升级模式。也就是说万一App刷坏了Boot至少还能通过串口或USB接收一个有效镜像包让设备恢复回来。这个模式不是SBSFU默认开启的需要你在Boot层自己留一个判断逻辑。标准方案里Boot会无条件验签并跳转App如果App坏了Boot会一直停在失败状态等待有效固件。此时如果你没有预留任何通信接口这块板子就变砖了。哪怕是开发阶段这也是非常影响效率的问题。所以我在自己的工程里加了一个I/O检测如果某个按键在上电时被按住就不跳转App而是直接进入UART升级接收模式。这个改动很小却在关键时刻救过我很多次。