OTA 固件升级断电变砖:A/B 双分区缺位

300 台采集终端远程升级固件,现场供电波动致 23 台升级中断变砖。根因是单分区 OTA 直接覆写运行固件,无备份分区与回滚机制。改双分区 A/B 方案并加签名校验后恢复批量升级能力。

问题背景

公司的环境监测终端(基于 STM32 + 外挂 Flash,采集温湿度与气体数据上报)部署了 300 余台,分布在不同厂房。早期固件更新靠现场拆机用 ST-Link 刷写,一台半小时;今年初固件加入了远程 OTA 能力,通过后台下发升级包,设备在凌晨空闲时段自行下载并升级——半年来跑过三次小版本升级,一切正常,OTA 被视为理所当然的能力。

10 月上旬,新固件 v2.4(新增 Modbus-RTU 从站支持)按计划对 300 台设备批量推送。升级当夜厂区恰有计划性电路检修,多栋厂房间歇断电。次日上午盘点:23 台设备无法上线,现场确认全部处于"变砖"状态——上电后设备指示灯常亮但不启动,串口无任何输出。

故障现象

  • 23 台变砖设备的分布与当晚断电区域完全重合;其余 277 台升级成功且运行正常。
  • 变砖设备上电后 Bootloader 打印固件头校验失败后停在一个死循环,不尝试任何恢复动作。
  • 通过烧录座读回 Flash 内容:应用分区的固件是半新半旧——前半段是 v2.4 的新数据,后半段还是 v2.3 的残留,写入在断电瞬间被硬切。
  • 后台升级记录显示这 23 台的状态停留在"下载完成,写入中"。
  • 设备没有电池备份,断电即失电——没有"优雅收尾"的机会。

排查过程

第一步确认变砖机理。读回的 Flash 镜像说明了一切:这台设备的 OTA 是单分区就地覆写——bootloader 把新固件直接写进当前运行的应用分区,写入过程被断电打断,应用分区落下一个既不是 v2.3 也不是 v2.4 的残缺镜像。重启后 bootloader 校验固件头 CRC 失败,而它的逻辑里"校验失败"没有对应的恢复路径(设计时假设校验失败不会发生),直接死循环。也就是说,这台设备不是"升级失败",是"升级机制里根本没有失败这个状态"。

第二步复盘为什么半年没事、这次出事。前三次升级全部成功,是因为没有断电;OTA 机制的脆弱点从来都在,只是被"升级时段不会断电"这个未成文的假设掩盖着。这次计划性检修恰好落在升级窗口,把假设击穿了——300 台里有 23 台(7.7%)在写入窗口内碰上断电,概率与检修时长吻合。

第三步看厂商方案与业界惯例的差距。向固件外包厂商索要设计文档,其 OTA 方案为"下载到暂存区 → 擦除应用区 → 逐块写入 → CRC 校验",确实是单分区。而嵌入式行业对"升级中断电"的标准答案是 A/B 双分区(或带回滚的 reserve 分区):新固件写入备用分区,校验通过后仅切换启动指针;断电只损失一次升级,设备永远能从完好的另一半启动。厂商文档里也承认 v1 方案"未考虑升级过程中失电"。

第四步明确救砖与防再发的两条线:变砖的 23 台必须现场拆机刷写(这正是 OTA 要消灭的作业方式,等于为设计缺陷返工一遍);更重要的是,OTA 机制本身必须重做,否则下次任何一次意外断电(不限于计划检修)都会批量复现。

解决方案

  1. 救砖:23 台设备分三个片区安排现场刷写,用烧录座恢复 v2.4;顺带为每台设备加装"升级窗口 UPS 或超级电容"作为临时缓冲(硬件改造,随下次维护批量实施)。
  2. OTA 重做(治本):固件升级为 A/B 双分区方案——Flash 划分 slot A/slot B,新固件写入非活动分区,全量 CRC 与签名校验通过后置"试运行"标志,bootloader 引导新固件;新固件首次上报心跳后才置"确认"标志,否则下次复位自动回滚旧分区。断电在任何时刻发生,设备都能从完整分区启动,最坏损失是"这次升级没成功"。
  3. 完整性加强:升级包加 Ed25519 签名校验,bootloader 拒绝未签名镜像——顺带堵住了"伪造升级包"这个此前完全敞开的安全口子。
  4. 运维策略:后台批量升级按厂房分批灰度(先 5% 观察 24 小时再放量);升级窗口与厂区电力作业计划建立联动,电力检修日在后台冻结升级任务。

根因分析

直接根因是OTA 采用单分区就地覆写,升级窗口内断电使应用分区留下残缺镜像,bootloader 无失败恢复路径而陷入死循环。深层根因是设计阶段接受了"升级期间不会断电"的隐含假设,却没有为假设失效设计任何兜底——嵌入式设备部署在物理世界,断电不是异常而是必然事件;半年无事只说明运气好,不说明机制对。

预防措施

  • 固件设计评审新增"失电安全"必审项:任何写 Flash 的路径都必须回答"此刻断电会怎样",答不出就不是可发布方案;
  • A/B 双分区+签名校验列为公司嵌入式设备的 OTA 标准架构,新项目强制执行;
  • 批量 OTA 一律分批灰度,禁止全量一夜推送;
  • 设备侧升级窗口与现场电力计划联动,检修日冻结升级任务。

总结

这 23 台变砖设备是同一种工程错误的 23 份罚单:在嵌入式世界,“用户可能随时拔电源"不是边界情况,而是默认运行环境。OTA 的全部价值就在于让人不在现场也能安全地改设备——而单分区覆写方案恰恰把这个前提卖掉了。A/B 双分区的思想成本极低(多一半 Flash、一个启动指针),却把"升级失败"从不可恢复的砖变成了可重试的日常。任何写持久存储的过程,都值得先问一句:此刻断电,明天它会以什么状态醒来?

延伸阅读

使用 Hugo 构建
主题 Stack 由 Jimmy 设计