本篇主要介绍一下各个平台和分区配置相关联的知识。
一、MTK平台
1、MTK平台分区表配置
MTK官方文档专门针对分区表进行介绍:FAQ28492
Legacy单分区的分区配置路径:
alps/vendor/mediatek/proprietary/tools/ptgen/MTxxxx/partition_table_emmc.csv
AB 版本双分区的分区配置路径:
alps/vendor/mediatek/proprietary/tools/ptgen/MTxxxx/partition_table_emmc_ab.csv
其中几个重要解释如下:
- Type:RAW data 或 EXT4,这种定义其实只有在 正在运行挂载时才会被用到,但对OTA 升级的同仁,就要注意,因为某些判断脚本中会根据这个type 类型决定加入镜像
- Size_KB:分区大小单位kb,这个定义了 eng 和 user 版本在storage中占用的空间。但目前user 和eng 版本没有区分,所以只需要设定一个即可
- Region:分区所在的区域,如:EMMC_BOOT1_BOOT2,UFS_LU0_LU1 就表明在 EMMC 和 UFS 的 boot区域;而在 EMMC_USER 或 UFS_LU2,就表明 在EMMC 和 UFS 的user区域,也就是可供客制化的区域
- Group:可以填写动态分区的名称,默认只有main 函数。感觉没有什么用
- Reserved:用的不多,后续补充~应该是预留的空间,一般分区都是 N
- Download:是否下载标记,这个比较重要,关联刷机包的配置
- Download File:下载的镜像,这个也比较重要,我们可以很直观了解到对应哪个IMG文件
- OTA Update:分区是否支持升级
- EmptyBoot_Needed:在pl 阶段可以起来的分区,如 gz、tee、logo几个分区
- FastBoot_Erase:fastboot是否可以擦除,像super、userdata通常配置为yes,即可以通过恢复出厂设置擦除?
- FastBoot_Download:fastboot是否可以下载
- Operation_Type:操作类型。AUTO就会根据对齐进行调整大小;BOOTLOADERS表示设备启动镜像(例如preloader分区);INVISIBLE不可见不用刷机只定义范围;PROTECTED/BINREGION/RESERVED表示需要保留的分区,固件升级Fireware upgrade会备份数据,只有格式化刷机才会擦除
- Addr_Align:对齐方式,为写保护
- Delete_Rule:是否需要这个分区,通过判断条件来确定是否需要这个分区。例如le(PRODUCT_SHIPPING_API_LEVEL,30)表示PRODUCT_SHIPPING_API_LEVEL小于30就会删除此分区
2、MTK平台刷机配置表
MTK平台的刷机工具Flash Tool工具加载MTXXXX_Android_scatter.txt文件,改工具根据txt文件里面定义的标准格式解析如下信息:分区名称,起始地址和结束地址,镜像的路径来进行刷机。scatter.txt文件的内容者根据第一节分区表配置而来。
其原理就是build ptgen阶段会解析默认分区表文件partition_table_MT65XX.xls并生成相关的partition .c/.h文件和flash download使用的scatter file。他的格式如下:
如上Flash Tools工具显示的需要下载的分区和镜像文件也和分区表中配置的Download与Download File字段一致。
最后提验一下MTK针对scatter文件的生成逻辑和原理:FAQ28494
1)分区表目录
Q版本之前:device/mediatek/build/build/tools/ptgen
R版本之后:vendor/mediatek/proprietary/tools/ptgen
这个目录下很多的platform ,common , utils,并且包含着一些与 OTA 相关的脚本文件。
可以通过如上方式查找project对应的ptgen.mk文件
2)ptgen.mk逻辑
ptgen.mk文件包含了 storage 的类型支持, feature 对应的 partition 是否开关,这些都会读取 ProjectConfig.mk 的设定来进行选择,也可以确定使用哪个 Platform 中的分区表。
在ptgen.mk 还有一个关键信息:default_ptgen.py ,程序会根据 ProjectConfig.mk的配置来进行生成ptgen。
进入default_ptgen.py 首先看 main 函数
3)编译日志生成scatter.txt文件
从log 的角度看,在编译的初期,就会根据BoardConfig.mk 和 ProjectConfig.mk 中的信息,进行生成scatter.txt文件。可以分为如下几个步骤:
开始ptgen脚本
[ptgen-py] !!!!!!!!!!!!!!!!!!! start !!!!!!!!!!!!!!!!!!!
[ptgen-py] *******************Arguments*********************针对MTK_PTGEN_COMMAND中的配置进行赋值(ProjectConfig.mk)
[ptgen-py] MTK_PLATFORM = MT6765
[ptgen-py] TARGET_COPY_OUT_ODM_DLKM = ||ODM_DLKM-PATH-PH||
[ptgen-py] MTK_COMBO_NAND_SUPPORT =
[ptgen-py] TARGET_BUILD_VARIANT = userdebug
[ptgen-py] MTK_DPM_SUPPORT =
[ptgen-py] MTK_TINYSYS_SCP_SUPPORT = yes
[ptgen-py] MTK_AUDIODSP_SUPPORT =分区表的路径打印(根据MTK_AB_OTA_UPDATER 选择 AB 分区的分区表)
[ptgen-py] MTK_AB_OTA_UPDATER = yes[ptgen-py] layout_path = vendor/mediatek/proprietary/tools/ptgen/MT6765/partition_table_emmc_ab.csv
[ptgen-py] SHEET_NAME = emmc_ab
[ptgen-py] *******************Arguments*********************
[ptgen-py] partition layout file path : vendor/mediatek/proprietary/tools/ptgen/MT6765/partition_table_emmc_ab.csv根据deleteRule 来确定是否用该分区(runDelRule)
[ptgen-py] frp has deleteRule: ne(MTK_FACTORY_RESET_PROTECTION_SUPPORT,yes) is False
[ptgen-py] md_udc has deleteRule: ne(MTK_BOARD_USES_METADATA_PARTITION,true) is False
[ptgen-py] persist has deleteRule: ne(MTK_PERSIST_PARTITION_SUPPORT,yes) is True
[ptgen-py] efuse has deleteRule: ne(MTK_EFUSE_WRITER_SUPPORT,yes) is True
[ptgen-py] md1dsp_a has deleteRule: eq(MTK_SINGLE_BIN_MODEM_SUPPORT,yes) is True
[ptgen-py] spmfw_a has deleteRule: ne(SPM_FW_USE_PARTITION,yes) is False
[ptgen-py] scp_a has deleteRule: ne(MTK_TINYSYS_SCP_SUPPORT,yes) is False
[ptgen-py] sspm_a has deleteRule: ne(MTK_TINYSYS_SSPM_SUPPORT,yes) is False
[ptgen-py] vendor_boot_a has deleteRule: le(PRODUCT_SHIPPING_API_LEVEL,30) is False
[ptgen-py] tee_a has deleteRule: ne(MTK_ATF_SUPPORT,yes) and ne(MTK_TEE_SUPPORT,yes) is False
[ptgen-py] odm_a has deleteRule: ne(TARGET_COPY_OUT_ODM,odm) is True
[ptgen-py] vbmeta_a has deleteRule: ne(MTK_BOARD_AVB_ENABLE,true) is False计算需要对齐的分区地址(runCalAddr)
ptgen-py] BoardConfig File exists: /worktmp/cas_personal/mtk27529/alps-mp-s0.mp1--2022_02_24_02_00/alps-mp-s0_mp1--2022_02_24_02_00/merged/device/mediateksample/k62v1_64_bsp/BoardConfig.mk
[ptgen-py] Need adjust start address for seccfg, because it is 0x522000 now.pad size is 0x2de000, and pre part [protect2]size is 0xade000
[ptgen-py] Need adjust start address for sec1, because it is 0x80000 now.pad size is 0x780000, and pre part [seccfg]size is 0x800000
[ptgen-py] Need adjust start address for md1img_a, because it is 0x500000 now.pad size is 0x300000, and pre part [logo]size is 0xb00000
[ptgen-py] Need adjust start address for md1img_b, because it is 0x500000 now.pad size is 0x300000, and pre part [vbmeta_vendor_a]size is 0xb00000
[ptgen-py] Need adjust start address for super, because it is 0x500000 now.pad size is 0x300000, and pre part [tee_b]size is 0x800000生成分区表(genfile_scatter)
[ptgen-py] Gen ymal file in Path out/target/product/k62v1_64_bsp/obj/PTGEN/../../MT6765_Android_scatter.txt
[ptgen-py] Gen xml file in Path out/target/product/k62v1_64_bsp/obj/PTGEN/../../MT6765_Android_scatter.xml
[ptgen-py] Gen partition_size.mk file in Path out/target/product/k62v1_64_bsp/obj/PTGEN/partition_size.mk根据super分区设定的大小,对动态分区进行设置大小
[ptgen-py] main group size candi:
[ptgen-py] parts_sum := 4328521728
[ptgen-py] group_limit := 4292870144
[ptgen-py] final selected 4292870144完成分区表设定,进行编译
[ptgen-py] Gen PGPT in Path out/target/product/k62v1_64_bsp/obj/PTGEN/../../PGPT_EMMC
[ptgen-py] !!!!!!!!!!!!!!!!!!! end !!!!!!!!!!!!!!!!!!!
3、MTK平台分区表配置不生效
在某些时候分区配置表与手机实际大小并不一致,或者分区表配置修改了后没有生效,如下两个FQA回答了可能的原因:FAQ11445 FAQ21282
4、MTK平台最大内存限制
因一些需求,在MTK平台上面接触到了CUSTOM_CONFIG_MAX_DRAM_SIZE宏控,这个宏控的命名来看就是定制最大内存大小的限制,如果配置为1G,那么即使内存大小有4G,android系统是否只能使用1G的大小呢?基于这个疑问,我跟读了代码逻辑:
如上代码是获取此宏控的值,返回值为最大dram大小。
1)PL阶段DRAM初始化
如下代码DRAM继续初始化,但是在计算DRAM有多少个rank的时候,通过计算超过此宏匹配的值大小之后,直接break。如果配置为1G,那么这里的rank数量就只有1,会忽略掉后面的统计。
DRAM内存在进行分配的时候,也会计算,如果超过此宏配置大小,会直接跳过。
如上代码处于PL阶段,上电之后的第一个阶段,加载代码到SRAM运行。
2)BL2阶段内存拓扑构建
BL2阶段是bootloader第二阶段,加载代码到DRAM运行了。在setup_mblock_info函数中进行内存拓扑构建。这里会遍历所有rank,如果超过此宏配置的最大内存值计算出来的rank数量就会直接break。例如CUSTOM_CONFIG_MAX_DRAM_SIZE如果为1G的话:
- rank0: size=1GB, 累计1GB ≤ 1GB → mblock[0].size = 1GB ✅ 全部可用
- rank1: size=1GB, 累计2GB > 1GB → mblock[1].size = 1GB - 1GB = 0 ❌ 被截断
//vendor/mediatek/proprietary/bootable/bootloader/lk2/lib/mblock/mblock_bl2.c u64 get_config_max_dram_size(void) { u64 max_dram_size = -1; /* max value */ char *doe_max_dram_size_config = 0; u64 doe_max_dram_size; int size_shift = 28; /* 256 MB */ #ifdef CUSTOM_CONFIG_MAX_DRAM_SIZE max_dram_size = CUSTOM_CONFIG_MAX_DRAM_SIZE; #endif #ifdef MTK_DOE_CONFIG_ENV_SUPPORT doe_max_dram_size_config = dconfig_getenv("DOE_CUSTOM_CONFIG_MAX_DRAM_SIZE"); #endif if (doe_max_dram_size_config) { doe_max_dram_size = (u64)atoi(doe_max_dram_size_config); if (doe_max_dram_size < 1) doe_max_dram_size = 1; LTRACEF_LEVEL(ALWAYS, "DOE CUSTOM_CONFIG_MAX_DRAM_SIZE: %llx\n", doe_max_dram_size); if (((doe_max_dram_size << size_shift) >> size_shift) != doe_max_dram_size) goto out; /* overflow, use default value */ max_dram_size = doe_max_dram_size << size_shift; } out: LTRACEF_LEVEL(ALWAYS, "CUSTOM_CONFIG_MAX_DRAM_SIZE: 0x%llx\n", max_dram_size); return max_dram_size; } void setup_mblock_info(struct mblock_info_t *mblock_info, struct dram_info_t *orig_dram_info, struct mem_desc_t *lca_reserved_mem) { int i; u64 max_dram_size = -1; /* MAX value */ u64 size = 0; u64 total_dram_size = 0; memset((void *)mblock_info, 0, sizeof(struct mblock_info_t)); for (i = 0; i < orig_dram_info->rank_num; i++) { total_dram_size += orig_dram_info->rank_info[i].size; } mblock_info->mblock_magic = MBLOCK_MAGIC; mblock_info->mblock_version = MBLOCK_VERSION; max_dram_size = get_config_max_dram_size(); lca_reserved_mem->start = lca_reserved_mem->size = 0; // non-4GB mode case // we do some DRAM size fixup here base on orig_dram_info for (i = 0; i < orig_dram_info->rank_num; i++) { size += orig_dram_info->rank_info[i].size; mblock_info->mblock[i].start = orig_dram_info->rank_info[i].start; mblock_info->mblock[i].rank = i; /* setup rank */ if (size <= max_dram_size) { mblock_info->mblock[i].size = orig_dram_info->rank_info[i].size; } else { /* max dram size reached */ size -= orig_dram_info->rank_info[i].size; mblock_info->mblock[i].size = max_dram_size - size; /* get lca_reserved_mem info */ lca_reserved_mem->start = mblock_info->mblock[i].start + mblock_info->mblock[i].size; if (mblock_info->mblock[i].size) mblock_info->mblock_num++; break; } if (mblock_info->mblock[i].size) mblock_info->mblock_num++; } LTRACEF_LEVEL(ALWAYS, "total_dram_size: 0x%llx, max_dram_size: 0x%llx\n", total_dram_size, max_dram_size); LTRACEF_LEVEL(ALWAYS, "dump mblock info\n"); for (i = 0; i < mblock_info->mblock_num; i++) { LTRACEF_LEVEL(ALWAYS, "mblock[%d] start=0x%llx size=0x%llx\n", i, mblock_info->mblock[i].start, mblock_info->mblock[i].size); } if (total_dram_size > max_dram_size) { /* add left unused memory to lca_reserved memory */ lca_reserved_mem->size = total_dram_size - max_dram_size; LTRACEF_LEVEL(ALWAYS, "lca_reserved_mem start: 0x%llx, size: 0x%llx\n", lca_reserved_mem->start, lca_reserved_mem->size); } // for 4GB mode, we fixup the start address of every mblock }3)LK阶段传递linux内核
在LK阶段逻辑也基本同上。
4)传给android层
最后把此宏传给了ro.vendor.mtk_config_max_dram_size属性,android层即可通过此属性来进行一些上层功能的定制。
5、MTK平台新增分区
6、Super分区lpdump命令
Super分区也叫做dynamic动态分区,动态分区是Android的用户空间分区系统,在Android R版本开始引入,目的是为了解决system和vender等分区size不能动态调整的问题。例如物理分区表配置固定size后,如果软件版本对system,verdor分区size需要频繁调整时,需要修改物理分区表和重新编译gpt表,使用起来不是很便利。
我们可以通过命令adb shell lpdump来输出打印super分区的具体内容:
$ lpdump Slot 0: Metadata version: 10.2 Metadata size: 1000 bytes Metadata max size: 65536 bytes Metadata slot count: 3 Header flags: virtual_ab_device Partition table: ------------------------ Name: product_a Group: main_a Attributes: readonly Extents: 0 .. 253743 linear super 2048 ------------------------ Name: product_b Group: main_b Attributes: readonly Extents: ------------------------ Name: system_a Group: main_a Attributes: readonly Extents: 0 .. 1410975 linear super 256000 ------------------------ Name: system_b Group: main_b Attributes: readonly Extents: 0 .. 15647 linear super 1667072 ------------------------ Name: system_ext_a Group: main_a Attributes: readonly Extents: 0 .. 700503 linear super 1683456 ------------------------ Name: system_ext_b Group: main_b Attributes: readonly Extents: ------------------------ Name: vendor_a Group: main_a Attributes: readonly Extents: 0 .. 430351 linear super 2385920 ------------------------ Name: vendor_b Group: main_b Attributes: readonly Extents: ------------------------ Super partition layout: ------------------------ super: 2048 .. 255792: product_a (253744 sectors) super: 256000 .. 1666976: system_a (1410976 sectors) super: 1667072 .. 1682720: system_b (15648 sectors) super: 1683456 .. 2383960: system_ext_a (700504 sectors) super: 2385920 .. 2816272: vendor_a (430352 sectors) ------------------------ Block device table: ------------------------ Partition name: super First sector: 2048 Size: 2147483648 bytes Flags: none ------------------------ Group table: ------------------------ Name: default Maximum size: 0 bytes Flags: none ------------------------ Name: main_a Maximum size: 2145386496 bytes Flags: none ------------------------ Name: main_b Maximum size: 2145386496 bytes Flags: none ------------------------其中重点需要关注的内容如下:
Super partition layout
此部分记录了super分区从第一个扇区到最后一个扇区的内容
super: 2048 .. 255792: product_a (253744 sectors)
---->super的第2048扇区到255792扇区是product_a,总共占用了253744个扇区
super: 256000 .. 1666976: system_a (1410976 sectors)---->super的第256000扇区到1666976扇区是system_a,总共占用了1410976个扇区
super: 1667072 .. 1682720: system_b (15648 sectors)---->super的第1667072扇区到1682720扇区是system_b,总共占用了15648个扇区
super: 1683456 .. 2383960: system_ext_a (700504 sectors)---->super的第1683456 扇区到2383960扇区是system_ext,总共占用了700504个扇区
super: 2385920 .. 2816272: vendor_a (430352 sectors)---->super的第2385920扇区到2816272扇区是vendor_a,总共占用了430352个扇区
注意:单位为扇区,因此需要转换为MB,公式为sectors * 512 /1024/1024
即这段内容表明super分区按照顺序包含如下几个分区:product_a占用124MB,system_a占用689MB,system_b占用8MB,system_ext_a占用342MB,vendor_a占用210MB
Block device table
此部分记录了super分区的总大小,即2147483648 /1024 /1024 = 2048MB,此值应该和上面的super分区表的值对应,如果不对应,需要check第三节的内容,是否存在定制
Partition name: super
First sector: 2048
Size: 2147483648 bytes
Flags: none
二、高通平台
三、相关案例
1、MTK平台Super分区不够导致编译报错
问题背景:此项目需要集成google mainline和google gms包,集成之后导致super分区不够编译失败,报错日志如下:
问题分析:根据如上日志进行解读,在编译super的时候,produc_a先申请了1795MB,system_a申请了749MB,system_b申请了54MB,system_ext_a需要428MB,最后vendor_a需要申请226MB,但因为空间不够无法申请。
前后对比:因为上面日志是加了google mainline gms包之后的,加之前这块的编译日志如下,即product_a从129MB飙升到1795MB,挤压super空间,导致最后的vendor_a分区没有足够的大小。
解决思路:从日志已经很明确是super分区不够,从日志中显示super分区为3244949504b即大约3094MB,从前面的produc_a+system_a+system_b+system_ext_a+vendor_a = 3252MB即,刚好差200多M。查看分区表配置信息如下:
通过adb shell lpdump之前的机器,super各个分区的大小和这个编译日志能够匹配的上,super大小和编译日志里面记录的'super:3244949504'一致,和分区表配置的4G完全不一样,最后通过MTK的FQA,找到BoardConfig.mk中对super分区有客制化定义:
经过此案例以及可疑点我们可以得出如下几个结论:
- 经验一:在MTK平台针对分区表配置不生效或者跟实际对不上的情况,需要搜索是否存在BOARD_MTK_XXX_SIZE_KB的客制化。
- 结论一:针对super分区应该只关心super分区总大小,其内部各个子分区大小不用根据实际情况来进行调整,例如product_a实际使用了1.7G,但是分区表配置的256MB并没有导致什么报错或者异常,system_a和system_b分区在分区表配置了3G,即这两部分加起来都超过super配置的4G了,但同样也没有存在什么报错或者异常。其原因为super分区内部根据各个实际大小来进行动态调整。
2、MTK平台限制内存大小为1G
问题背景:因为内存涨价,导致成本非常高,因此从市场环境来看,搭载1G内存的android系统非常的有优势,但是最小的android系统都是2G,即2G内存大小的android设备被google官方定义为搭载Google GO项目,这样的项目同样具备google服务的核心组件。
解决思路:MTK平台CUSTOM_CONFIG_MAX_DRAM_SIZE宏控对内存大小进行了限制。可以使用该宏控限制android系统内存大小,然后编译镜像刷机到2G内存的设备,查看整个android系统内存占用不超过1G。
解决方案:CUSTOM_CONFIG_MAX_DRAM_SIZE = 0x40000000
原理解析:MTK如何通过CUSTOM_CONFIG_MAX_DRAM_SIZE宏控进行限制的?可以参考第一章第四节。