rk35xx 通过recovery升级问题

rk35xx 通过recovery升级问题 Firefly 的recovery库是一个核心组件它构建了一个独立的微型 Linux 系统专门用于在设备主系统之外执行高可靠性的固件升级。简单来说它的工作流程是主系统通过命令触发将升级指令写入特定分区并重启随后引导程序U-Boot加载recovery系统最后recovery系统解析并执行升级包完成固件更新。整个流程与 Rockchip 平台深度集成主要用于实现系统级OTA升级和恢复出厂设置。 OTA 升级原理与完整流程Rockchip 平台的 OTA 升级流程可以清晰地拆分为以下几个步骤渲染错误:Mermaid 渲染失败: Parse error on line 5: .... 写入升级指令| D[misc 分区 (BCB)] A -- -----------------------^ Expecting SQE, DOUBLECIRCLEEND, PE, -), STADIUMEND, SUBROUTINEEND, PIPE, CYLINDEREND, DIAMOND_STOP, TAGEND, TRAPEND, INVTRAPEND, UNICODE_TEXT, TEXT, TAGSTART, got PS步骤 1: 升级触发与数据准备用户触发升级后升级程序 (update_engine或rkupdate) 会完成两项核心工作校验升级包首先对 OTA 包如update.img或update.zip进行签名校验确保其完整性和来源可信。写入 BCB 指令校验通过后会向misc分区的启动控制块BCB写入特定指令。其中command字段是关键通常为boot-recovery同时recovery字段会携带升级包的路径例如--update_package/udisk/update.img。这个操作就是告诉引导程序下次启动时要做什么。 关于misc分区和 BCB 结构体misc分区是一个数据中转站在用户空间通常通过文件系统节点例如/dev/block/by-name/misc进行访问。BCB 结构体的核心内容定义在bootable/recovery/bootloader.h中structbootloader_message{charcommand[32];// 如 boot-recoverycharstatus[32];// 升级进度状态charrecovery[1024];// 详细升级指令如 --update_package/sdcard/update.zip// ... 其他字段};步骤 2: 重启并进入 Recovery 模式主系统执行重启命令reboot。硬件重启控制权移交给引导程序通常是 U-Boot。U-Boot 在启动过程中会检查misc分区的 BCB 数据。若读取到command字段包含boot-recoveryU-Boot 便会根据预设配置加载recovery分区上的recovery.img镜像而不是常规的boot.img。步骤 3: Recovery 系统执行升级recovery.img被加载到内存并启动它实际上是一个包含精简内核和最小根文件系统通常基于initramfs的微型系统。其启动流程如下系统启动内核启动并挂载initramfs。第一个执行的程序通常是/init。启动核心程序init进程会根据配置文件如init.rc启动recovery主程序。recovery服务会解析 BCB 信息。调用具体执行者recovery程序会解析 BCB 中的指令识别升级包类型并调用相应的升级后端rkupdate后端专门用于处理 Rockchip 私有格式的update.img完整镜像包。update_engine后端功能更强大支持update.zip、update.img等多种包格式并兼容 A/B 分区升级。写入固件rkupdate或update_engine根据指令解析升级包文件并将其中包含的各个分区镜像如boot.img,system.img等准确地写入到 Flash 存储的对应分区中。 关键代码模块与实现分析recovery项目的源码目录通常位于bootable/recovery/。以下是其核心模块与代码实现的关键部分。1.recovery主程序流程控制中枢 (recovery.cpp)recovery程序的入口在recovery.cpp的main()函数。它会解析 BCB决定是执行升级还是显示菜单是升级流程的控制中心。// bootable/recovery/recovery.cpp (简化逻辑)intmain(intargc,char**argv){// 1. 加载并解析 BCB (Bootloader Control Block)std::string boot_command;std::string boot recovery;// ... 从 misc 分区读取数据参考 bootloader.cpp 的 get_bootloader_message_mtd// 读取到的内容会被存入结构体 struct bootloader_message// 2. 解析命令行参数例如 --update_package/path/to/package// adb 等会将这些参数写入 /cache/recovery/command// get_args() 会合并 BCB 和 /cache/recovery/command 中的参数// update_package 变量最终会包含 OTA 包的路径如 /sdcard/update.zip// 3. 根据命令执行相应操作// process_update_package(update_package) 是执行升级的核心函数if(!update_package.empty()){// 处理 OTA 升级包intstatusinstall_package(update_package);// 升级成功则重启失败则可能显示错误菜单}elseif(!wipe_data){// 显示 Recovery 主菜单ui-ShowMenu();}return0;}代码逻辑参考了 Android 原生 recovery 模块的实现方式。updater机制脚本驱动的升级逻辑当升级包为update.zip格式时recovery会启动updater程序。updater的核心是执行包内的META-INF/com/google/android/updater-script脚本该脚本使用 Edify 语言编写定义了每一步升级操作如分区擦除、文件写入等。updater-script示例# 显示进度 ui_print(Starting update...); # 擦除 system 分区 format(ext4, EMMC, /dev/block/by-name/system, 0); # 将 new_system.img 写入 system 分区 block_image_update(/dev/block/by-name/system, package_extract_file(system.transfer.list), system.new.dat, system.patch.dat); # 写入 boot 分区 package_extract_file(boot.img, /dev/block/by-name/boot); ui_print(Update completed successfully.);2.rkupdate后端Rockchip 私有格式执行者 (external/rkupdate/)rkupdate是处理 Rockchip 私有update.img固件的后端程序。update.img是一个包含多个分区镜像的集合包。rkupdate的核心功能是解析这个集合包并将每个分区镜像烧录到 Flash 的正确位置。下面是update.img解析的核心代码片段简化逻辑// external/rkupdate/rkupdate.c (简化逻辑)intdo_rkupdate(constchar*package_path){FILE*fpfopen(package_path,rb);// 1. 解析 Rockchip 固件头部 (RC4)// 读取前几个字节确认是 RC4 格式并获取头部信息intfdopen(package_path,O_RDONLY);rc4_header header;read(fd,header,sizeof(header));// 2. 循环处理每个分区镜像// 根据头部信息找到每个分区的起始地址和长度// 然后逐个分区进行烧写for(inti0;iheader.num_images;i){// 获取分区镜像的偏移和大小off_toffsetget_partition_offset(i);size_tsizeget_partition_size(i);constchar*nameget_partition_name(i);// 3. 烧写到对应分区// 调用 rkflash 相关函数写入 /dev/block/by-name/ 下的设备节点if(write_partition(name,fd,offset,size)!0){LOGE(Failed to write partition %s,name);return-1;}// 写入进度更新update_progress(...);}fclose(fp);LOGI(rkupdate completed successfully!);return0;}3.update_engine后端现代化升级方案 (external/recovery/update_engine/)update_engine是一个设计上更现代化、功能更全面的升级后端。它是一个独立的守护进程通过 D-Bus 通信采用状态机管理升级任务将升级过程抽象为IDLE-CHECKING-DOWNLOADING-UPDATING等状态。架构优势通过抽象接口PartitionWriter,FileSystem等实现了高内聚低耦合便于移植和扩展。多格式支持原生支持update.zip和update.img。update_engine的状态机迁移可以简化为IDLE - CHECKING - DOWNLOADING - UPDATING - IDLE | | | --------------------------- ERROR️ 独立实现与进阶方案除了使用 Rockchip 的官方方案嵌入式 Linux 设备还有多种构建 OTA 能力的途径。独立实现方案要独立实现一套 OTA 系统核心在于构建一个可靠、安全的升级环境。关键步骤和组件如下制作 Recovery 镜像 (recovery.img)这个镜像本身是一个独立的 Linux 系统。你可以采用以下方式构建基于 Buildroot在 Buildroot 的配置中为另一个配置 (output/recovery/) 选择最小化的 Linux 内核和 BusyBox并加入升级程序源码。BCB 操作与系统服务编写一个独立程序例如my_ota_client来操作/dev/block/by-name/misc分区用于写入和读取 BCB 指令。使用 Linux 的inotify机制或实现一个简单的守护进程来监听升级事件。引导程序适配修改 U-Boot 源码使其在启动流程中加入对misc分区的检查逻辑。这通常涉及 Rockchip 特有的reboot mode驱动。升级包生成脚本编写一套脚本用于生成最终可在设备上使用的update.img或update.zip升级包。进阶实现方案使用开源框架 (SWUpdate/RAUC/Mender)这是实现专业级 OTA 功能的主流选择。它们功能完善社区活跃可极大缩短开发周期。SWUpdate最灵活支持本地、网络等多种升级方式并可直接复用update-engine。RAUC在需要复杂的 A/B 分区更新和回滚机制的场景下架构非常清晰。Mender提供开箱即用的完整解决方案。引入 A/B (Seamless) 分区升级这是提升用户体验的关键。系统拥有两套独立分区A 和 B。升级时系统在后台更新非当前运行的那套分区完成后只需简单重启即可切换实现了真正的“无缝”升级且升级失败能自动回滚。两种方案对比维度Rockchip 官方方案开源方案 (SWUpdate 等)集成难度低与 SDK 深度集成中高需独立移植灵活性受限于 Rockchip 框架非常高可深度定制社区生态依赖 Rockchip 支持全球开发者社区活跃维护成本随 SDK 更新需要持续关注上游总的来说Firefly 的recovery库展示了 Rockchip 平台实现系统升级的标准方法其核心是围绕recovery系统这一独立环境来保证升级的高可靠性。在工程实践中如果追求快速开发和与 SDK 的深度集成Rockchip 的官方方案是首选而当项目需要更灵活的升级策略、更强的社区支持或有特定的安全要求时采用 SWUpdate 等成熟的开源框架将更具优势。