👁 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*