简介:本资源是一份面向MTK平台Android系统开发工程师与内核定制人员的实战型技术方案,聚焦Android 9.0+环境下在MT6761芯片上新增FDRAM分区并挂载至根目录的完整实现路径,解决高版本A/B更新机制下传统分区添加失效、vold挂载失败、fstab适配异常等典型问题。压缩包共21个文件,含6个Makefile类构建脚本(用于分区表编译与烧录)、5个SELinux策略文件(.te)保障安全上下文、2个Python自动化脚本(分区校验与fstab生成)、2个CSV映射表(分区布局与设备节点对照),以及makefile、file_contexts、rc服务配置等关键系统级配置文件,总大小仅133KB,轻量但高度聚焦。目前已有683人学习下载,资源结构清晰、模块职责明确,提供可直接复用的分区定义模板、格式化命令组合、fstab条目范例及Android 9.0专属vold适配要点,大幅降低手动调试风险与重复踩坑成本。
1. 添加新分区 FDRAM 并挂载到根目录:MTK Android 系统里最易翻车的「空间扩容」操作
你刚拿到一块 MTK 平台的 Android 设备固件,发现/data分区才 2GB,而实际需求要塞下 5GB 的模型缓存 + 日志轮转 + OTA 差分包 —— 这时候有人告诉你:“加个 FDRAM 分区,挂到/下不就完了?”
听起来像玄学捷径,实则是黑匣子级高危操作。我见过太多人卡在mount: Invalid argument卡死 recovery,或刷完机直接进不了 system,甚至fstab改错一行导致init启动失败、无限重启。这不是 Linux 桌面加个硬盘那么简单:MTK 的 bootimg 结构、lk 阶段的分区识别逻辑、fstab.mt6765(或对应芯片名)的挂载时序、init.rc中on fs触发点,全得对上号。这套操作适合已能解包/重打包 boot.img、熟悉fastboot flash流程、且手头有完整 vendor 分区镜像的固件调试者。如果你还在用adb shell mount试挂载,建议先停在这——本篇讲的是从分区表定义开始,到getprop sys.boot_completed成功返回1的完整闭环。
2. 准备工作:确认芯片平台、提取原始分区表与 fstab 文件
2.1 确认 MTK 芯片型号与 Android 版本基线
MTK 不同 SoC(如 mt6765 / mt6785 / mt6833 / mt6893)使用的分区命名规则、lk 初始化顺序、fstab文件路径均有差异。不能跳过这步。
执行以下命令获取关键信息:
adb shell getprop ro.board.platform adb shell getprop ro.build.version.release adb shell cat /proc/cpuinfo | grep "Hardware"提示:
ro.board.platform返回值常为mt6765或mt6785;若返回unknown,则需通过cat /proc/cmdline查看 kernel cmdline 中的androidboot.hardware=参数。Android 11+ 与 Android 12+ 在init挂载逻辑上引入了fs_mgr的 strict mode,对fstab字段校验更严,后续配置必须匹配。
2.2 解包 boot.img 获取原始 fstab 与分区布局
MTK 平台的fstab通常内嵌在boot.img的 ramdisk 中,路径为/fstab.<platform>(如/fstab.mt6765),而非独立分区。需先解包:
# 假设已安装 android_boot_image_tools(推荐使用 https://github.com/pbatard/bootimg-tools) ./unpack_bootimg.py --boot_img boot.img --out boot_out gunzip -c boot_out/ramdisk.cgz | cpio -i解压后检查是否存在:
fstab.mt6765(以你实际芯片名为准)default.propinit.rc
同时,查看原始分区布局(用于确认是否有空闲 LBA 区域可划出 FDRAM):
adb shell cat /proc/partitions adb shell ls -l /dev/block/platform/*/*/by-name/重点关注misc、recovery、boot、system、vendor、userdata对应的设备节点(如/dev/block/mmcblk0pX),记录其起始扇区(start)与大小(size)。FDRAM 分区必须落在未被占用的连续扇区范围内,且不能跨物理 block 边界(常见坑:误将 FDRAM 划在userdata末尾但未对齐 emmc erase block size)。
2.3 确认 vendor 分区是否含libmodules与fstab备份
部分 MTK 方案会将fstab或init相关脚本放在vendor分区中(路径如/vendor/etc/fstab.mt6765)。执行:
adb shell ls -l /vendor/etc/fstab* adb shell ls -l /vendor/lib/modules/若存在,说明该设备采用 vendor-init 模式,fstab修改需同步更新 vendor 分区,否则init启动时因找不到模块或挂载规则而 fallback 到默认行为(即忽略你的 FDRAM)。
3. 定义 FDRAM 分区:修改分区表并生成新 bootimg
3.1 修改 GPT 分区表(适用于 eMMC UFS 设备)
MTK 大多使用 GPT(非 MBR)。需用gdisk或sgdisk工具在boot.img外部镜像中新增分区。注意:此操作针对的是boot.img所在的物理块设备(如mmcblk0),不是 ramdisk 内容!
首先,导出当前 eMMC 分区表(需 root 权限):
adb root adb shell dd if=/dev/block/mmcblk0 of=/sdcard/emmc_gpt.bin bs=512 count=34 adb pull /sdcard/emmc_gpt.bin用sgdisk添加新分区(假设空闲起始扇区为12345678,大小为2097152扇区 ≈ 1GB):
sgdisk --new=9:12345678:+2097152 \ --change-name=9:"FDRAM" \ --typecode=9:ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 \ emmc_gpt.bin参数说明:
-9表示第 9 个分区(GPT 最多支持 128);ebd0a0a2-b9e5-4433-87c0-68b6b72699c7是 Linux filesystem 标准 type code;
切勿使用--hybrid或--mbr-code—— MTK lk 不识别 hybrid MBR/GPT 混合模式,会导致 lk 阶段无法识别新分区。
3.2 更新 boot.img 中的 fstab 文件
进入之前解包的boot_out/ramdisk/目录,编辑fstab.mt6765(以实际芯片名为准)。在文件末尾添加一行:
/dev/block/mmcblk0p9 /fdram ext4 rw,barrier=1,discard,noatime,nosuid,nodev wait,check,first_stage_mount字段详解:
/dev/block/mmcblk0p9:必须与sgdisk创建的分区号严格一致;/fdram:挂载点,不能是/(根目录由 system 分区提供,FDRAM 只能作为子挂载点);ext4:MTK lk 默认仅支持 ext4,不支持 f2fs(除非你已编译并烧录自定义 lk);wait,check,first_stage_mount:
wait:等待设备节点出现;check:启动时运行e2fsck(生产环境建议改为nofail避免因文件系统脏导致卡住);first_stage_mount:表示在第一阶段 init(即init进程早期)挂载,早于on fs触发点,确保后续服务可依赖该路径。
3.3 重新打包 boot.img 并签名(如需)
重新构建 ramdisk:
find . | cpio -o -H newc | gzip > ../new_ramdisk.cgz ./mkbootimg --kernel kernel --ramdisk ../new_ramdisk.cgz \ --base 0x40078000 --pagesize 2048 --kernel_offset 0x00008000 \ --ramdisk_offset 0x01000000 --tags_offset 0x00000100 \ --output new-boot.img注意:
--base、--kernel_offset等参数必须与原boot.img一致(可用unpack_bootimg.py -i boot.img查看)。MTK 部分机型要求boot.img必须带签名校验(如avb),此时需用avbtool重新签名:avbtool add_hash_footer --image new-boot.img --partition_name boot --partition_size 33554432
4. 修改 init.rc 与 SELinux 策略:让系统真正“认”这个分区
4.1 在 init.rc 中创建挂载点并设置权限
仅靠fstab不足以让 Android 框架感知该路径。需在init.rc(或init.<platform>.rc)中显式声明:
# 在 on fs 阶段之后添加(确保 fstab 已解析) on post-fs-data # 创建挂载点目录 mkdir /fdram 0755 system system # 设置 SELinux 上下文(关键!否则 adb shell 无法访问) restorecon_recursive /fdram为什么放
post-fs-data?因为on fs阶段只处理 fstab 挂载,/fdram目录本身尚未创建;而post-fs-data在 userdata 挂载后、zygote 启动前执行,时机最稳妥。
4.2 编译并注入 SELinux 策略(.te文件)
Android 8.0+ 强制启用 SELinux enforcing 模式。若不添加策略,init会因avc: denied { mounton }拒绝挂载。需在 device 目录下新增策略文件(如device/xxx/sepolicy/vendor/fdram.te):
# fdram.te type fdram_file, file_type, data_file_type; type fdram_block_device, dev_type; # 允许 init 挂载到 /fdram allow init fdram_block_device:blk_file mounton; allow init fdram_file:dir create_dir_perms; allow init fdram_file:file create_file_perms; # 允许 shell 访问(调试用,量产建议收紧) allow shell fdram_file:dir r_dir_perms; allow shell fdram_file:file r_file_perms;然后在BoardConfig.mk中加入:
BOARD_SEPOLICY_DIRS += device/xxx/sepolicy/vendor血泪经验:
restorecon_recursive /fdram必须在mkdir后立即执行,否则init创建的目录上下文为u:object_r:rootfs:s0,而fdram_file类型需u:object_r:fdram_file:s0,不匹配则后续所有进程(包括logd、storaged)均无权读写。
4.3 验证 init.rc 修改是否生效
修改后重新编译 boot.img,刷入前可快速验证语法:
# 使用 aosp 提供的 init_parser(需编译 out/host/linux-x86/bin/init_parser) ./init_parser new-boot.img # 若输出 "Parsing succeeded" 且无 warning,则语法正确若报错Unknown action trigger 'post-fs-data',说明你改的是旧版init.rc(Android 7.x 用on property:触发),需查清目标 Android 版本对应的 init 语法规范。
5. 常见问题排查:五条真实踩过的坑与后悔药
5.1 现象:设备开机卡在 MTK Logo,串口打印Failed to mount /fdram
原因:fstab中设备路径错误(如写成/dev/block/mmcblk0p10但实际是 p9),或 lk 阶段未识别该分区(GPT type code 错误 / 分区未对齐)。
解决:
- 用
fastboot devices进入 fastboot 模式,执行fastboot getvar partition-type:fdram(若支持); - 更可靠方式:串口连接,看 lk 启动日志中是否有
Partition: FDRAM, start: 0xXXXXXX, size: 0xXXXXXX; - 若无此行,说明 lk 未加载该分区定义,需检查
preloader或lk源码中partition_table.c是否 hardcode 了最大分区数(如MAX_PARTITION_NUM = 8),需同步修改并重烧 lk。
5.2 现象:adb shell可见/fdram目录,但ls -l /fdram返回Permission denied
原因:SELinux 上下文未正确设置,或restorecon_recursive未执行(init.rc中漏写或触发时机错误)。
解决:
- 执行
adb shell ls -Z /fdram,确认输出是否为u:object_r:fdram_file:s0; - 若是
u:object_r:rootfs:s0,手动执行adb shell restorecon -R /fdram; - 检查
init.rc中restorecon_recursive是否拼写为restorecon_recursive(少下划线会静默失败)。
5.3 现象:挂载成功,但df -h显示/fdram使用率为 100%,touch /fdram/test报No space left on device
原因:ext4 文件系统未格式化,或格式化时未指定-O ^has_journal(MTK lk 不支持 journal 功能)。
解决:
- 在 recovery 模式下执行:
adb shell # 进入 recovery 后 e2fsck -f /dev/block/mmcblk0p9 mkfs.ext4 -O ^has_journal /dev/block/mmcblk0p9 - 严禁在 Android 正常运行时格式化——
mkfs会破坏正在挂载的文件系统元数据。
5.4 现象:/fdram挂载成功,但logcat中持续报avc: denied { write } for pid=1 comm="init"
原因:fstab中缺少context=u:object_r:fdram_file:s0字段,导致挂载时未自动设置上下文。
解决:
- 修改
fstab.mt6765对应行,补全 context:/dev/block/mmcblk0p9 /fdram ext4 rw,barrier=1,discard,noatime,nosuid,nodev wait,check,first_stage_mount,context=u:object_r:fdram_file:s0 - 重新打包 boot.img。
5.5 现象:OTA 升级后/fdram消失,fstab恢复为原始版本
原因:OTA 包中的boot.img未包含你的修改,或 vendor 分区被 OTA 覆盖(若fstab存于 vendor)。
解决:
- 将
fstab修改合并进 AOSP 源码的device/xxx/目录,参与 OTA 构建; - 若必须外挂,可在 OTA 后通过
update_engine_client --update --omaha_url=...触发二次升级,或在postinstall.sh中注入dd命令修补boot.img(高风险,仅限开发阶段)。
6. 验证挂载是否真正生效:从内核到应用层的四层检测法
6.1 第一层:内核设备节点与分区识别(底层可信度)
这是最硬的证据。进入 fastboot 或 recovery,执行:
fastboot devices fastboot getvar all 2>&1 | grep -i fdram # 若支持,应输出类似:partition-type:fdram: raw更通用的方式是直接读取 sysfs:
adb shell ls -l /sys/block/mmcblk0/mmcblk0p9/ cat /sys/block/mmcblk0/mmcblk0p9/name # 应输出 FDRAM cat /sys/block/mmcblk0/mmcblk0p9/size # 应为预期扇区数(如 2097152)若
ls /sys/block/mmcblk0/mmcblk0p9/报No such file or directory,说明 lk 或 kernel 层根本未识别该分区 —— 此时所有上层配置都是空中楼阁,必须回退到第 3 章重检 GPT 和 lk。
6.2 第二层:init 阶段挂载日志(启动时序验证)
抓取dmesg和logcat -b all的早期日志(需在init启动瞬间执行):
adb shell dmesg | grep -i "fdram\|mount" adb logcat -b events -b main -b system -v time | grep -i "fs_mgr\|fdram" | head -20成功日志应包含:
[ 1.234567] fs_mgr: __mount(source=/dev/block/mmcblk0p9, target=/fdram, type=ext4, flags=0x1000, opts=rw,barrier=1,discard,noatime,nosuid,nodev) [ 1.234589] fs_mgr: mount(/dev/block/mmcblk0p9, /fdram, ext4) succeeded若看到failed to mount /fdram但无后续错误,大概率是fstab字段语法错误(如多了一个空格),需用init_parser严格校验。
6.3 第三层:运行时状态与权限(功能可用性)
挂载成功后,必须验证三件事:可读、可写、可执行(若需):
adb shell # 1. 检查挂载属性 mount | grep fdram # 输出应含 rw,relatime(非 ro) # 2. 创建测试文件并验证属主 touch /fdram/test_$(date +%s) ls -lZ /fdram/test_* # 属主应为 system:system,上下文为 u:object_r:fdram_file:s0 # 3. 模拟应用写入(用 shell uid 测试) runas com.android.shell touch /fdram/from_app_$(date +%s) 2>/dev/null && echo "APP WRITE OK" || echo "APP WRITE FAIL"注意:
runas命令在 Android 10+ 受限制,若失败,改用su 0 touch ...(需 root)。关键不是命令是否成功,而是ls -lZ输出的上下文是否匹配你定义的fdram_file类型。
6.4 第四层:业务集成验证(真实场景穿透)
最后一步,必须用一个真实业务逻辑跑通。例如,让storaged服务将日志写入/fdram:
# 修改 /system/etc/storaged.conf(若存在)或通过 prop 注入 adb shell setprop persist.storaged.log_path /fdram/storaged_logs adb shell stop storaged adb shell start storaged adb shell ls -l /fdram/storaged_logs/或更彻底:编译一个最小 native 程序,用open("/fdram/test.bin", O_CREAT|O_WRONLY)调用,strace 看系统调用是否返回0。
从那以后我每次新增分区,都强制走一遍这四层检测:先sysfs确认设备存在,再dmesg看 init 是否挂载,接着ls -lZ验证 SELinux,最后用strace跑通一个 open/write/close 循环。少一层,上线后就可能半夜被报警电话叫醒。希望帮到你。
本文还有配套的精品资源,点击获取