2. 嵌入式系统启动流程
固件升级,说白了就是给设备换脑子。我做了这么多年嵌入式,发现很多工程师把升级想得太简单——不就是写个Flash嘛?嗯,等你在产线上看到成片变砖的设备时,就知道这里面的门道有多深了。
这一章,我们先把升级的几种物理通道理清楚,再聊聊升级流程和安全考量。这些都是后面设计Bootloader的基础。
一、固件升级的几种方式
根据设备与外部通信的物理接口,固件升级主要分为四种方式。我按实际项目中的使用频率来排个序。
| 升级方式 | 典型场景 | 传输速率 | 适用阶段 |
|---|---|---|---|
| 串口升级 | 研发调试、小批量产线 | 115200 bps ~ 921600 bps | 开发、生产 |
| USB升级 | 消费电子、工业手持设备 | 12 Mbps ~ 480 Mbps | 生产、售后 |
| 网络升级 | IoT设备、网关、服务器 | 10/100 Mbps 以上 | 远程运维 |
| SD卡升级 | 嵌入式相机、车载设备 | 取决于SDIO/SPI模式 | 现场维护 |
串口升级
这是最基础的方式。我记得刚入行时,用的还是51单片机,串口下载程序得用ISP工具,每次都要手动拉低BOOT引脚。现在虽然方便了,但串口升级的核心逻辑没变:通过UART接收固件数据,写入Flash。
串口升级的典型流程:
- 设备进入Bootloader模式(通常通过检测特定引脚电平或接收特定字符)
- 上位机发送握手命令,确认通信参数
- 按固定协议分包发送固件数据(每包通常256字节或512字节)
- 设备每收到一包,写入Flash并回传ACK
- 全部数据发送完毕,校验固件完整性
- 跳转到应用程序执行
USB升级
USB升级比串口快得多。常见的实现方式有两种:一种是模拟成U盘,用户直接把固件文件拖进去;另一种是使用DFU(Device Firmware Upgrade)协议。
我个人更推荐DFU方式。为什么?因为U盘方式需要文件系统支持,占用Flash空间大,而且容易因为文件系统损坏导致升级失败。DFU协议是专门为固件升级设计的,轻量、可靠。
USB DFU升级的关键点:
- 设备枚举为DFU设备,而不是HID或CDC设备
- 使用USB控制传输(Endpoint 0)进行命令交互
- 数据通过批量传输(Bulk Transfer)发送,速度更快
- 支持DNLOAD(下载)和UPLOAD(上传)两种操作
网络升级
这是远程运维的标配。我做过一个网关项目,设备部署在偏远山区,每次升级都要派人开车跑几百公里。后来改成OTA(Over-The-Air)升级,省了不知道多少差旅费。
网络升级的常见协议栈:
- HTTP/HTTPS:最简单,设备作为客户端下载固件文件
- MQTT:适合低带宽场景,通过主题发布固件通知
- CoAP:基于UDP,适合资源受限设备
- 私有协议:安全性更高,但开发量大
SD卡升级
SD卡升级适合现场维护场景。操作员把固件文件拷到SD卡里,插到设备上,设备检测到SD卡后自动升级。
实现要点:
- Bootloader需要支持文件系统(通常是FAT32)
- 固件文件命名要固定,比如
firmware.bin - 升级完成后要删除或重命名固件文件,防止重复升级
- 考虑SD卡热插拔检测,用GPIO中断触发
你想想看,SD卡升级的好处是什么?不需要上位机软件,不需要网络,一张卡就能搞定。但坏处也很明显——你得有人去现场插卡。
二、升级流程概述
不管用哪种方式,固件升级的宏观流程都是一样的。我画了一张流程图,把核心环节串起来。
这个流程里,最容易出问题的是第4步和第5步。校验不通过就写入,或者写入过程中断电,都会导致设备变砖。所以,校验必须在写入之前完成,或者边写边校验。
三、升级的安全性考量
安全性不是锦上添花,而是生死攸关。我见过太多因为升级安全没做好而翻车的案例。
3.1 固件完整性校验
完整性校验保证固件在传输过程中没有被篡改或损坏。常用的校验方式:
- CRC32:速度快,适合资源受限设备,但防不了恶意篡改
- SHA256:安全性高,但计算量大,需要硬件加速
- MD5:已经不推荐使用,碰撞攻击成本太低
我的建议:对于消费级产品,CRC32就够了。对于安全要求高的工业设备或金融设备,必须用SHA256 + 签名验证。我曾经在一个支付终端项目里,因为用了CRC32被安全审计打回来,后来改成SHA256+RSA签名,才通过认证。
3.2 固件加密
加密防止固件被逆向分析。特别是IoT设备,固件里往往藏着服务器地址、密钥等敏感信息。
常见的加密方案:
| 加密算法 | 密钥长度 | 适用场景 |
|---|---|---|
| AES-128 | 128 bit | 通用嵌入式设备 |
| AES-256 | 256 bit | 高安全需求设备 |
| Chacha20 | 256 bit | 无硬件AES加速的MCU |
3.3 签名验证
签名验证是防止固件被恶意替换的最后一道防线。流程是这样的:
- 开发者在服务器上用私钥对固件哈希值签名
- 设备用预置的公钥验证签名
- 签名通过才允许升级
这里有个关键点:公钥必须烧死在芯片的OTP区域,不能被软件修改。否则攻击者可以替换公钥,然后用自己的私钥签名恶意固件。
3.4 版本回滚防护
攻击者有时会利用旧版本的漏洞。比如新版本修复了某个安全漏洞,但攻击者可以强制设备回滚到有漏洞的旧版本,然后利用漏洞入侵。
防护措施:
- 在Bootloader中记录当前固件版本号
- 禁止回滚到低于某个安全基线版本的固件
- 使用版本计数器,每次升级递增,不可逆
3.5 防断电保护
升级过程中断电,是最常见的变砖原因。解决方案就是双备份机制:
- 方案A:两个固件分区,一个运行区,一个备份区。升级时写入备份区,成功后切换
- 方案B:Bootloader + 应用程序分离。Bootloader只负责升级,应用程序负责业务逻辑。升级失败时,Bootloader还能重新尝试
我个人更推荐方案A。虽然多占一倍Flash空间,但可靠性最高。你想想看,一个设备在野外运行,升级失败后还能自动回滚,这省了多少售后成本。
补充一点:双备份不只是分区,还要有切换标志。这个标志必须放在独立的存储区域(比如EEPROM或备份寄存器),不能和固件放在同一个Flash扇区。否则固件写入失败时,标志也可能被破坏。
小结
这一章我们聊了四种升级方式的特点和适用场景,梳理了升级的宏观流程,也重点讲了安全性考量。记住一句话:升级功能做出来不难,做得可靠才见功力。后面几章,我们会深入到Bootloader的具体实现,把这些安全机制一个一个落地。