ESC
输入关键词搜索文章标题和内容

嵌入式OTA升级原理解密:从下载到刷写,设备如何安全"复活"

本文由 linuxROS 整理发布,首发于 linuxros.cn,转载请注明出处。

嵌入式OTA升级原理解密:从下载到刷写,设备如何安全"复活"

导读:OTA升级不是"下载一个bin文件往里写"这么简单。本文聚焦两种主流方案——MCU的双Bank架构与Linux的A/B分区,讲清楚升级过程中"断电不失手,变砖可回滚"的核心机制,并对比 SWUpdate、Mender、RAUC三大开源工具。


一、OTA升级核心原理

无论MCU还是Linux,OTA的本质都是先写备份、再切换、最后擦除的三步走:

flowchart TB subgraph 下载["🔵 第一步:下载固件到暂存区"] D1["接收.swu/.bin固件包"] D2["完整性校验<br/>SHA-256 / CRC32"] D3["签名验证<br/>RSA / ECDSA"] D4["写入备用区"] end subgraph 重启["🟡 第二步:重启进入Bootloader"] R1["设置更新标志位"] R2["跳转至Bootloader"] R3["Bootloader读取标志位"] end subgraph 刷写["🟢 第三步:Bootloader校验并写入目标分区"] W1["从备用区读取固件"] W2["验签+验完整性"] W3["解压(如已压缩)"] W4["写入目标Flash分区"] W5["更新启动标记"] end subgraph 回滚["🔴 异常处理"] B1{"启动成功?"} B2["回滚至旧版本"] B3["报告升级失败"] end 下载 --> 重启 --> 刷写 刷写 -->|"重启验证"| B1 B1 -->|"否"| B2 B1 -->|"是"| B3 style 下载 fill:#E3F2FD,stroke:#1976D2 style 重启 fill:#FFF8E1,stroke:#F57C00 style 刷写 fill:#E8F5E9,stroke:#388E3C style 回滚 fill:#FFEBEE,stroke:#D32F2F

断电不失手的秘密:新固件永远先写入备用区,原固件原地不动。断电发生时Bootloader依然能加载旧系统。


二、MCU方案:双Bank架构

MCU没有文件系统,Flash直接被分成两块——Bank A和Bank B,轮着用。

来自 linuxros.cn · linuxROS

2.1 Flash分区结构

flowchart TB subgraph Flash布局["MCU Flash 分区布局"] BL["Bootloader<br/>固定区域<br/>永不改动"] BA["App Bank A<br/>当前运行固件"] BB["App Bank B<br/>备用区<br/>接收新固件"] CFG["Config区<br/>升级标志/版本号"] end style BL fill:#F3E5F5,stroke:#7B1FA2 style BA fill:#E8F5E9,stroke:#388E3C style BB fill:#FFF8E1,stroke:#F57C00 style CFG fill:#E3F2FD,stroke:#1976D2

2.2 双Bank升级流程

flowchart TB subgraph 正常启动["正常启动路径"] A1["上电/复位"] --> A2{"Bank A 可用?"} A2 -->|"是"| A3["从Bank A启动"] A3 --> A4["运行App"] end subgraph 升级流程["OTA升级路径"] B1["接收新固件"] --> B2["写入Bank B"] B2 --> B3["Bank B 验签"] B3 --> B4{"验签通过?"} B4 -->|"否"| B5["丢弃Bank B<br/>保持Bank A"] B4 -->|"是"| B6["设置升级标志<br/>bank_to_boot=B"] B6 --> B7["系统重启"] B7 --> A2 A2 -->|"标志指向B"| A3B["从Bank B启动"] A3B --> A4B["运行新固件"] A4B --> A5["Bank A变为备用区"] end style A1 fill:#E3F2FD,stroke:#1976D2 style A2 fill:#FFF8E1,stroke:#F57C00 style BA fill:#E3F2FD,stroke:#1976D2 style BB fill:#FFF8E1,stroke:#F57C00 style B4 fill:#FFF8E1,stroke:#F57C00 style B5 fill:#FFEBEE,stroke:#D32F2F style A5 fill:#E3F2FD,stroke:#1976D2

2.3 常见MCU OTA开源方案

方案 特点 适用场景
MCUBoot 开源安全Bootloader,支持签名验签、AES加密、双Bank NXP/i.MX系列
STM32FOTA ST官方方案,支持X-CUBE-BLE/X-CUBE-SBSFU STM32全系列
ESP8266/ESP32 OTA WiFi模块内置,支持HTTP/HTTPS差分升级 ESP系列
TI UNIFLASH 德州仪器方案,支持Serial/BLE/WiFi多种方式 TI CC系列

三、Linux方案:A/B分区

Linux设备有文件系统,OTA分区远比MCU复杂。主流方案是用A/B分区实现无缝切换。

3.1 eMMC/SD卡分区结构

flowchart TB subgraph Boot["Boot分区 A/B"] K1["Kernel A"] K2["Kernel B"] end subgraph Rootfs["Rootfs分区 A/B"] R1["rootfs A<br/>当前系统"] R2["rootfs B<br/>备用系统"] end subgraph Data["Data数据分区"] DAT["数据分区<br/>配置文件/日志/应用数据"] end subgraph Config["Config配置区"] C["UBoot环境变量<br/>bootlimit/bootpart"] end style Boot fill:#E3F2FD,stroke:#1976D2 style Rootfs fill:#E8F5E9,stroke:#388E3C style Data fill:#FFF8E1,stroke:#F57C00 style Config fill:#F3E5F5,stroke:#7B1FA2

3.2 A/B分区升级流程

flowchart TB subgraph 运行中["当前系统运行中"] CUR["rootfs A 正在运行<br/>应用持续服务"] end subgraph 下载写入["OTA下载写入"] DL["从服务器下载.swu包"] VER["sw-description<br/>描述镜像配置"] WR["写入rootfs B<br/>不触碰rootfs A"] end subgraph 切换["重启切换"] SW["更新UBoot环境变量<br/>bootpart=B"] RE["重启"] end subgraph 验证["新系统启动验证"] NEW["从rootfs B启动"] CHECK{"健康检查通过?"} OK["确认升级成功<br/>rootfs A变为备用"] FAIL["回滚rootfs A<br/>保持原状"] end CUR -->|"后台下载"| DL DL --> WR --> SW --> RE --> NEW --> CHECK CHECK -->|"成功"| OK CHECK -->|"失败"| FAIL style CUR fill:#E8F5E9,stroke:#388E3C style DL fill:#E3F2FD,stroke:#1976D2 style WR fill:#FFF8E1,stroke:#F57C00 style SW fill:#FFF8E1,stroke:#F57C00 style NEW fill:#E8F5E9,stroke:#388E3C style CHECK fill:#FFF8E1,stroke:#F57C00 style OK fill:#E8F5E9,stroke:#388E3C style FAIL fill:#FFEBEE,stroke:#D32F2F

3.3 UBoot A/B切换原理

flowchart TB subgraph 环境变量["UBoot 环境变量"] PART["mmcbootpart=1<br/>当前启动分区"] PARTB["mmcbootpart=2<br/>备用分区"] LIMIT["bootlimit=3<br/>允许连续失败次数"] end subgraph 切换逻辑["A→B 切换逻辑"] SET["setenv bootpart 2"] SAV["saveenv<br/>固化到Flash"] REBOOT["reset"] end subgraph 回滚机制["连续失败回滚"] CNT["fail_count++"] CHK{"fail_count > bootlimit?"} ROLL["回滚至旧分区<br/>fail_count=0"] end SET --> SAV --> REBOOT CNT --> CHK -->|"是"| ROLL style PART fill:#E3F2FD,stroke:#1976D2 style PARTB fill:#FFF8E1,stroke:#F57C00 style SET fill:#FFF8E1,stroke:#F57C00 style CHK fill:#FFF8E1,stroke:#F57C00 style ROLL fill:#FFEBEE,stroke:#D32F2F

四、开源方案对比

方案 类型 签名验签 差分升级 断电保护 适用平台
SWUpdate 块+文件级 ✅ RSA/ECDSA ✅ ✅ A/B分区 嵌入式Linux
Mender 块级 ✅ RSA ✅ ✅ A/B分区 Yocto/Linux
RAUC 块级 ✅ RSA/Ed25519 ✅ ✅ A/B分区 嵌入式Linux
balenaOS 容器化 ✅ ✅ delta ✅ A/B分区 ARM/Yocto

核心结论:三者都支持A/B分区和签名验签。SWUpdate最灵活(支持单文件更新),RAUC安全标准最高(Muttus模式),Mender与Yocto集成最深。


五、总结

OTA升级的本质是**"不破坏性写入 + 可回滚切换"**。记住一个口诀:

MCU看Bank,Linux看分区;下载写备用,重启再切换;验签不过关,坚决不刷写。

方案 核心差异
MCU双Bank Flash物理分成两块,Bootloader决定从哪块启动
Linux A/B分区 文件系统级分区,OTA工具写备用分区,UBoot切换启动目标
共同点 新固件验签通过前,原系统纹丝不动;任何阶段失败都能回滚

版权声明

作者linuxROS
协议本作品采用 CC BY-NC-SA 4.0 许可协议:署名-非商业性使用-相同方式共享
关注欢迎关注微信公众号 linuxROS,获取更多机器人 / 嵌入式 / Linux 干货
返回首页