简介:面向联发科MT6761平台的新分区定制需求,聚焦Android 9.0及以上系统中FDRAM分区的添加与根目录挂载,适合从事BSP开发、系统存储管理或ROM定制的工程师。FDRAM(Fast Data RAM)多用于缓存及临时数据存储,在升级A/B分区机制后,传统分区方法已不适用,本套资料正好给出完整适配思路。压缩包含21个文件,体积仅133KB,以mk构建脚本、te安全策略、csv布局配置和python辅助工具为主,另有makefile、file_contexts、rc及c源码,覆盖partition.map修改、partition.bin烧录、文件系统格式化、fstab挂载及vold服务适配的全流程,各类型文件分工明确,便于对照学习。目录按源码路径组织,附带diff补丁与关键配置示例,可直接对比默认工程移植使用,同时针对SELinux权限和A/B双分区更新提供排错要点。已有682人学习,借助这套精简资料,能显著降低MTK平台分区定制的门槛,提升存储布局调整与调试效率。
1. 添加 FDRAM 分区挂载根目录:一次 MTK 平台的动刀实录
搞 MTK Android 定制的开发者,迟早会撞上「系统分区不够用,想加一块新分区」这个需求。FDRAM 这种自定义分区,听起来就是改个分区表、加一行挂载的事,真动起手来却能把人折腾到怀疑人生:不是 scatter 文件格式写错导致刷机工具不认,就是分区加上了但内核 dts 没配,开机直接卡在 boot 动画。更隐蔽的是那种「分区挂上了,但 data 分区没挂载成功」的玄学问题,让你在 log 里扒半天才发现是上下文的标记对不上。这篇文章把我完整操作一遍「新增 FDRAM 分区并挂载到根目录」的过程、参数、报错处理都记了下来,全程基于 MTK Android 11 平台。无论你是要扩大系统空间,还是要把日志、缓存挪到独立分区,这套流程都能直接抄作业。新手建议先完整读一遍再动手,老手可以直接跳到分区表配置那章对比细节。
2. 动刀前的环境检查:工具链、固件包与分区规划
2.1 确认平台版本与当前分区布局
MTK 平台的分区操作对版本非常敏感。Android 9 和 Android 11 的 boot 流程、分区表格式、fstab 解析方式都有差异,动手前必须先确认当前系统到底跑在哪套机制上。我习惯先看三处:系统版本、内核版本、当前分区挂载情况。
# 在已经 root 的设备上执行,或者在 uboot 阶段通过串口进入命令行 adb shell getprop ro.build.version.release adb shell getprop ro.build.version.security_patch adb shell cat /proc/version adb shell cat /proc/partitions adb shell cat /proc/mounts | grep -E "system|vendor|data"第一条getprop拿到的 Android 大版本决定你改 fstab 的语法风格,Android 10 之后强制使用fs_mgr标志位,而 Android 8/9 还兼容旧的vold写法。/proc/partitions让你看清楚当前已经存在哪些块设备,/proc/mounts则是确认 system、vendor、data 各自的挂载点和文件系统格式。
这一步的目标不是记住每个参数,而是要确认三件事:平台跑的是不是 Android 11(决定后面 fstab 的写法)、当前有没有空余的 partition table 槽位可以塞进 FDRAM、以及 boot 分区是 A/B 还是 A-only 结构。A/B 结构的动态分区机制和传统静态分区在新增加分区的处理方式上完全不同,前者还要连带处理 super 分区里的 dynamic partition 列表。
2.2 准备主机环境:Linux 编译机与常用工具
MTK 的固件打包和分区表生成工具只在 Linux 下跑得顺畅,Windows 下虽然也能用 FlashTool 刷机,但修改 partition_table 和重打包 image 必须在 Linux 环境完成。我一般用 Ubuntu 20.04 LTS,内存 32G 以上,磁盘至少留 200G,因为 MTK 的 out 目录随便一编就是几十上百 G。
# 编译机需要的基础工具包 sudo apt update sudo apt install -y git python3 python3-pip openjdk-8-jdk \ device-tree-compiler mtd-utils bc lzop zip unzip \ gcc g++ make libncurses5-dev libssl-dev # MTK 编译需要的 python 库 pip3 install --user rsa pycryptodome编译阶段用到的mkimage、img2simg、simg2img这些工具在 Android build 系统里是自带编译出来的,不需要单独安装。真正需要提前备好的反而是两个容易被忽略的命令:dtc(设备树编译器)用来验证你改过的 dts 是否正确,mtd-utils里的flash_erase和nandwrite用来在板子上直接操作裸 flash 分区。
主机环境就绪后,接下来要确认拿到手的固件是「完整包」还是「单分区包」。完整包里的scatter.txt是整个分区布局的源头,所有后续的分区新增操作都要从这张表开始改起。
2.3 备份:改分区前必须做的一件事
改分区表是高风险操作,分区表刷坏轻则系统起不来,重则底层 bootloader 分区都被覆盖。我见过有人图省事跳过备份,结果 scatter 文件里少写了一行逻辑,整块 emmc 变成砖,短接复位都得拆机。备份只需要三个动作:备份当前完整 scatter 分区用镜像、备份环境变量区、备份 boot 和 bootloader。
# 备份完整分区表与关键分区(在 root 的设备上) adb shell su -c "dd if=/dev/block/mmcblk0 of=/sdcard/partition_table_backup.img bs=1M count=8" adb pull /sdcard/partition_table_backup.img ./backup/ # 保存当前 scatter.txt 和分区参数 cp scatter.txt scatter.txt.orig cp partition_table_MT6785.pit partition_table_MT6785.pit.orig 2>/dev/null || true备份出来的partition_table_backup.img是前 8MB 的完整镜像,里面包含了 GPT 头和所有分区表的 entry,平时用不到,但真到了需要救援的时候,它就是最后一根救命稻草。scatter.txt.orig和.pit.orig是让你改坏了随时能 diff 回滚的参照物。
注意:备份和设备 side 的操作必须在设备完全 root 的前提下进行。如果你的设备还没解锁 bootloader,先把解锁流程走完,否则后面所有写分区的操作都会被权限挡住。
3. 修改分区表:scatter 文件里新增 FDRAM 条目
3.1 MTK 分区表机制:scatter、partition_table 与 GPT 的关系
MTK 的分区布局逻辑和高通不太一样,它维护了一张名为scatter.txt的文本清单,另外在 preloader 阶段还有一张二进制格式的partition_table(通常是partition_table_XXXX.pit)。真正刷机时,FlashTool 会先读 scatter 文件,再依据里面的分区参数把各个镜像写到对应 LBA 地址。所以新增分区要改的地方不是某一个文件,而是「scatter.txt + partition_table + 内核 dts + fstab」这一整套。
现阶段 MTK Android 11 的分区布局主要由三类构成:boot 相关分区(preloader、boot、vendor_boot、dtbo)、文件系统分区(system、vendor、odm、userdata)、以及各类缓存和元数据分区。FDRAM 这种自定义分区,通常放在 vendor 后面、userdata 前面的空白区域,这样既不会影响 boot 流程里的镜像加载地址,也不会挤压用户数据区的连续空间。
来看一张典型的 scatter.txt 片段:
# 分区名 起始地址(hex) 分区大小(hex) 分区类型 preloader 0x0 0x200000 SV5 boot 0x200000 0x1000000 NORMAL vendor 0x5000000 0x8000000 NORMAL userdata 0x18000000 0x80000000 NORMAL在vendor和userdata之间插入 FDRAM 条目后:
vendor 0x5000000 0x8000000 NORMAL fdyram 0x10000000 0x2000000 NORMAL userdata 0x18000000 0x80000000 NORMAL看到区别没有?新分区的起始地址必须接在 vendor 分区结束的地址上,不能凭空留出空洞。空洞区域 flash 工具会当作未定义区域处理,刷机后这块空间无法被正确挂载,甚至会导致后面的 userdata 地址错位。
3.2 计算新分区的大小与 LBA 对齐
MTK 平台的 flash 是 emmc,逻辑区块大小为 512 字节,但分区表做地址对齐时通常按 1MB 对齐。你给 FDRAM 规划大小的时候,先想清楚用途:如果只是放日志和缓存,512M 到 1G 足够了,如果想把整个/data的一部分业务挪过去,那至少得 4G 起步。
我一般按这个思路来计算地址:
# 确认 vendor 分区的结束地址(scatter 里 vendor 的起始地址 + 大小) # 假设 vendor 起始 0x5000000(83,886,080),大小 0x8000000(134,217,728) # 那么 vendor 结束地址 = 0xD000000(218,103,808) # 新分区起始地址 = 218,103,808,向上取整到 1MB 对齐 # 218,103,808 / 1048576 = 208,正好是整数,不需要调整 # FDRAM 分配 512MB = 0x2000000 # 起始 0xD000000,结束 0xF000000 python3 -c " start = 0xD000000 size = 0x2000000 end = start + size print('start=0x%X end=0x%X size=0x%X' % (start, end, size)) # 检查 userdata 起始地址是否大于等于 end userdata_start = 0x18000000 print('gap:', hex(userdata_start - end)) "对齐检查很关键。如果 vendor 的结束地址不是 1MB 的整数倍,你计算新分区起始地址时就往上取整到下一个 1MB 边界,空出来的那一小块只能白白浪费掉,这是 MTK 平台的硬性约束,不接受也不行。分区大小也尽量用 1MB 的整数倍,否则 ext4 格式化时会出现Geometry告警,虽然不影响挂载,但看着碍眼,后续 fsck 也可能报错。
3.3 同步修改 partition_table 和 download agent 参数
光改 scatter.txt 不够,MTK FlashTool 在下载模式会同时校验 partition_table 里的 LBA 布局。如果你只改了 scatter,不更新 partition_table,刷机到了一个固定阶段会报错,提示partition size mismatch或者直接卡在Download DA阶段。
MTK 的 partition_table 文件是二进制,一般通过MTK_Partition_Table_Editor这类内部工具来改。没有这个工具的情况下,可以换一条路:用 scatter 文件里的布局反过来生成一个新的 partition_table。具体做法是先把原 pit 用工具 dump 成文本,再修改后重新打包。不同平台对应的编辑器版本不同,拿不到编辑器的就只能把整个分区表用代码方式解析修改,常见做法是参考 MTK 开源的genpartition工具。
# 用 Python 解析并修改 partition_table 的示意逻辑 import struct # 假设读取了 pit 文件到内存 with open('partition_table_MT6785.pit', 'rb') as f: data = bytearray(f.read()) # 在分区条目区域找到 vendor 条目的偏移,插入 fdyram 条目 # 每个分区条目固定 256 字节,修改条目数和总大小 # 这一块依赖具体平台的二进制布局,无法直接给出统一函数这种二进制修改方式的坑在于结构体字段的偏移量在平台间有差异,需要对着具体平台的文档来确认。我一般不建议随笔改 pit,更稳妥的方法是直接用 FlashTool 的 download agent 重新生成分区表,让工具自己根据 scatter 重建布局。
4. 打通内核与挂载层:dts、fstab 与 init 脚本的配合
4.1 内核 dts:让系统知道 FDRAM 这个块设备
分区表改完只是第一步,内核里也要注册这个分区对应的 platform device,系统启动时才能看到/dev/block/mmcblk0pXX这样的设备节点。MTK 平台的 dts 文件通常放在arch/arm64/boot/dts/mediatek/目录下,不同项目对应不同板级 dts 文件。
打开项目对应的 dts(比如k6785v1_64.dts),在partition相关节点下新增分区信息。在老版本 MTK 平台里,这个节点叫partition_meta,新平台则用mtd或者mmc节点下的partitions子节点。
&mmc0 { /* 原有分区 */ partition@D000000 { label = "fdyram"; size = <0x2000000>; /* 512MB */ reg = <0x0 0xD000000 0x0 0x2000000>; }; };partition@D000000里的地址必须和 scatter.txt 里新分区的起始地址一致,size更是要逐一对应,两个文件只要有一处数字对不上,内核生成的 mmc block 设备节点序号就和实际分区布局错位,挂载时很容易出现「想要的 FDRAM 没出现,反而看到一堆乱序分区名」的诡异现象。
改完 dts 后,用dtc做一次语法校验:
dtc -I dts -O dtb -o test.dtb k6785v1_64.dts如果 dts 里引用了未定义节点或括号没闭合,dtc 会直接报错,这可以作为第一道防线。校验通过后再把 dts 编入完整 dtbo 镜像,替换原固件里的 dtbo。
4.2 fstab 配置:开机挂载 FDRAM 到根目录的指定目录
系统要自动挂载 FDRAM,核心工作在 fstab。MTK Android 11 的 fstab 分为fstab.mt6785(挂在根目录的)和 vendor 分区里的fstab,具体由 boot 阶段哪个 fstab 文件被 init 读取来决定。Android 11 推荐的做法是在system/etc或者vendor/etc下配置,同时挂载逻辑要写进第一阶段的 ramdisk 或 vendor_boot。
在 fstab 里新增一行挂载项:
/dev/block/by-name/fdyram /mnt/fdyram ext4 defaults wait,check解释一下字段:第一段是分区的 by-name 路径,比直接写/dev/block/mmcblk0p23要稳健,因为分区序号可能在改动后变化,by-name 是符号链接,指向不会乱;第二段是把分区挂到根文件系统的/mnt/fdyram下;第三段指定文件系统类型为 ext4,改成 f2fs 也行,但 f2fs 在 MTK 平台的老内核上可能会有兼容问题,除非你明确要规避 flash 写寿命问题,否则我推荐 ext4;第四段是挂载选项,defaults等同于rw,suid,dev,exec,auto,nouser,async,实战里按需裁剪;最后一段wait,check让 fs_mgr 在挂载前检查分区并等待设备节点就绪。
如果你挂载的目标不是独立的挂载点,而是想直接融合到根目录下某个现有路径(比如把 FDRAM 作为/data下某个子目录的存储),那要改的就是挂载点路径,同时还需要把这行配置放进init.rc或者在 fstab 里配合mount_all的执行顺序。这里有个注意点:fstab 里的挂载点目录必须在 init 早期就被创建好,否则 fs_mgr 会因找不到目录而静默跳过错。
4.3 init.rc 与权限标记:开机创建目录并设定上下文
fstab 挂载真正落地前,挂载点目录必须存在。如果你在 fstab 里写了/mnt/fdyram,但根文件系统里根本没有这个目录,开机时 fs_mgr 挂载失败并不会报明显错误,只在 logcat 里留一行mount point does not exist,你回头排查时很容易漏掉。
我通常会在init.rc的 early mount 阶段创建目录并设置权限。修改device/mediatek/<project>/root/init.rc,在on early-init或on init阶段加:
on init mkdir /mnt/fdyram 0755 root root chmod 0755 /mnt/fdyramSELinux 是 Android 的大坑,光有目录和权限还不够,fdyram分区挂载后会被标记成u:object_r:unlabeled或者某类默认 label,导致后续读写被 SELinux 拒绝。解决方式是在file_contexts里给它定义上下文:
/mnt/fdyram(/.*)? u:object_r:fdyram_file:s0定义完成后在sepolicy里把 fdyram_file 类型和 system_app、shell 的读写权限打通。如果你只是自己调试用,可以在adb shell里临时用setenforce 0扛过去,但正式发布固件时这一步不能省,否则站在用户角度就是「分区挂上了但写入失败」的奇葩 bug。
4.4 重新编译并确认镜像产物
所有配置改完之后,进入编译流程。MTK 的编译一般通过source build/envsetup.sh和lunch选择项目后,用模块编译命令逐一输出 dtbo、boot 和 system 镜像。
source build/envsetup.sh lunch <project>-userdebug # 先编译设备树和内核镜像,确认 dts 修改生效 make dtboimage -j16 make bootimage -j16 make vendorbootimage -j16 # 编译 vendor 和 system 分区镜像,让 fstab 和 init.rc 改动打进去 make vendorimage -j16 make systemimage -j16编译过程里最容易翻车的是 dts 编译报错,直接提示某个 node 命名冲突或者 size 越界。遇到这种情况,回到arch/arm64/boot/dts/mediatek/检查partition@的地址范围,看看是不是和已有分区重叠了。另一个常见坑是make vendorbootimage编出来的 ramdisk 里没带上新的 fstab,这通常是因为 fstab 文件放错目录,vendor_boot 解包后看不到这个文件。检查产物可以用unpack_bootimg工具解包 vendorboot,直接查阅 ramdisk 里的 fstab 内容是否包含 fdyram 那一行。
5. 常见问题排查:新增分区挂载失败的 5 个典型案例
5.1 现象一:FlashTool 刷机时报partition mismatch
现象:使用新的 scatter 文件刷机,FlashTool 在格式化或者下载 DA 阶段直接报错,提示partition mismatch或error: partition size error。
原因:分区的地址或大小只改了 scatter,没同步到 partition_table 二进制文件。FlashTool 在下载阶段会交叉比对两个文件里的 LBA 信息,任何不一致都会直接中断,这是 MTK 特有的保护机制。
解决:用 MTK 分区表编辑器打开你改过的 scatter,重新生成配套的 pit 文件;或者用MTK_FlashTool的partition manager重新 make emmc 分区表。改完后再校验一遍scatter和pit里 fdyram 的起始地址和大小,保持一致。
5.2 现象二:分区节点没出现,/dev/block下找不到 fdyram
现象:开机后adb shell ls /dev/block/by-name/里没有 fdyram 的符号链接,甚至cat /proc/partitions里也没有对应大小的块设备。
原因:内核 dts 里的 partition 节点没有生效。可能是因为 dts 里用的地址和 scatter 不一致,或者这个 dts 文件编译后没有真正被打进 dtbo 镜像。
解决:先确认 dtbo 镜像里实际包含的是不是改过的 dts,用cat /proc/device-tree/查看设备树里 mmc0 节点下的 partition 内容。如果设备树里已经有 fdyram 节点但没有块设备生成,多半是内核代码里mmc_add_partition的逻辑没走到,这种就回到内核 config 检查CONFIG_MTK_MMC是否把新分区排除在外了。
5.3 现象三:挂载后权限被 SELinux 拒绝
现象:分区挂载成功,/mnt/fdyram也能看到,但adb shell或者 app 往里写文件时提示Operation not permitted,即使 shell 已经是 root 也不行。
原因:SELinux 的fdyram_file类型没有给相应 domain 授予写入 permission,或者file_contexts没有正确标记这个分区的 label。Android 8 之后 SELinux 强制模式开启,任何未授权类型都写不进去。
解决:在sepolicy目录下的对应te文件里,为system_app和shell增加allow规则,例如allow shell fdyram_file:dir create_dir_perms;,然后再加allow shell fdyram_file:file create_file_perms;。重新编译vendor镜像并刷机验证。
5.4 现象四:开机卡在 boot logo 或反复重启
现象:刷入新固件后设备卡在开机 logo,logcat 看不到完整启动日志,或者不断 soft reboot。
原因:这类问题九成是 boot 阶段的 fstab 挂载失败导致 fs_mgr 判定 system 异常。常见原因包括:fstab 里wait标错了导致等一个不存在的设备节点;或者是挂载点目录在 init.rc 里的创建顺序太晚,boot 第一阶段找不到目录直接 kill 掉挂载流程。
解决:先抓 boot log 确认卡在哪一步。MTK 平台可以通过adb reboot后立刻adb logcat -b all或者在有串口的情况下看 uart log。如果明确是 fstab 的挂载问题,拉长wait或把挂载动作延后到on late-fs阶段处理,避免在 early boot 时抢占资源。
5.5 现象五:挂载成功但重启后又消失
现象:手动 mount 后一切正常,重启之后分区又没了,必须再手动 mount 一次。
原因:fstab 配置没有被打进启动阶段实际读取的那个 ramdisk。MTK 平台经常出现一种情况:fstab 改的是system/etc/fstab.mt6785,但 boot 流程实际读取的是vendor_bootramdisk 里的同名文件,你改了个寂寞。
解决:解包 vendor_boot 镜像校验 fstab 内容。如果确实缺失,把改好的 fstab 拷到 vendor_boot 的 ramdisk 里,重新打包 vendorbootimage,再刷机验证。
6. 验证与收尾:从分区块设备到真实读写的完整检查
改完分区、刷完机、系统能正常启动,并不代表这套配置就彻底没问题了。我的习惯是每次新加分区后都强制走一遍「块设备 → 文件系统 → 读写 → 重启持久化」四步检查,整套流程刷完才敢交付。这里分享一下我实际用的验证命令和执行顺序。
# 第一步:确认分区节点存在且大小正确 adb shell ls -l /dev/block/by-name/fdyram adb shell cat /proc/partitions | grep fdyram # 第二步:确认文件系统格式 adb shell blkid /dev/block/by-name/fdyram # 第三步:确认挂载点和文件系统类型 adb shell mount | grep fdyram adb shell df -h /mnt/fdyramby-name符号链接存在说明内核 dts 正确生成节点,blkid返回 ext4 格式则说明分区已被某次格式化过。如果blkid为空,说明这块分区还从没被格式化过,需要手动执行:
adb shell mkfs.ext4 /dev/block/by-name/fdyram格式化这个动作只需要做一次,后续重新刷机如果只刷 system 和 boot 分区,FDRAM 里的数据不会丢。但如果你重新刷了包含分区表的完整包,内容会被清掉,这是正常现象。
真实读写测试这一步,我用的方法很朴素但有效:写一个几百 MB 的文件进去,读取后做 md5 校验,然后重启设备再验证一次文件是否还在。
# 写入一个大于缓存的文件,绕过 page cache 验证真实落盘 adb shell dd if=/dev/urandom of=/mnt/fdyram/testfile.img bs=1M count=200 adb shell md5sum /mnt/fdyram/testfile.img adb reboot # 重启后再次读取 adb shell md5sum /mnt/fdyram/testfile.img md5sum 结果一致,说明分区挂载持久化和读写链路都通了。这块非常关键。很多分区挂载问题恰恰是「重启前一切正常,重启后打回原形」,md5 校验能在 5 分钟内帮你确认 fstab 和 ramdisk 的配置是不是真的生效了,而不是临时手动 mount 碰巧救回来的。
最后一个检查项是fs_mgr的开机 mount 日志。手动 mount 能成功不等于开机自动挂载成功,要看 fs_mgr 有没有真正走到 fstab 里那一行。
adb shell dmesg | grep fs_mgr adb shell dmesg | grep fdyram adb logcat -d | grep fdyramfs_mgr的日志里如果出现fdyram相关错误,比如mount failed或fs_mgr_verify警告,那说明还有问题藏着。通常我会在logcat -d里过滤Early和late-fs两个时间点的挂载记录,确认挂载动作是在 boot 早期触发而不是我的手动操作。
全部走完这套验证后,我一般还会做一次「强制刷机恢复测试」:把格式化后的 FDRAM 重新用完整 scatter 刷一遍,再走一遍上述检查,确保这次改动不是你手动干预出来的结果,而是从刷机流程到系统启动全自动跑通的能力。从那以后,我每加一个新分区,都强制走一遍这套流程,甚至在验证脚本里加了一个高亮输出的 ANSI 转义,专门标红 md5 不一致的情况。希望能帮到准备动分区表的你,少走几步弯路。
本文还有配套的精品资源,点击获取