👁 711
斐讯 R1智能音箱eMMC扩容:分区分析与ROM制作
手里有个R1智能音箱,SoC是Rockchip RK3228A(Cortex-A7四核),原装eMMC只有2GB,装完系统剩余空间少得可怜。手头有16GB eMMC芯片和T76编程器,决定动手扩容。
这篇文章先把分区结构和ROM制作过程记下来,下一篇再写写入和验证。
先搞清楚分区表
RK3228A不用GPT也不用MBR,分区信息写在uboot的CMDLINE里,格式是mtdparts。用编程器读出原始镜像后,strings一下就看到了:
mtdparts=rk29xxnand:
0x00002000@0x00002000(uboot),
0x00004000@0x00004000(trust),
0x00002000@0x00008000(misc),
0x00000800@0x0000A000(baseparamer),
0x00007800@0x0000A800(resource),
0x00006000@0x00012000(kernel),
0x00006000@0x00018000(boot),
0x00010000@0x0001E000(recovery),
0x00020000@0x0002E000(backup),
0x00040000@0x0004E000(cache),
0x001FE000@0x0008E000(userdata),
0x00008000@0x0028C000(metadata),
0x00002000@0x00296000(kpanic),
0x00180000@0x00298000(system),
0x00040000@0x00418000(private),
-@0x00458000(user)
格式是大小@偏移(分区名),单位是扇区(512字节)。-表示占满剩余空间。
整理成表更直观:
| 分区 | 偏移 | 大小 | 说明 |
|---|---|---|---|
| uboot | 4MB | 4MB | 引导加载器,分区表就藏在这里 |
| trust | 8MB | 8MB | TrustZone |
| misc | 16MB | 4MB | Android misc |
| baseparamer | 20MB | 1MB | 基础参数 |
| resource | 21MB | 15MB | 资源 |
| kernel | 36MB | 12MB | 内核 |
| boot | 48MB | 12MB | Android boot镜像 |
| recovery | 60MB | 32MB | 恢复分区 |
| backup | 92MB | 64MB | 备份 |
| cache | 156MB | 128MB | 缓存 |
| userdata | 284MB | 1020MB | 用户数据——只有1GB! |
| metadata | 1304MB | 16MB | 元数据 |
| kpanic | 1324MB | 4MB | 内核崩溃日志 |
| system | 1328MB | 768MB | 系统分区 |
| private | 2096MB | 128MB | 私有数据 |
| user | 2224MB | 剩余 | 用户分区 |
问题很明显:userdata只有1GB,夹在cache和metadata中间,想扩大也扩不了。 后面还排着metadata、kpanic、system、private、user,把空间全占了。
扩容思路:把userdata挪到尾巴上
2GB eMMC上userdata只有1GB,换成16GB后如果只是简单写入,mtdparts告诉内核userdata还是1GB,多出来的14GB全给了最后的user分区——没有ext4格式,Android用不上。
所以必须重排分区:
1. 前面uboot到cache不变(0~284MB)
2. system紧跟cache(原来在1304MB之后,现在提到284MB)
3. metadata、kpanic、private紧凑排列
4. userdata移到最后,用-占满剩余空间
新分区表:
mtdparts=rk29xxnand:
0x00002000@0x00002000(uboot),
0x00004000@0x00004000(trust),
0x00002000@0x00008000(misc),
0x00000800@0x0000A000(baseparamer),
0x00007800@0x0000A800(resource),
0x00006000@0x00012000(kernel),
0x00006000@0x00018000(boot),
0x00010000@0x0001E000(recovery),
0x00020000@0x0002E000(backup),
0x00040000@0x0004E000(cache),
0x00180000@0x0008E000(system), ← system提到cache后面
0x00008000@0x00218000(metadata),
0x00002000@0x00220000(kpanic),
0x00040000@0x00222000(private),
-@0x00262000(userdata) ← userdata挪到末尾,吃满剩余空间
关键变化:
– 原来的user分区删掉了(Android实际不用)
– userdata从1GB → 13.37GB,扩了13.4倍
– 所有分区的偏移地址重新计算,保证不重叠
编程器备份原始eMMC
用T76编程器读取原始2GB eMMC。R1的eMMC是BGA153封装,编程器座子选AUTO-EMMC_8B_1.8V。
读取信息确认芯片:
MID: 15 (Samsung)
PNM: 8GTF4R
容量: 7456 MB (0x01D2000000)
寿命: 0%-10% (几乎全新)
HS400 支持: YES
完整备份耗时约3分钟,生成r1_emmc_20260605_030938.img(15.6GB,因为T76会读到eMMC声明容量)。SHA256校验保存备用。
制作16GB新ROM
核心步骤用Python脚本build_r1_16g_reordered.py完成:
1. 创建目标镜像
TARGET_BYTES = 15636365312 # 16GB eMMC实际容量
with open(OUT, 'wb') as f:
f.truncate(TARGET_BYTES)
2. 复制前284MB不动
uboot到cache这10个分区偏移不变,直接从原始镜像拷贝:
copy_region(SRC, OUT, 0, 0x8E000) # 0~284MB
3. 搬移system、metadata、kpanic、private
这4个分区从旧位置搬到新位置:
for name in ['system', 'metadata', 'kpanic', 'private']:
old_off, size = old_parts[name]
new_off, _ = new_parts[name]
copy_region(SRC, OUT, old_off, size, new_off)
| 分区 | 旧偏移 | 新偏移 | 大小 |
|---|---|---|---|
| system | 1328MB | 284MB | 768MB |
| metadata | 1304MB | 1072MB | 16MB |
| kpanic | 1324MB | 1088MB | 4MB |
| private | 2096MB | 1092MB | 128MB |
注意system的旧偏移(0x298000)比metadata(0x28C000)还靠后,但数据搬移互不冲突——因为是逐分区拷贝,不是整块移动。
4. 拷贝userdata到新位置并扩容
先把原始userdata(1GB ext4)复制到新偏移0x262000,然后提取出来用resize2fs扩展:
# 提取原始userdata
copy_region(SRC, OUT, 0x8E000, 0x1FE000, 0x262000)
# 提取到临时文件,resize2fs扩展
ud_start = 0x262000 * 512
ud_size = TARGET_BYTES - ud_start # 13.37GB
# truncate + e2fsck + resize2fs + e2fsck
# 写回
这一步把1GB的ext4文件系统在线扩展到13.37GB,resize2fs会自动调整superblock和block bitmap。
5. 替换uboot中的mtdparts字符串
这是最关键的一步。uboot分区在4MB~8MB之间,里面有8处mtdparts字符串(uboot主镜像+备份),必须全部替换:
with open(OUT, 'r+b') as f:
f.seek(4 * MB)
data = bytearray(f.read(4 * MB))
old_count = data.count(OLD) # 确认8处
data = data.replace(OLD, NEW + b'x00' * (len(OLD) - len(NEW)))
f.seek(4 * MB)
f.write(data)
新字符串比旧字符串短(删掉了user分区),多出来的空间用x00填充,保证不破坏后续数据。
6. 验证
# boot分区magic: "ANDROID!"
# system分区ext4 magic: "53ef"
# userdata分区ext4 magic: "53ef"
# mtdparts替换: old_count=0, new_count=8
验证结果JSON:
{
"old_count": 0,
"new_count": 8,
"boot_magic": "ANDROID!",
"system_magic": "53ef",
"userdata_magic": "53ef",
"userdata_size_bytes": 14357102592,
"sha256": "60d69199645dfaacb4c8d3d6e20854df70aac12d6c8c35391b70865a3947ef3b"
}
全部通过。
踩坑记录
1. mtdparts不是GPT
刚开始我下意识以为是GPT分区表,差点用fdisk去改。RK3228A的分区定义完全在uboot CMDLINE里,内核启动时解析mtdparts字符串来识别分区。改分区就是改字符串,没有分区表工具可以帮你。
2. 分区名必须保留
userdata和user是两个不同的分区。Android的/data挂载的是userdata,不是user。我把user干掉了,把userdata挪到末尾,这样Android才能正确挂载扩容后的空间。
3. 字符串替换要补零
新mtdparts比旧mtdparts短,直接替换会导致后面残留旧字符。必须用x00填充到原长度,否则uboot解析可能出问题。
4. resize2fs需要先e2fsck
ext4扩容前必须先跑e2fsck -f -y修复可能的不一致,否则resize2fs会拒绝执行。扩容完再跑一次确认文件系统健康。
下一篇
ROM制作完成,下一步是把镜像用T76编程器写入16GB eMMC,插回R1开机验证。包括写入校验、启动测试、以及Android是否正确识别13.37GB的userdata。
*by 数码罗记·godsun.pro*
💖 打赏支持
如果这篇文章帮到了你,欢迎打赏支持
微信扫一扫,赞赏支持
发表回复