ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RK3128固件解包打包避坑指南:从No space left到成功刷机

RK3128固件解包打包避坑指南:从No space left到成功刷机 1. 为什么RK3128的固件折腾值得单独写一篇避坑指南瑞芯微RK3128这颗芯片放在今天看确实不算新了四核Cortex-A7、Mali-400MP2的GPU主频1.2GHz到1.3GHz左右当年大量出现在电视盒子、早教机、广告机、山寨投影仪、车载中控这类设备里。正因为出货量巨大、方案五花八门现在二手市场上还能淘到一堆基于它的设备很多人拿到手第一件事就是想改固件——换个桌面、精简预装、改开机画面、调分区大小甚至把原本的安卓系统换成更轻量的方案。但真正动过手的人都知道RK3128的固件解包打包是个典型的“入门容易、踩坑不断”的活。工具链看着简单afptool加img_maker两条命令似乎就能搞定可实际操作中你会遇到分区表对不上、镜像大小超限、校验失败、刷进去黑屏、卡在开机logo、No space left on device报错等等一系列问题。尤其是那个No space left几乎每个折腾RK3128的人都至少撞过一次它背后牵扯的是分区容量、文件系统开销、镜像对齐、打包参数这一整套东西不是简单删几个文件就能解决的。这篇内容就是把我自己在RK3128固件解包打包上踩过的坑、试过的方案、最后跑通的流程完整梳理一遍。适合手里有RK3128设备、想自己改固件的人也适合刚接触瑞芯微平台固件结构、想搞明白update.img到底是怎么拼出来的朋友。我会从固件结构讲起把解包、修改、打包、刷入整条链路拆开重点放在那些文档里不会写、但实际一定会遇到的问题上。看完你至少能明白为什么你的固件打包出来刷不进去为什么明明删了文件还是提示空间不足以及怎么一步步把这些问题定位出来。2. RK3128固件结构与工具链的整体设计思路2.1 update.img到底是个什么东西很多人拿到RK3128的固件看到的就是一个update.img文件几百兆到一两个G不等。直觉上会以为它就是一个普通的文件系统镜像直接挂载或者用解压软件打开就行。实际上不是瑞芯微这套固件的组织方式比较特殊update.img是一个容器格式里面按顺序塞了多个分区镜像外加一个描述整体布局的头部和分区表。具体来说一个典型的RK3128update.img大致包含这几层结构RKFW头部文件最开头的一段固定结构记录了固件版本、芯片型号、loader偏移、镜像总大小等信息。刷机工具第一眼读的就是它用来判断这个固件是不是给当前芯片的。LoaderMiniLoaderAll.bin引导加载程序负责初始化DDR、加载后续分区。RK3128的loader和芯片强绑定拿错型号的loader刷进去基本就是砖。分区镜像区按分区表顺序排列的各个分区镜像常见的有parameter、misc、kernel、boot、recovery、system、userdata、cache等。RKAF/RKFP尾部信息记录分区表、各分区偏移和大小、校验值。打包工具靠它把散装镜像拼成整体。理解这个结构非常关键因为后面所有的“空间不足”“校验失败”“刷入黑屏”根源基本都能追溯到这几层里的某一层对不上。2.2 为什么选afptool加img_maker这套组合瑞芯微官方和社区流传的解包打包工具不止一套有Windows下的图形工具也有Linux下的命令行工具。我最终稳定用的是afptool配合img_maker这套命令行组合原因有几个。第一是可控性。图形工具点几下确实方便但一旦出错你根本不知道它背后做了什么分区偏移、对齐方式、校验计算全是黑盒。命令行工具每一步都透明参数你能看到、能改出问题能定位。第二是跨平台和可脚本化。afptool在Linux下跑得很稳整个解包、替换、打包流程可以写成脚本改一次固件不用重复点鼠标。对于需要反复迭代调试的场景这个效率差距非常大。第三是对分区表的直接操作能力。RK3128改固件很多时候要动parameter文件里的分区大小图形工具往往不让你随便改命令行下你可以直接编辑分区表再重新打包。这套工具的核心逻辑其实很朴素afptool -unpack把update.img拆成一个个独立的分区镜像加一个parameter文件你改完内容后用afptool -pack按分区表重新组装最后img_maker给整体加上RKFW头部和校验生成可刷的update.img。2.3 分区表parameter文件才是全局的命门这里要单独强调parameter文件因为它是整个固件布局的“地图”。RK3128的parameter是一个纯文本文件每行描述一个分区格式大致是FIRMWARE_VER: 5.1.1 MACHINE_MODEL: RK3128 MACHINE_ID: 007 MANUFACTURER: RK3128 MAGIC: 0x5041524B ATAG: 0x00200800 MACHINE: 3128 CHECK_MASK: 0x80 PWR_HLD: 0,0,A,0,1 TYPE: GPT CMDLINE: mtdpartsrk29xxnand:0x000020000x00004000(uboot),0x000020000x00006000(trust),...最后那行CMDLINE里的mtdparts就是分区定义格式是大小起始偏移(分区名)全部用十六进制表示单位是扇区512字节。比如0x000020000x00004000(uboot)意思是从偏移0x4000扇区开始长度0x2000个扇区也就是4MB作为uboot分区。所有“No space left”问题的根源几乎都在这一行上。你往system分区里塞的东西超过了它定义的大小打包时就会报空间不足。而分区大小和起始偏移是环环相扣的改一个分区的长度后面所有分区的起始偏移都得跟着挪否则就会重叠或者出现空洞。这是RK3128固件修改里最容易翻车的地方后面我会专门用一节讲怎么算。3. 解包环节的核心细节与实操要点3.1 环境准备与工具获取我用的环境是Ubuntu 20.0464位。工具方面需要准备三个东西afptool、img_maker以及可选的rkflashtool用于刷机和读取设备分区。这些工具在瑞芯微的Linux SDK里都有社区也有单独编译好的版本。有一点要注意32位和64位的工具不能混用。我最早图省事用了一个32位的afptool解包小固件没问题遇到超过2GB的update.img就直接报错退出排查了半天才发现是工具本身的位数限制。所以务必确认你手里的工具是64位版本用file afptool看一眼架构。另外afptool和img_maker对文件路径比较敏感建议把工具和固件放在同一个工作目录下避免路径里有中文或空格。我吃过一次亏固件放在带空格的目录里afptool解析参数时把空格当成了分隔符直接报参数错误折腾了好久才反应过来。3.2 解包命令与输出结构解读解包命令本身很简单mkdir output afptool -unpack update.img output执行完之后output目录里会出现一堆文件。典型的输出包括parameter分区表文本文件MiniLoaderAll.binloaderuboot.img、trust.img、misc.img、kernel.img、boot.img、recovery.img、system.img、userdata.img、cache.img等分区镜像package-file打包时用的文件清单记录每个分区对应哪个镜像文件这里有个细节不是所有RK3128固件解出来都有完整分区。有些精简固件没有recovery有些没有cache有些把system和userdata合并了。这取决于厂商打包时的配置。你解包后第一件事应该是打开parameter和package-file对照着看搞清楚这个固件到底有哪些分区、每个分区多大、对应哪个文件。package-file的格式也值得说一下它长这样# NAME PATH package-file package-file parameter parameter uboot uboot.img trust trust.img misc misc.img kernel kernel.img boot boot.img recovery recovery.img system system.img左边是分区名右边是镜像文件名。打包工具就是按这个清单去找文件的。如果你替换了某个镜像但没更新package-file或者文件名对不上打包就会失败或者打出来的固件缺分区。3.3 system.img的挂载与修改system.img通常是最大的那个分区也是大多数人想改的地方。RK3128的system.img一般是ext4格式可以直接在Linux下挂载mkdir system_mount sudo mount -o loop system.img system_mount挂载之后就能像普通目录一样增删文件了。但这里有几个坑必须提前说。第一个坑是权限。挂载后所有文件属主会变成当前用户如果你直接复制文件进去权限位可能不对导致刷进去之后系统起不来。正确做法是用sudo操作并且复制时保留权限比如用cp -a或者rsync -a。第二个坑是SELinux上下文。安卓系统的system分区里很多文件带SELinux标签你在Linux下挂载修改后这些标签会丢失。如果固件本身开启了SELinux强制模式刷进去可能卡在开机阶段。稳妥的做法是修改完后用setfattr或者专门的工具恢复上下文或者干脆在打包前把SELinux设成宽容模式改cmdline里的androidboot.selinuxpermissive。第三个坑是空间。这是最要命的。system.img的大小是固定的你挂载后往里加东西加到超过镜像容量就会写不进去报No space left on device。注意这里报的No space left是文件系统层面的和后面打包时分区层面的No space left是两回事但很多人会混淆。文件系统层面满了你得先扩容system.img本身或者删掉一些不必要的东西腾地方。3.4 扩容system.img的正确姿势如果确实需要往system里塞更多东西就得扩容system.img。步骤是先看当前system.img多大比如是0x40000000字节1GB。用dd或者truncate把镜像文件扩大到目标大小比如1.5GB。用resize2fs把ext4文件系统扩展到新大小。挂载进去确认容量变了。命令大致是truncate -s 1536M system.img e2fsck -f system.img resize2fs system.img这里有个关键点扩容后的system.img大小必须和parameter里system分区的定义匹配。你镜像扩到1.5GB但分区表里system还是1GB打包时要么报空间不足要么打出来的固件分区和镜像对不上。所以扩容镜像和改分区表必须同步做这是RK3128固件修改里最需要小心的一环。4. 打包环节从No space left到成功生成update.img4.1 分区表parameter的重新计算假设我要把system分区从1GB扩到1.5GB那parameter里的CMDLINE就得改。原始可能是CMDLINE: mtdpartsrk29xxnand:0x000020000x00004000(uboot),0x000020000x00006000(trust),0x000020000x00008000(misc),0x000080000x0000A000(kernel),0x000080000x00012000(boot),0x000100000x0001A000(recovery),0x002000000x0002A000(system),0x000100000x0022A000(cache),0x000100000x0023A000(userdata),-0x0024A000(user)system是0x00200000个扇区也就是0x00200000 * 512 0x40000000字节 1GB。要扩到1.5GB就是0x00300000个扇区。改完之后system后面所有分区的起始偏移都要加上0x00100000因为system多了0x100000个扇区。改完的CMDLINE应该是CMDLINE: mtdpartsrk29xxnand:0x000020000x00004000(uboot),0x000020000x00006000(trust),0x000020000x00008000(misc),0x000080000x0000A000(kernel),0x000080000x00012000(boot),0x000100000x0001A000(recovery),0x003000000x0002A000(system),0x000100000x0032A000(cache),0x000100000x0033A000(userdata),-0x0034A000(user)这里必须逐个核对偏移一个算错后面全乱。我建议用个小脚本或者表格来算别心算。下面这个表是我实际改的时候用的对照分区原大小(扇区)原偏移新大小(扇区)新偏移uboot0x20000x40000x20000x4000trust0x20000x60000x20000x6000misc0x20000x80000x20000x8000kernel0x80000xA0000x80000xA000boot0x80000x120000x80000x12000recovery0x100000x1A0000x100000x1A000system0x2000000x2A0000x3000000x2A000cache0x100000x22A0000x100000x32A000userdata0x100000x23A0000x100000x33A000user剩余0x24A000剩余0x34A000注意最后user分区用的是-表示“剩余全部空间”它的起始偏移跟着前面走就行不用算大小。4.2 打包命令与No space left的触发点分区表改好、镜像准备好之后打包分两步。第一步用afptool把散装镜像按分区表组装afptool -pack ./ output/update_new.img这里的./是当前目录output是存放parameter、package-file和各分区镜像的目录。afptool会读package-file按顺序把镜像拼起来同时生成RKAF尾部信息。No space left最常在这一步爆出来。报错信息通常是类似Error: image system.img size(0x40000000) is larger than partition size(0x30000000)或者反过来镜像比分区小太多导致对齐问题。这个报错的本质是你提供的镜像文件实际大小和parameter里定义的分区大小不匹配。可能是你扩容了镜像但忘了改分区表也可能是改了分区表但镜像没跟着扩还可能是镜像文件本身有额外的头部或尾部数据导致实际占用比预期大。我遇到过一次特别隐蔽的情况system.img用resize2fs扩容后文件大小是1.5GB没错但afptool读到的“有效数据大小”还是旧的1GB因为它读的是ext4超级块里的信息而不是文件实际大小。解决办法是先e2fsck -f修复一遍确保超级块更新再打包。4.3 img_maker加头部与校验afptool -pack出来的update_new.img还没有RKFW头部不能直接刷。需要用img_maker加上头部和校验img_maker -rk3128 -b loader.bin -kernel kernel.img -boot boot.img -system system.img -recovery recovery.img -misc misc.img -parameter parameter -o update_final.img不过实际用的时候更常见的是用img_maker配合一个配置文件或者直接用afptool的-pack加img_maker的组合脚本。不同版本的img_maker参数略有差异核心是告诉它芯片型号-rk3128、loader路径、各分区镜像路径以及输出文件名。这一步的坑在于loader必须匹配。RK3128的loader有多个版本拿RK3128A的loader去刷RK3128可能能启动但功能异常拿别的芯片的loader直接就是砖。所以改固件时loader尽量用原固件解出来的那个不要随便换。4.4 校验与刷入前的自检打包完成后别急着刷。先做几项自检用afptool -unpack把新生成的update.img再解一遍确认能正常解开、分区表和你改的一致、各分区镜像大小对得上。检查update.img总大小确认没有超过设备存储容量。如果有条件用rkflashtool先读一下设备当前分区表和你的新分区表对比确认兼容。刷入可以用瑞芯微的刷机工具Linux下用rkflashtoolWindows下用对应的升级工具。刷的时候注意进入maskrom模式或者loader模式具体按键组合因设备而异一般是按住复位键再上电。5. 常见问题与排查技巧实录5.1 No space left的三种面孔与对应解法No space left这个报错在RK3128固件折腾里出现的频率极高但它其实对应至少三种不同场景解法完全不同。我整理了一个速查表报错场景典型信息根本原因解法挂载修改时No space left on devicesystem.img文件系统容量满扩容img或删除文件afptool打包时image size larger than partition镜像大小超过分区定义改parameter分区大小或缩小镜像刷入时刷机工具报空间不足固件总大小超过设备存储精简固件或调整分区布局第一种是文件系统层面的你在挂载的目录里cp文件时报错说明ext4的块用完了。这时候df -h看挂载点如果显示100%就是这个问题。解法要么删东西要么扩容。第二种是打包层面的afptool在拼镜像时发现某个镜像比parameter里定义的分区大。这时候要对照parameter和实际镜像大小把分区改大或者把镜像改小。第三种是设备层面的固件本身没问题但设备存储太小装不下。RK3128设备常见存储是4GB或8GB如果你把system扩得太大加上userdata、cache总大小可能超过实际NAND容量。这时候要么换更大存储的设备要么精简其他分区。5.2 刷入后黑屏或卡logo的排查思路固件打包成功、刷入也成功但开机黑屏或者卡在logo这是第二大类问题。排查顺序建议是先确认loader对不对。如果loader错了设备可能连启动阶段都进不去表现就是完全黑屏、无任何输出。这时候只能重新刷回原固件或者用maskrom模式救。再确认kernel和boot。如果loader没问题但kernel不匹配可能卡在loader阶段或者kernel启动早期。可以接串口看输出RK3128的调试串口通常是UART2波特率1500000。串口输出能告诉你卡在哪一步。然后确认system。如果kernel起来了但system挂载失败会卡在安卓开机动画。常见原因是system.img的文件系统损坏、SELinux上下文丢失、或者权限不对。用e2fsck检查镜像确认挂载修改时用了正确的权限保留方式。最后确认分区表。如果分区表里某个分区的偏移和实际镜像对不上系统可能读到错误的数据。用afptool -unpack重新解一遍你刷进去的固件逐项核对。5.3 实操心得与避坑清单折腾RK3128固件这些年我总结了几条血泪经验列出来供参考永远保留一份原厂固件。改坏了能刷回去这是底线。我习惯把原厂update.img和解包后的所有文件都备份两份一份放本地一份放移动硬盘。改分区表前先画图。把所有分区的偏移和大小列成表改一个就更新整张表别凭记忆。loader不要乱换。除非你明确知道新loader兼容你的设备否则用原厂的。扩容镜像后一定跑e2fsck。不然超级块信息可能不更新打包时读到的还是旧大小。打包后先解包验证。这一步花不了几分钟但能避免刷坏设备后重新拆机进maskrom的麻烦。串口是救命稻草。花十几块钱买个USB转TTL接上调试串口能看到启动全过程排查效率提升十倍。注意镜像对齐。有些工具要求镜像大小按扇区对齐不对齐可能导致打包出来的固件有隐藏问题。afptool一般会处理但自己心里要有数。5.4 关于RK3128之外的一些延伸虽然这篇主要讲RK3128但这套解包打包的思路在瑞芯微其他芯片上是相通的。比如RK3368、RK3288、RK3568这些固件结构大同小异都是RKFW头部加分区镜像加RKAF尾部工具链也类似。区别主要在loader、分区布局、以及一些芯片特有的配置。如果你把RK3128这套流程跑通了换到RK3568上基本就是换个loader、调一下分区表的事。设备树那块RK3568会更复杂一些但解包打包的骨架是一样的。另外现在有些新设备用的是瑞芯微的RV1106这类芯片固件格式又有变化但核心逻辑还是“容器加分区镜像”。理解了RK3128这套再看新的也就有了底子。最后分享一个我常用的小技巧把整个解包、修改、打包流程写成一个shell脚本每次改固件只需要改几个变量然后跑脚本。这样既避免了手敲命令出错也方便记录每次改了什么。脚本里加上set -e任何一步失败就停不会带着错误往下跑。这个习惯帮我省了无数时间也避免了好几次差点刷坏设备的事故。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进