<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>固件升级 on Zero Day Notes</title>
        <link>https://blog.5772447.xyz/tags/%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7/</link>
        <description>Recent content in 固件升级 on Zero Day Notes</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh</language>
        <lastBuildDate>Fri, 09 Oct 2026 09:00:00 +0800</lastBuildDate><atom:link href="https://blog.5772447.xyz/tags/%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>OTA 固件升级断电变砖：A/B 双分区缺位</title>
            <link>https://blog.5772447.xyz/posts/7fec59ab/</link>
            <pubDate>Fri, 09 Oct 2026 09:00:00 +0800</pubDate>
            <guid>https://blog.5772447.xyz/posts/7fec59ab/</guid>
            <description>&lt;h2 id=&#34;问题背景&#34;&gt;&lt;a href=&#34;#%e9%97%ae%e9%a2%98%e8%83%8c%e6%99%af&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;问题背景&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;公司的环境监测终端(基于 STM32 + 外挂 Flash,采集温湿度与气体数据上报)部署了 300 余台，分布在不同厂房。早期固件更新靠现场拆机用 ST-Link 刷写，一台半小时；今年初固件加入了远程 OTA 能力，通过后台下发升级包，设备在凌晨空闲时段自行下载并升级——半年来跑过三次小版本升级，一切正常，OTA 被视为理所当然的能力。&lt;/p&gt;&#xA;&lt;p&gt;10 月上旬，新固件 v2.4(新增 Modbus-RTU 从站支持)按计划对 300 台设备批量推送。升级当夜厂区恰有计划性电路检修，多栋厂房间歇断电。次日上午盘点：23 台设备无法上线，现场确认全部处于&amp;quot;变砖&amp;quot;状态——上电后设备指示灯常亮但不启动，串口无任何输出。&lt;/p&gt;&#xA;&lt;h2 id=&#34;故障现象&#34;&gt;&lt;a href=&#34;#%e6%95%85%e9%9a%9c%e7%8e%b0%e8%b1%a1&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;故障现象&#xD;&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;23 台变砖设备的分布与当晚断电区域完全重合;其余 277 台升级成功且运行正常。&lt;/li&gt;&#xA;&lt;li&gt;变砖设备上电后 Bootloader 打印固件头校验失败后停在一个死循环，不尝试任何恢复动作。&lt;/li&gt;&#xA;&lt;li&gt;通过烧录座读回 Flash 内容:应用分区的固件是&lt;strong&gt;半新半旧&lt;/strong&gt;——前半段是 v2.4 的新数据，后半段还是 v2.3 的残留，写入在断电瞬间被硬切。&lt;/li&gt;&#xA;&lt;li&gt;后台升级记录显示这 23 台的状态停留在&amp;quot;下载完成，写入中&amp;quot;。&lt;/li&gt;&#xA;&lt;li&gt;设备没有电池备份，断电即失电——没有&amp;quot;优雅收尾&amp;quot;的机会。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;排查过程&#34;&gt;&lt;a href=&#34;#%e6%8e%92%e6%9f%a5%e8%bf%87%e7%a8%8b&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;排查过程&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;第一步确认变砖机理。读回的 Flash 镜像说明了一切：这台设备的 OTA 是&lt;strong&gt;单分区就地覆写&lt;/strong&gt;——bootloader 把新固件直接写进当前运行的应用分区，写入过程被断电打断，应用分区落下一个既不是 v2.3 也不是 v2.4 的残缺镜像。重启后 bootloader 校验固件头 CRC 失败，而它的逻辑里&amp;quot;校验失败&amp;quot;没有对应的恢复路径(设计时假设校验失败不会发生)，直接死循环。也就是说，这台设备不是&amp;quot;升级失败&amp;quot;，是&amp;quot;升级机制里根本没有失败这个状态&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;第二步复盘为什么半年没事、这次出事。前三次升级全部成功，是因为没有断电；OTA 机制的脆弱点从来都在，只是被&amp;quot;升级时段不会断电&amp;quot;这个未成文的假设掩盖着。这次计划性检修恰好落在升级窗口，把假设击穿了——300 台里有 23 台(7.7%)在写入窗口内碰上断电，概率与检修时长吻合。&lt;/p&gt;&#xA;&lt;p&gt;第三步看厂商方案与业界惯例的差距。向固件外包厂商索要设计文档，其 OTA 方案为&amp;quot;下载到暂存区 → 擦除应用区 → 逐块写入 → CRC 校验&amp;quot;，确实是单分区。而嵌入式行业对&amp;quot;升级中断电&amp;quot;的标准答案是 &lt;strong&gt;A/B 双分区&lt;/strong&gt;(或带回滚的 reserve 分区)：新固件写入备用分区，校验通过后仅切换启动指针；断电只损失一次升级，设备永远能从完好的另一半启动。厂商文档里也承认 v1 方案&amp;quot;未考虑升级过程中失电&amp;quot;。&lt;/p&gt;&#xA;&lt;p&gt;第四步明确救砖与防再发的两条线：变砖的 23 台必须现场拆机刷写(这正是 OTA 要消灭的作业方式，等于为设计缺陷返工一遍)；更重要的是，OTA 机制本身必须重做，否则下次任何一次意外断电(不限于计划检修)都会批量复现。&lt;/p&gt;&#xA;&lt;h2 id=&#34;解决方案&#34;&gt;&lt;a href=&#34;#%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;解决方案&#xD;&#xA;&lt;/h2&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;救砖&lt;/strong&gt;：23 台设备分三个片区安排现场刷写，用烧录座恢复 v2.4;顺带为每台设备加装&amp;quot;升级窗口 UPS 或超级电容&amp;quot;作为临时缓冲(硬件改造，随下次维护批量实施)。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;OTA 重做(治本)&lt;/strong&gt;：固件升级为 A/B 双分区方案——Flash 划分 slot A/slot B,新固件写入非活动分区，全量 CRC 与签名校验通过后置&amp;quot;试运行&amp;quot;标志，bootloader 引导新固件；新固件首次上报心跳后才置&amp;quot;确认&amp;quot;标志，否则下次复位自动回滚旧分区。断电在任何时刻发生，设备都能从完整分区启动，最坏损失是&amp;quot;这次升级没成功&amp;quot;。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;完整性加强&lt;/strong&gt;：升级包加 Ed25519 签名校验，bootloader 拒绝未签名镜像——顺带堵住了&amp;quot;伪造升级包&amp;quot;这个此前完全敞开的安全口子。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;运维策略&lt;/strong&gt;：后台批量升级按厂房分批灰度(先 5% 观察 24 小时再放量)；升级窗口与厂区电力作业计划建立联动，电力检修日在后台冻结升级任务。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;根因分析&#34;&gt;&lt;a href=&#34;#%e6%a0%b9%e5%9b%a0%e5%88%86%e6%9e%90&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;根因分析&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;直接根因是&lt;strong&gt;OTA 采用单分区就地覆写，升级窗口内断电使应用分区留下残缺镜像，bootloader 无失败恢复路径而陷入死循环&lt;/strong&gt;。深层根因是设计阶段接受了&amp;quot;升级期间不会断电&amp;quot;的隐含假设，却没有为假设失效设计任何兜底——嵌入式设备部署在物理世界，断电不是异常而是必然事件；半年无事只说明运气好，不说明机制对。&lt;/p&gt;&#xA;&lt;h2 id=&#34;预防措施&#34;&gt;&lt;a href=&#34;#%e9%a2%84%e9%98%b2%e6%8e%aa%e6%96%bd&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;预防措施&#xD;&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;固件设计评审新增&amp;quot;失电安全&amp;quot;必审项：任何写 Flash 的路径都必须回答&amp;quot;此刻断电会怎样&amp;quot;，答不出就不是可发布方案；&lt;/li&gt;&#xA;&lt;li&gt;A/B 双分区+签名校验列为公司嵌入式设备的 OTA 标准架构，新项目强制执行；&lt;/li&gt;&#xA;&lt;li&gt;批量 OTA 一律分批灰度，禁止全量一夜推送；&lt;/li&gt;&#xA;&lt;li&gt;设备侧升级窗口与现场电力计划联动，检修日冻结升级任务。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;总结&#34;&gt;&lt;a href=&#34;#%e6%80%bb%e7%bb%93&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;总结&#xD;&#xA;&lt;/h2&gt;&lt;p&gt;这 23 台变砖设备是同一种工程错误的 23 份罚单：在嵌入式世界，&amp;ldquo;用户可能随时拔电源&amp;quot;不是边界情况，而是默认运行环境。OTA 的全部价值就在于让人不在现场也能安全地改设备——而单分区覆写方案恰恰把这个前提卖掉了。A/B 双分区的思想成本极低(多一半 Flash、一个启动指针)，却把&amp;quot;升级失败&amp;quot;从不可恢复的砖变成了可重试的日常。任何写持久存储的过程，都值得先问一句：此刻断电，明天它会以什么状态醒来？&lt;/p&gt;&#xA;&lt;h2 id=&#34;延伸阅读&#34;&gt;&lt;a href=&#34;#%e5%bb%b6%e4%bc%b8%e9%98%85%e8%af%bb&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;延伸阅读&#xD;&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.5772447.xyz/posts/10494fcc/&#34; &gt;RS485 总线干扰致采集网关每隔几小时集体掉线&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.5772447.xyz/posts/f691db9d/&#34; &gt;基于STM32的空气质量检测仪&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.5772447.xyz/posts/c4f182e1/&#34; &gt;基于STM32的家庭气象站&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item></channel>
</rss>
