第4章:固件文件结构解析
做量产测试这么多年,我见过太多工程师拿到一个RK固件包就懵了。一个.img文件或者.update文件,里面到底装了啥?为什么有时候刷机失败,换个固件包就好了?今天我就带你把这层窗户纸捅破。
4.1 RK固件包的整体结构
说白了,RK的固件包就是一个大容器。它把多个独立的分区镜像打包在一起,再加上一些元数据信息。我个人习惯把固件包想象成一个「快递箱」——外面是包装(文件头),里面是各种零件(分区镜像)。
常见的固件包有两种格式:
- .img格式:早期RK芯片用的多,比如RK3128、RK3229。结构相对简单。
- .update格式:新平台主流格式,比如RK3568、RK3588。带签名校验,安全性更高。
不管哪种格式,核心内容都差不多。我给大家拆开看看:
| 固件组成部分 | 说明 | 是否必须 |
|---|---|---|
| 固件头部 | 包含魔数、版本号、分区数量等信息 | 是 |
| 分区表(Parameter) | 定义每个分区的起始地址、大小、名称 | 是 |
| 分区镜像数据 | bootloader、uboot、kernel、system等实际数据 | 是 |
| 校验信息 | CRC32或SHA256校验值 | 新平台必须 |
核心要点:固件包的本质就是「分区表 + 分区数据」的集合。你理解了分区表,就理解了固件包的灵魂。
4.2 分区表(Parameter)深度解析
分区表,在RK平台里通常叫parameter.txt或者parameter。它是一段纯文本,但格式非常严格。我刚开始做RK开发时,就因为少写了一个逗号,导致整个固件刷进去起不来。嗯,这种坑踩过一次就记住了。
来看一个典型的Parameter文件内容:
FIRMWARE_VER: 8.1
MACHINE_MODEL: RK3568_EVB
MACHINE_ID: 007
MANUFACTURER: Rockchip
MAGIC: 0x5041524B
ATAG: 0x00200800
MACHINE: 0xffffffff
CHECK_MASK: 0x80
PWR_HLD: 0,0,A,0,1
TYPE: GPT
CMDLINE: mtdparts=rk29xxnand:0x00002000@0x00004000(uboot),0x00002000@0x00006000(trust),0x00002000@0x00008000(misc),0x00020000@0x0000a000(boot),0x00020000@0x0002a000(recovery),0x00040000@0x0004a000(backup),0x00080000@0x0008a000(cache),0x00400000@0x0010a000(system),0x00008000@0x0050a000(metadata),0x00080000@0x00512000(vendor),0x00080000@0x00592000(oem),-@0x00612000(userdata)
这里最核心的就是CMDLINE这一行。它定义了每个分区的布局。我来拆解一下格式:
0x00002000@0x00004000(uboot)
- 0x00002000:分区大小,单位是扇区(512字节)。这里就是4MB。
- @0x00004000:分区起始地址,也是扇区单位。从8MB位置开始。
- (uboot):分区名称,烧录工具和系统都靠这个名字识别。
我的小技巧:在量产测试脚本里,我经常用rkdeveloptool read-partition命令来验证分区表是否被正确烧录。如果读出来的分区大小和parameter里对不上,那基本就是烧录出问题了。
4.3 各分区的作用详解
每个分区都有自己的使命。我按启动顺序给大家捋一遍:
4.3.1 Bootloader分区
这是芯片上电后第一个执行的代码。它负责初始化DDR、时钟、存储控制器等基础硬件。说白了,就是给后续的代码搭好舞台。
我曾经遇到过一个项目,换了DDR颗粒后频繁启动失败。查了两天才发现,是bootloader里的DDR初始化参数没更新。从那以后,我每次换物料都会先确认bootloader版本对不对。
4.3.2 Uboot分区
Uboot是真正的引导加载程序。它比bootloader复杂得多,支持文件系统、网络、USB等。它的核心工作是加载kernel到内存,然后跳转过去。
在量产测试中,uboot还有一个重要角色——它支持fastboot和rockusb协议。我们量产烧录工具就是通过uboot来和PC通信的。
4.3.3 Trust分区
这个分区存放ARM TrustZone的安全固件。如果你的产品不需要安全启动,这个分区可以很小甚至为空。但如果你要做支付类、版权保护类的产品,这个分区就至关重要了。
4.3.4 Misc分区
一个很小的分区,用来存放系统启动模式。比如正常启动、恢复模式、量产模式等。我经常在量产脚本里通过写misc分区来让设备进入烧录模式。
4.3.5 Boot分区
存放Linux内核(kernel)和设备树(dtb)。这是系统启动的核心。kernel负责驱动硬件、管理内存和进程。设备树告诉kernel硬件是怎么连接的。
4.3.6 System分区
这是Android系统的主要分区,存放/system目录下的所有文件。包括框架层代码、系统应用、库文件等。在Linux系统中,这个分区通常对应根文件系统。
这个分区是固件包里最大的,动辄几百MB甚至上GB。量产烧录时,大部分时间都花在写这个分区上。
4.3.7 其他常见分区
| 分区名 | 作用 | 量产测试关注点 |
|---|---|---|
| recovery | 恢复系统,用于OTA升级失败后的救砖 | 确认recovery能正常启动 |
| cache | 系统缓存,OTA升级包临时存放 | 大小要够用,否则OTA会失败 |
| userdata | 用户数据分区,相当于手机的内部存储 | 量产测试后要清空 |
| vendor | 厂商定制分区,存放硬件相关的库和配置 | 驱动相关的文件都在这里 |
避坑指南:我曾经在量产时发现,同一批固件烧到不同批次的主板上,有的能启动有的不能。最后定位到是parameter里分区大小定义和实际NAND Flash的坏块管理策略不匹配。记住:分区大小一定要考虑坏块预留空间,尤其是NAND Flash方案。
4.4 固件打包与解包实战
在量产测试中,我们经常需要解包固件来替换某个分区,或者重新打包。RK官方提供了rkImageMaker和afptool这两个工具。
解包一个.update固件:
# 解包update固件
afptool -unpack update.img output_dir
# 查看解包后的文件结构
ls output_dir/
# 你会看到类似这样的输出:
# Image.cfg parameter.txt uboot.img trust.img boot.img system.img ...
重新打包:
# 修改完分区镜像后重新打包
afptool -pack output_dir new_update.img
我个人习惯在量产测试脚本里加一个固件校验步骤:解包后检查每个分区镜像的MD5值,确保固件包没有被损坏。这个习惯帮我避免过好几次因为固件传输错误导致的批量刷机失败。
4.5 总结
固件文件结构,说白了就是一张「地图」加上一堆「货物」。地图是分区表,货物是各个分区的镜像数据。你只要掌握了这张地图,就能在固件世界里来去自如。
下一章我会讲量产烧录的具体流程和常见问题。到时候我会分享一个我踩过的坑——因为固件包里的bootloader版本和硬件不匹配,导致3000台设备全部变砖。嗯,那真是刻骨铭心的一次经历。