二、A/B分区设计:解决固件大小不匹配问题
A/B分区是OTA升级的基础架构,核心是设计固定大小的双分区,避免固件大小差异导致的启动失败。
1. 分区设计原理
- 固定分区:设计时确定A、B分区的起始地址和大小,编译脚本将新固件链接到固定分区,不足部分用0xFF填充
- 校验策略:Bootloader只校验有效数据长度和CRC,不校验填充区
- 跳转逻辑:跳转地址按分区起始地址计算,固件大小不影响启动
2. 代码实现示例
// 分区定义#definePARTITION_A_START0x08018000#definePARTITION_A_SIZE0x13800#definePARTITION_B_START0x0802B800#definePARTITION_B_SIZE0x13800// 固件填充函数voidfirmware_pad(uint8_t*firmware,uint32_tfirmware_len,uint32_tpartition_size){if(firmware_len<partition_size){memset(firmware+firmware_len,0xFF,partition_size-firmware_len);}}// 固件校验函数boolfirmware_verify(uint8_t*firmware,uint32_tfirmware_len,uint32_tpartition_size){// 只校验有效数据长度和CRCif(firmware_len>partition_size){returnfalse;}uint32_tcrc=crc32_calculate(firmware,firmware_len);returncrc==expected_crc;}二、跳转安全:关中断的必要性与实现
从Bootloader跳转到APP前必须关闭全局中断,否则会导致HardFault错误。
1. 原理分析
- VTOR未重定向:跳转瞬间VTOR还未重定向到APP的向量表
- 中断处理错误:如果此时产生中断,CPU会去Bootloader的向量表找入口,导致程序跑飞
- 解决方法:先关全局中断、清中断标志,再设置栈指针和PC跳转
2. 代码实现示例
typedefvoid(*pFunction)(void);voidbootloader_jump_to_app(uint32_tapp_addr){// 1. 关闭全局中断__disable_irq();// 2. 清中断标志NVIC->ICER[0]=0xFFFFFFFFU;NVIC->ICPR[0]=0xFFFFFFFFU;// 3. 设置栈指针uint32_tmsp=*(volatileuint32_t*)app_addr;__set_MSP(msp);// 4. 设置PC指针pFunction jump_to_app=(pFunction)(*(volatileuint32_t*)(app_addr+4));jump_to_app();}三、签名验签:固件安全的核心防线
签名验签是防止固件篡改的关键,通常采用ECC或RSA算法。
1. 验签流程
- 签名流程:服务器用私钥对固件哈希做签名
- 验签流程:Bootloader预置公钥,验签时先算固件哈希,再用公钥验签名
- 存储策略:公钥烧在Flash安全区或OTP不可篡改区域
2. 代码实现示例
// ECC验签示例(基于STM32H7安全库)#include"crypto_ecc.h"boolverify_firmware_signature(constuint8_t*firmware,size_tlen,constuint8_t*signature){EC_KEY*pubkey=EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);// 从OTP区加载公钥参数// ...// 计算固件SHA-256uint8_thash[32];CRYPTO_SHA256(firmware,len,hash);// 构造ECDSA签名对象ECDSA_SIG*sig=ECDSA_SIG_new();BIGNUM*r=BN_bin2bn(&signature[0],32,NULL);BIGNUM*s=BN_bin2bn(&signature[32],32,NULL);ECDSA_SIG_set0(sig,r,s);// 验签bool result=(1==ECDSA_do_verify(hash,32,sig,pubkey));ECDSA_SIG_free(sig);EC_KEY_free(pubkey);returnresult;}四、失败处理:坏固件的回退机制
验签失败时,必须拒绝跳转并执行回退策略,防止设备变砖。
1. 回退策略
- A/B分区:直接回退到上一个有效分区
- 单分区:留在Boot模式,等待重新升级
- 日志记录:记录失败码并上报云端,便于问题排查
2. 代码实现示例
voidhandle_verify_failure(void){// 1. 拒绝跳转bootloader_reject_jump();// 2. 回退到上一个有效分区if(partition_type==PARTITION_AB){rollback_to_previous_partition();}else{// 单分区留在Boot模式enter_boot_mode();}// 3. 记录失败码并上报log_failure_code(FAILURE_VERIFY);report_to_cloud(FAILURE_VERIFY);}五、看门狗策略:防止OTA升级卡死
OTA升级过程中,看门狗是防止设备卡死的关键。
1. 喂狗策略
- 喂狗间隔:必须小于看门狗超时时间
- 超时设置:看门狗超时时间调到大于单次擦除周期,或使用长窗口看门狗
- 升级完成:升级完成后重新开启标准看门狗
2. 代码实现示例
// OTA升级过程中的喂狗函数voidota_feed_watchdog(void){// 在擦写循环里定期喂狗HAL_IWDG_Refresh(&hiwdg1);}// OTA升级流程voidota_upgrade_process(void){// 1. 初始化看门狗,设置长超时时间MX_IWDG1_Init();// 2. 擦写固件for(uint32_ti=0;i<firmware_len;i+=BLOCK_SIZE){flash_erase_block(i);flash_write(i,firmware+i,BLOCK_SIZE);// 喂狗ota_feed_watchdog();}// 3. 升级完成,重新开启标准看门狗HAL_IWDG_DeInit(&hiwdg1);MX_IWDG1_Init_Standard();}总结
Bootloader与OTA的核心考点集中在量产安全设计、底层跳转、固件安全、失败处理和看门狗策略五个方面。面试时,不仅要理解原理,还要能结合代码说明实现细节,这样才能在面试中脱颖而出,获得更高的薪资。