MTK平台scatter.txt生成全解析:从分区表原理到自定义实践
1. 项目概述:从芯片到镜像,理解MTK分区表的枢纽
在MTK(联发科)平台的Android设备开发中,无论是进行系统定制、固件升级还是深度调试,有一个文件你绝对绕不开,那就是scatter.txt。这个看似普通的文本文件,却是连接芯片物理内存布局与软件镜像烧录的“地图”。很多刚接触MTK平台的朋友,拿到一个项目源码包,看到里面预置的scatter.txt,可能会直接拿来就用,或者仅仅修改一下分区大小。但你是否想过,这个文件是如何生成的?它背后的数据来源是什么?修改一个分区参数,会如何影响整个系统的启动和运行?
这就是我们今天要深入探讨的核心:MTK scatter.txt的生成过程。这个过程远不止是运行一个脚本那么简单,它贯穿了从芯片设计、硬件选型到软件系统规划的整个产品定义阶段。理解这个过程,不仅能让你在遇到“分区表错误”、“刷机失败”时快速定位问题,更能让你在自定义分区、适配新硬件或进行系统裁剪时,做到心中有数,游刃有余。简单来说,scatter.txt是ptgen(分区表生成器)工具根据硬件和软件需求“编译”出来的最终产物,而我们要拆解的,正是这个“编译”过程的输入、处理和输出。
2. 核心概念解析:分区表、scatter.txt与ptgen
在深入生成过程之前,我们必须厘清几个核心概念,以及它们之间的关系。这就像盖房子前要先看懂建筑图纸一样。
2.1 什么是分区表(Partition Table)?
在存储设备(如eMMC、UFS)上,分区表定义了如何将一块连续的物理存储空间划分成多个逻辑上独立的部分,每个部分就是一个分区。例如,boot分区存放内核和ramdisk,system分区存放Android系统,userdata分区存放用户数据。在MTK平台,分区表的核心信息通常来源于两个地方:
- 硬件定义:芯片本身有默认的存储器映射和预定义的分区建议,这通常在芯片的参考设计文档中。
- 软件需求:具体的项目(Project)根据要搭载的Android版本、功能特性(如双系统、安全启动)、预装应用大小等,对分区的大小、类型和顺序提出具体要求。
2.2 scatter.txt的角色与结构
scatter.txt是MTK专属的、面向SP Flash Tool(烧录工具)和LK(Little Kernel,MTK平台的bootloader之一)的分区表描述文件。它不是一个简单的列表,而是一个结构化的脚本,告诉工具:
- 每个分区的名称:如
PRELOADER,PGPT,PRO_INFO,NVRAM,PROTECT_F,PROTECT_S,SECCFG,UBOOT,BOOTIMG,RECOVERY,SEC_RO,MISC,LOGO,EXPDB,FRP,NVDATA,METADATA,SYSTEM,CACHE,USERDATA,FAT等。 - 该分区在物理存储上的起始地址(physical_start_addr):这是绝对地址,从存储器的0x0开始计算。
- 分区的大小(partition_size):以字节为单位。
- 该分区对应的镜像文件(file_name):烧录时使用的实际文件,如
boot.img,system.img。 - 分区的操作属性:如是否可擦除(
is_download),是否在烧录时进行校验等。
一个典型的scatter.txt片段如下:
- partition_index: SYS0 partition_name: PRELOADER file_name: preloader_k62v1_64_bsp.bin is_download: true type: SV5_BL_BIN linear_start_addr: 0x0 physical_start_addr: 0x0 partition_size: 0x40000 region: EMMC_USER storage: HW_STORAGE_EMMC boundary_check: true is_reserved: false operation_type: BOOTLOADERS reserve: 0x002.3 ptgen:分区表的“编译器”
ptgen(Partition Table Generator)是MTK平台用于生成分区表相关文件(主要是scatter.txt)的核心工具或脚本集合。它通常位于MTK发布的源码包中,路径类似于vendor/mediatek/proprietary/scripts/ptgen/。你可以把它理解为一个“编译器”,它读取“源代码”(硬件配置和软件需求),经过处理,输出“目标文件”(scatter.txt等)。
ptgen的主要输入:
- Memory Device List:一个定义了项目所用存储器件(如eMMC 64GB)基本信息的文件,包括总容量、块大小、预留区域等。
- Partition Configuration File:一个详细定义每个分区属性、大小规则和依赖关系的配置文件。这是工程师进行自定义的主要战场。
- Project Configuration:具体项目的Makefile或配置文件中定义的变量,如
MTK_EMMC_SIZE,MTK_NAND_PAGE_SIZE,MTK_FAT_ON_NAND等。
ptgen的主要输出:
scatter.txt:用于烧录。partition_table.mk(或ptgen.mk):用于Android编译系统,指导images的生成和打包。- 有时还会输出用于LK或Preloader内部使用的头文件。
理解了这三者的关系,我们就可以进入正题,看看ptgen是如何工作的。
3. scatter.txt生成流程全解析
生成一个可用的scatter.txt是一个多阶段、有严格顺序的过程。下面我们以一个虚拟的MTK 6789平台项目为例,拆解其完整流程。
3.1 第一阶段:硬件与项目配置收集
这是生成过程的基石,所有后续计算都依赖于本阶段的输入。
3.1.1 确定存储设备类型和容量这是第一步,也是决定性的一步。项目硬件工程师会选定具体的eMMC或UFS芯片,例如“SK海力士 128GB eMMC 5.1”。这个信息会通过项目的配置文件(如ProjectConfig.mk)传递给编译系统,通常以变量形式存在,例如:
MTK_EMMC_SUPPORT=yes EMMC_CHIP=KLUBG4U1EA-B0C1 # 这是海力士某型号的Part Number MTK_EMMC_SIZE=0x1D0000000 # 以字节表示的128GB容量,即116GB左右(厂商进制和系统进制的差异)ptgen会首先读取这些变量,确定它是在为哪种存储设备、多大容量生成分区表。这里有一个关键坑点:eMMC的总容量并非全部可用,开头一部分(通常是前几个MB)被用于硬件级的坏块管理、固件等,这部分是ptgen必须避开的。因此,在Memory Device List文件中,会明确定义USER_AREA_START_ADDRESS(用户可用区域起始地址)。
3.1.2 解析分区配置文件这是工程师干预最多的文件。在MTK代码中,通常位于device/mediatek/build/build/tools/ptgen/或类似路径下,文件名可能是[platform]_partition_table.conf(如mt6789_partition_table.conf)。 这个文件定义了所有可能的分区,以及它们的大小计算规则。规则非常灵活:
- 固定大小:
partition_size: 0x800000(固定8MB)。 - 比例大小:
partition_size: 2%(占用户可用空间的2%)。 - 剩余空间:对于最后一个分区(如
USERDATA),通常会定义为partition_size: -1,表示占用所有剩余空间。 - 对齐要求:
alignment: 0x100000(1MB对齐)。这对Flash存储器的性能至关重要,未对齐的读写会极大降低速度。 - 依赖关系:例如
SECCFG分区必须在PROTECT_F之后,NVRAM必须有一个固定的备份分区等。
实操心得:修改这个配置文件是自定义分区的标准方法。但切记,不要只改一个分区的大小就了事。比如你把
SYSTEM分区从3GB扩大到4GB,那么它后面的所有分区(CACHE,USERDATA等)的起始地址都会后移1GB。你必须确保最后一个分区(如USERDATA)的“剩余空间”定义仍然有效,且总容量没有超出存储设备极限。一个实用的方法是,在修改后,用ptgen的调试模式先跑一遍,输出计算过程中的中间变量,检查是否有分区溢出或地址冲突。
3.2 第二阶段:ptgen核心计算与决策
收集完所有配置后,ptgen脚本(通常是Perl或Python编写)开始进行核心的逻辑计算。
3.2.1 建立分区顺序链表ptgen首先会根据配置文件,建立一个有顺序的分区链表。这个顺序不是随意的,它遵循MTK平台的内在要求:
- Preloader区域:最开始的区域,存放
PRELOADER和PGPT(Primary GPT表)等,这些分区在USER_AREA之外,地址非常靠前。 - 保护性分区:如
PRO_INFO,NVRAM,PROTECT_F/S。这些分区存放IMEI、NVRAM参数、安全密钥等关键数据,需要被保护,防止被意外擦除。 - Bootloader相关:
SECCFG,UBOOT,BOOTIMG,RECOVERY,SEC_RO。这是启动链的核心。 - 系统与数据分区:
MISC,LOGO,SYSTEM,VENDOR,PRODUCT,CACHE,USERDATA等。 - GPT备份:
SGPT(Secondary GPT表)位于磁盘末尾。
ptgen会按照这个逻辑顺序,依次处理每个分区节点。
3.2.2 动态计算分区大小与地址这是最核心的算法部分。ptgen会遍历分区链表:
- 计算当前分区大小:根据配置规则(固定值、百分比、剩余值),结合已知的总用户空间大小,计算出该分区的字节数。
- 地址对齐:将计算出的分区起始地址,根据该分区的
alignment要求进行向上对齐。例如,当前地址是0x12345678,对齐要求是0x100000(1MB),那么对齐后的地址将是0x12400000。 - 更新下一个分区的起始地址:将当前分区的起始地址加上其对齐后的大小,作为下一个分区的起始地址。
- 循环与校验:重复以上步骤,直到处理完所有分区。在这个过程中,
ptgen会持续进行边界检查:- 检查每个分区的结束地址是否超过了存储设备的总物理地址。
- 检查是否有分区地址重叠。
- 对于定义为“剩余空间”的分区(如
USERDATA),会用一个标志位标记,在最后计算时,用总空间减去已分配空间,得出其实际大小。
3.2.3 处理特殊分区与依赖一些分区有特殊逻辑:
- GPT表:
PGPT和SGPT的大小和位置是固定的,由eMMC规范决定,ptgen会直接写入固定值。 - 备份分区:如
NVRAM和NVDATA通常有主备两份,ptgen会自动计算备份分区的地址。 - FAT分区:如果项目配置了
MTK_FAT_ON_NAND(旧式设计),ptgen会在最后预留一个FAT分区。
3.3 第三阶段:文件生成与输出
计算完成后,ptgen会将结果输出到不同的文件中,以适应不同的下游工具。
3.3.1 生成scatter.txtptgen将遍历最终确定的分区链表,按照SP Flash Tool要求的格式,为每个分区生成一个条目。关键步骤包括:
- 将计算出的十六进制地址和大小填入
physical_start_addr和partition_size。 - 根据分区类型,关联默认的镜像文件名。例如,
BOOTIMG关联boot.img,RECOVERY关联recovery.img。 - 设置分区属性:
is_download: true表示该分区需要被烧录;对于PRO_INFO、NVRAM等存放设备唯一数据的分区,可能会设置为is_download: false,因为它们在量产时通过专用工具写入,日常刷机不应被覆盖。 - 最终生成一个完整的、语法正确的
scatter.txt文件。
3.3.2 生成编译系统文件同时,ptgen会生成一个partition_table.mk文件。这个文件被Android的编译系统(通过BoardConfig.mk引入)使用。它主要做两件事:
- 定义
BOARD_xxxIMAGE_PARTITION_SIZE:例如BOARD_SYSTEMIMAGE_PARTITION_SIZE := 3221225472。当执行make systemimage时,编译系统会读取这个值,来创建指定大小的system.img文件。如果system.img的实际内容超过了这个大小,编译就会报错。 - 定义分区列表:提供一个所有分区名称的列表,用于一些需要遍历分区的脚本。
3.3.3 集成到编译流程在完整的Android源码编译中(例如执行source build/envsetup.sh && lunch <project>然后make),ptgen的调用通常被集成在device/mediatek/<project>/device.mk或类似的Makefile中。它会在编译早期被自动执行,确保生成的scatter.txt和partition_table.mk与当前代码版本和项目配置同步。
4. 自定义分区与高级调试技巧
理解了自动生成过程,我们就能进行有效的手动干预和问题排查。
4.1 如何安全地自定义分区?
假设你需要为项目增加一个OEM分区来存放客制化应用,或者扩大VENDOR分区以容纳新的HAL库。
标准操作流程如下:
- 定位配置文件:找到你项目对应的
[platform]_partition_table.conf文件。 - 插入新分区定义:在合适的位置(例如,在
SYSTEM分区之后,USERDATA分区之前)添加你的新分区。必须明确指定其partition_size(固定值或百分比)和alignment。partition_name: OEM file_name: oem.img partition_size: 0x20000000 # 512MB alignment: 0x100000 # 1MB对齐 - 调整受影响分区:由于你插入了一个新分区,后面所有分区的起始地址都会后移。你必须手动调整最后一个使用“剩余空间”定义的分区(通常是
USERDATA)的规则,确保它仍然能正确计算大小。或者,你可以选择缩小某个现有分区(如CACHE)来腾出空间。 - 重新生成并验证:
- 执行
ptgen脚本或重新编译整个Android源码(make会触发ptgen)。 - 检查新生成的
scatter.txt,确认新分区的地址、大小正确,且没有地址重叠或溢出。 - 检查生成的
partition_table.mk,确认有BOARD_OEMIMAGE_PARTITION_SIZE的定义。
- 执行
- 更新编译配置:在设备的
BoardConfig.mk中,可能需要添加BOARD_OEMIMAGE_FILE_SYSTEM_TYPE := ext4等定义,以便编译系统知道如何打包oem.img。
避坑指南:绝对不要直接修改自动生成的
scatter.txt文件!因为这个文件是ptgen的输出结果。下次编译或执行ptgen时,你的修改会被覆盖。所有自定义都必须在输入文件(*.conf配置文件)中进行。
4.2 scatter.txt相关问题的排查思路
当刷机失败,SP Flash Tool报错如ERROR: S_UNDEFINED_ERROR(0xCDCD0501)或ERROR: S_PART_MAUI_SPACE_NOT_ENOUGH(0xFD010002)时,很可能与scatter.txt有关。
排查步骤:
- 核对版本:首先确认你使用的
scatter.txt是否与你的设备型号、硬件版本(尤其是eMMC容量)完全匹配。用64GB版本的scatter.txt去刷32GB的设备,百分之百会失败。 - 检查分区大小:使用文本编辑器打开
scatter.txt,计算所有分区的partition_size之和。这个总和加上PGPT/SGPT等开销,必须小于等于eMMC芯片的USER_AREA总大小。如果超出,就会触发空间不足错误。 - 检查镜像文件:确认
scatter.txt中列出的file_name(如system.img)是否真实存在于刷机包中,并且其实际文件大小是否超过了对应分区的partition_size。system.img过大是导致刷机失败的常见原因。 - 使用ptgen调试:如果怀疑是生成过程有问题,可以尝试手动运行
ptgen脚本,并打开调试输出。通常可以通过添加-v或--verbose参数来实现。这会打印出每一步的计算过程和中间变量,帮助你定位是哪个分区的计算出了错。 - 对比法:找一个已知正常的、同型号设备的
scatter.txt,与你出问题的文件进行逐行对比。重点关注PRELOADER,PGPT,BOOTIMG,SYSTEM,USERDATA这几个关键分区的地址和大小。
一个典型错误案例:工程师A为了放入更大的GMS包,将SYSTEM分区从0xC0000000(3GB) 改为0x100000000(4GB),但没有相应缩小USERDATA或确认总容量。ptgen在计算时,USERDATA的起始地址后移了1GB,但其“占用剩余空间”的规则导致其结束地址超出了eMMC的物理末尾地址。由于ptgen的边界检查,这个错误的配置在生成时就可能报错。如果检查被绕过,生成的scatter.txt中USERDATA分区将有一个非法的结束地址,用这个文件刷机,在写入USERDATA分区时就会发生失败。
5. 深入原理:从scatter.txt到设备启动
理解了生成过程,我们还能更深入地看scatter.txt如何影响设备的生死。
5.1 scatter.txt与Preloader/LK的交互
scatter.txt不仅是烧录工具的指南,其信息也被编译进了Preloader和LK中。
- 在编译
Preloader时,会提取scatter.txt中关于分区布局的信息,生成一个头文件(如partition_define.h)。Preloader依靠这个信息来定位LK(或UBOOT)所在的分区,从而加载并跳转。 LK同样需要知道分区布局,才能挂载boot或recovery分区来加载Linux内核。如果scatter.txt中的分区地址与Preloader/LK中硬编码或编译时写入的地址不匹配,设备将无法启动,卡在最早期的引导阶段。
5.2 GPT与MBR:现代分区表的基石
虽然MTK使用自定义的scatter.txt格式,但其描述的分区布局最终会体现在存储设备的GPT(GUID Partition Table)上。PGPT和SGPT分区就是存放GPT数据的地方。SP Flash Tool在烧录时,除了写入各个镜像文件,还会根据scatter.txt的信息,在PGPT和SGPT位置写入标准的GPT表。操作系统(包括Android)是通过读取GPT来识别分区的。因此,scatter.txt的准确性,直接决定了设备上GPT表的正确性。
5.3 动态分区(Dynamic Partition)带来的变化
从Android 10/Q开始,Google引入了动态分区(Dynamic Partition)机制,用于system、vendor、product等只读分区。这对于scatter.txt的生成有重大影响。
- 布局简化:在
scatter.txt中,不再为system、vendor等定义固定大小的独立分区,而是定义一个大的super分区。 - 生成逻辑变化:
ptgen的配置和计算逻辑需要适配动态分区。分区大小可能在刷机时由lpmake工具动态决定,而不是在scatter.txt中写死。scatter.txt中super分区的大小需要足够容纳所有动态子分区的总和。 - 工具链更新:SP Flash Tool也需要更新以支持烧录
super分区镜像。理解动态分区下的scatter.txt生成,需要同时理解super.img的构建过程。
这个过程虽然底层,但却是MTK Android设备开发的基石之一。它不像写应用逻辑那样有直接的界面反馈,但一旦出错,影响是根本性的——设备变砖。掌握它,意味着你拥有了从系统层面定义和改造设备存储布局的能力。下次当你打开一个scatter.txt文件时,希望你能看到的不仅仅是一行行地址和数字,而是一张由芯片规格、项目需求和工具脚本共同绘制的精密地图。