3. 恢复镜像(Recovery Image)的概念

聊到防回滚失败后的恢复方案,绕不开一个核心角色——Recovery 镜像。说白了,它就是系统里最后一道物理防线。我参与过的几个嵌入式项目,但凡 Recovery 分区设计得不够健壮,出问题后基本只能返厂烧录。所以这一节,咱们把它的概念、定位和工作原理彻底讲透。

3.1 Recovery 分区的作用

Recovery 分区,是一个独立于主系统(Boot + System)的最小化可执行环境。它不依赖主系统分区,也不依赖用户数据分区。它的核心使命只有一条:在系统无法正常启动时,提供一个可靠的修复入口。

具体来说,Recovery 分区承担这几项职责:

  • 系统修复:刷写损坏的 Boot 分区、System 分区或 Vendor 分区。
  • 回滚操作:当主系统因防回滚机制(AVB/Verified Boot)校验失败而拒绝启动时,通过 Recovery 刷入旧版本镜像。
  • 日志导出:读取主系统崩溃时的 last_kmsg、pstore 日志,辅助定位根因。
  • 工厂重置:擦除用户数据分区(userdata),恢复出厂状态。

关键认知:Recovery 分区是系统启动链中的“安全网”。它必须独立、完整、不可被主系统篡改。我见过一个项目,Recovery 分区和 Boot 分区共用同一个校验链,结果 Boot 被写坏后 Recovery 也跟着校验失败——那真是叫天天不应。

3.2 Recovery 分区与 Boot 分区的区别

很多刚入行的工程师容易把这两个分区搞混。我刚开始做嵌入式时也犯过这个错——以为 Recovery 就是 Boot 的备份。其实完全不是一回事。咱们用一张表来对比:

对比维度 Boot 分区 Recovery 分区
启动优先级 第一级,正常启动链入口 第二级,仅在 Boot 失败或按键触发时进入
镜像大小 通常 32MB~64MB(含内核 + dtb + ramdisk) 通常 64MB~128MB(含完整内核 + 独立文件系统)
文件系统 仅包含 initramfs,启动后挂载 system 分区 包含完整的最小化文件系统(如 busybox + 刷写工具)
校验方式 受 AVB/Verified Boot 保护,校验失败则拒绝启动 通常只做完整性校验(如 hash 校验),不做防回滚校验
可写性 正常运行时不可写,仅 OTA 更新时写入 同样不可写,但 Recovery 环境自身可以刷写其他分区
依赖关系 依赖 Bootloader 加载,依赖 System 分区 仅依赖 Bootloader,不依赖任何其他分区

你想想看,Boot 分区就像一扇大门,系统正常时从这扇门进去。Recovery 分区则是一扇防火门——平时关着,但一旦大门被堵死,它就是你唯一的逃生通道。所以 Recovery 分区必须设计得比 Boot 分区更“抗造”。

我的经验:在分区布局时,我习惯把 Recovery 分区放在 Boot 分区之前(物理地址更低)。这样即使 Boot 分区被完全写坏,Bootloader 仍然能通过固定偏移找到 Recovery 分区。曾经有个项目把 Recovery 放在最后,结果 Boot 分区损坏后 Bootloader 连分区表都读不出来——嗯,后来我们改了布局。

3.3 Recovery 模式的工作原理

Recovery 模式的工作流程,其实是一个有限状态机。咱们先看一张流程图,再逐段拆解:

Recovery 模式工作流程图 Recovery 模式工作流程图 Bootloader 检测启动条件 按键触发 / Boot 校验失败? 是 否 进入 Recovery 模式 正常启动 Boot 分区 加载 Recovery 内核 + ramdisk 挂载最小文件系统 等待用户操作 / 自动脚本 说明 Recovery 模式不依赖 主系统分区。 即使 System 分区全空, Recovery 仍可正常工作。

流程拆解如下:

  1. Bootloader 检测启动条件:上电后,Bootloader 首先检查硬件按键状态(如音量减 + 电源键),同时读取 misc 分区中的启动标记(如 boot-recovery 标记)。
  2. 判断是否进入 Recovery:如果检测到按键组合、misc 分区标记为 recovery,或者 Boot 分区校验失败(AVB 返回错误),则跳转到 Recovery 入口。否则正常加载 Boot 分区。
  3. 加载 Recovery 内核:Bootloader 从 Recovery 分区读取内核镜像(zImage/Image)和设备树(dtb),加载到内存指定位置。
  4. 挂载最小文件系统:内核启动后,挂载 Recovery ramdisk 中的文件系统。这个文件系统通常包含 busybox、刷写工具(fastboot、update_engine)、日志工具等。
  5. 等待用户操作或执行自动脚本:Recovery 模式启动后,要么显示菜单等待用户选择(如 apply update、wipe data),要么执行预置的自动恢复脚本(如从 SD 卡或 OTA 包刷写系统)。

注意:Recovery 模式下的文件系统是内存盘(ramdisk),所有修改在重启后都会丢失。这意味着你不能在 Recovery 模式下“安装”任何持久化工具——每次刷写操作都需要重新加载镜像包。我曾经踩过这个坑,试图在 Recovery 里保存日志到本地,结果重启后全没了。后来改用 pstore 或直接输出到串口才解决。

3.4 Recovery 模式与防回滚的关系

防回滚机制(Rollback Protection)通常只作用于 Boot 和 System 分区。Recovery 分区本身不参与版本号校验。为什么?

原因很简单:如果 Recovery 也做防回滚,那一旦 Recovery 镜像版本过低,系统就彻底失去了修复能力——这等于自己把自己锁死在门外。所以业界惯例是:Recovery 分区只做完整性校验(确保镜像没被损坏),不做版本号比较。

但这里有个微妙之处:Recovery 分区刷写 Boot 或 System 时,刷入的镜像仍然要遵守防回滚规则。也就是说,Recovery 可以刷写任意版本的镜像,但如果刷入的 Boot 版本低于当前防回滚阈值,下次启动时 AVB 仍然会拒绝加载。所以 Recovery 只是“通道”,不是“免检票”。

避坑指南:我曾经在一个项目中,Recovery 刷入了旧版 Boot 镜像,结果启动时 AVB 报错“rollback index mismatch”。当时排查了半天,最后发现是 Recovery 刷写工具没有更新防回滚阈值。解决方案是:在 Recovery 刷写流程中,显式调用 update_engine --update_rollback_index 来同步版本号。嗯,从那以后我把这个步骤写进了刷写脚本的强制检查项。

3.5 小结