公司动态
Android 7系统编译与刷机(三)从编译产物到高通刷机包
系列目录Android 7系统编译与刷机一源码环境搭建与完整编译流程 | 二Image镜像生成机制深度解析 | 三从编译产物到高通刷机包 | 四QFIL刷机工具详解 | 五特定分区镜像的单独导出 | 六特定分区刷入实战一、为什么要研究刷机包打包流程编译完系统后out/target/product/设备名/下有一堆.img文件。但如果直接把system.img和boot.img丢进 QFIL你会发现刷不进去——要么提示找不到烧录脚本要么直接报错。原因很简单QFIL 不认裸镜像它认的是 XML 脚本驱动的烧录包。一个完整的高通 QFIL 刷机包目录结构是这样的刷机包/ ├── rawprogram0.xml ← 核心描述每个镜像的烧录位置和参数 ├── patch0.xml ← 分区表修补根据实际存储大小调整分区 ├── partition.xml ← 分区表定义各分区的名称、大小、类型 ├── prog_emmc_firehose_xxx.elf ← Firehose 协议通信程序 ├── gpt_main0.bin ← 主 GPT 分区表 ├── gpt_backup0.bin ← 备份 GPT 分区表 ├── system.img ← 系统镜像可能被分割成多个片段 ├── boot.img ← 启动镜像 ├── userdata.img ← 用户数据镜像 └── ... ← 其他镜像这篇就来讲清楚编译产物怎么变成这个刷机包。二、刷机包核心文件详解2.1 partition.xml — 分区表定义partition.xml定义了设备存储的分区布局包括每个分区的名称、大小、类型和属性。?xml version1.0 encodingUTF-8?partition_definition!-- 分区表基础信息 --device_infosector_size512/sector_size!-- 扇区大小eMMC 通常为 512 --total_sectors61071360/total_sectors!-- 总扇区数 --storage_typeeMMC/storage_type!-- 存储类型eMMC 或 UFS --/device_infopartitionnamesbl1/name!-- 分区名称 --size1048576/size!-- 分区大小字节 --typeDEA0BA2C-CBDD-4805-B4F9-F428251C3E98/type!-- GPT 分区类型 GUID --bootabletrue/bootable!-- 是否可启动 --filenamesbl1.mbn/filename!-- 对应的镜像文件名 --/partitionpartitionnamesystem/namesize2684354560/size!-- 2.5GB --type97D7B011-54DA-4835-B3C4-917AD6E73D74/typebootablefalse/bootablefilenamesystem.img/filename/partitionpartitionnameuserdata/namesize0/size!-- 0 表示自动扩展到剩余空间 --type1B81E7E6-F50D-419B-A739-2AEEF8DA3335/typebootablefalse/bootablefilenameuserdata.img/filename/partition/partition_definition关键设计userdata分区的size设为0表示自动扩展到存储设备剩余空间。这是高通平台的标准做法——userdata不需要固定大小patch0.xml会在刷写时根据实际存储容量自动计算。2.2 rawprogram0.xml — 烧录程序脚本rawprogram0.xml是 QFIL 的核心指令文件描述了每个镜像文件要烧到哪个物理扇区、以什么方式烧。?xml version1.0 encodingUTF-8?data!-- system 分区的写入指令 --programSECTOR_SIZE_IN_BYTES512file_sector_offset0!--从文件的第0个扇区开始读--filenamesystem.img!-- 镜像文件名 --labelsystem!-- 分区标签 --num_partition_sectors5242880!-- 分区占用的扇区数 --physical_partition_number0!-- 物理分区号LUN --size_in_KB2621440!-- 分区大小KB --sparsetrue!-- 是否 sparse 格式 --start_sector131072!-- 起始扇区号 --/!-- boot 分区的写入指令 --programSECTOR_SIZE_IN_BYTES512file_sector_offset0filenameboot.imglabelbootnum_partition_sectors131072physical_partition_number0size_in_KB65536sparsefalsestart_sector2048//data每个program元素的关键属性属性含义典型值start_sector写入起始扇区由 GPT 分区表决定num_partition_sectors写入扇区数分区大小 / 扇区大小filename镜像文件名system.imgsparse是否 sparse imagesystem/userdata 为 trueboot 为 falsefile_sector_offset文件内偏移扇区通常为 0分包时不为 0physical_partition_number物理分区号eMMC 通常为 0关键设计sparsetrue告诉 Firehose 协议这个镜像文件是 sparse 格式。Firehose 在写入时会解析 sparse header跳过 DONTCARE chunk只写入有效数据。如果标记错了比如 boot.img 是 raw 格式却标了 sparse“true”就会触发Invalid sparse file format at header错误。2.3 patch0.xml — 分区表修补patch0.xml的职责是在烧录完成后修补 GPT 分区表确保分区表校验和正确以及userdata等自动扩展分区被正确填充到实际存储末尾。?xml version1.0 encodingUTF-8?data!-- 修补主 GPT 头部 --patchSECTOR_SIZE_IN_BYTES512filenamegpt_main0.binphysical_partition_number0size_in_KB16start_sector0byte_offset32valueCRC32whatUpdate-CRC/!-- 修补 userdata 分区大小 --patchSECTOR_SIZE_IN_BYTES512filenamephysical_partition_number0start_sector0whatUpdate-last-partition//data关键设计patch0.xml中的Update-last-partition指令是动态计算userdata分区大小的关键。它读取实际存储的总扇区数减去所有固定分区占用的扇区将剩余空间全部分配给最后一个分区。三、打包脚本从编译产物到刷机包3.1 打包流程总览高通平台的标准打包流程编译产物out/target/product/xxx/ │ ├── system.img ──────────────┐ ├── boot.img ────────────────┤ ├── userdata.img ────────────┤ ├── vendor.img ──────────────┤ └── ... │ ▼ ┌──────────────────────┐ │ update_common_info.py │ ← 高通官方打包脚本 │ - 分割大镜像 │ │ - 更新 GPT 分区表 │ │ - 生成 rawprogram.xml │ └──────────────────────┘ │ ▼ ┌──────────────────────┐ │ pack.sh │ ← 自定义打包脚本 │ - 链接镜像文件 │ │ - 调用 update_common │ │ - 收集所有文件打包 │ └──────────────────────┘ │ ▼ 刷机包.zip关键设计打包流程分两层——底层是高通官方的update_common_info.py负责镜像分割和 XML 生成上层是自定义的pack.sh负责收集文件和最终打包。这种分层设计便于在不同项目间复用打包逻辑。3.2 镜像文件分割对于大镜像如system.img高通工具会将其分割成多个小文件避免单个文件过大导致传输失败# 分割后的 system.img 文件system.img_0# 第 0 个片段system.img_1# 第 1 个片段system.img_2# 第 2 个片段对应的rawprogram0.xml中也会生成多个program元素!-- system 分片 0 --programfile_sector_offset0filenamesystem.img_0.../!-- system 分片 1 --programfile_sector_offset262144filenamesystem.img_1.../!-- system 分片 2 --programfile_sector_offset524288filenamesystem.img_2.../关键设计镜像分割是高通为了解决大文件传输限制而设计的。file_sector_offset告诉 Firehose 从文件的第几个扇区开始读数据写入到start_sector file_sector_offset对应的物理位置。每个分片独立写入互不干扰。3.3 自定义打包脚本示例#!/bin/bash# pack.sh — 将编译产物打包为 QFIL 刷机包ROOT$(pwd)LNX_OUTout/target/product/msm8974PACK_DIR$ROOT/flash_package# 1. 创建打包目录mkdir-p$PACK_DIR# 2. 链接关键镜像文件cp$LNX_OUT/boot.img$PACK_DIR/cp$LNX_OUT/system.img$PACK_DIR/cp$LNX_OUT/userdata.img$PACK_DIR/cp$LNX_OUT/recovery.img$PACK_DIR/# 3. 链接分区表相关文件cpdevice/qcom/msm8974/gpt_main0.bin$PACK_DIR/cpdevice/qcom/msm8974/gpt_backup0.bin$PACK_DIR/cpdevice/qcom/msm8974/partition.xml$PACK_DIR/# 4. 执行官方打包脚本python device/qcom/common/update_common_info.py\--partition_file$PACK_DIR/partition.xml\--output_dir$PACK_DIR# 5. 打包为 zipcd$PACK_DIRzip-r../flash_package.zip ./*关键设计打包脚本的核心是收集 生成 打包三步走。先用cp收集所有需要的文件再调用高通官方脚本生成 XML最后打包成 zip。脚本中硬编码了路径实际使用时应根据项目配置动态获取。四、AOSP 编译产物与高通刷机包的对应关系AOSP 编译产物刷机包中对应文件说明out/.../boot.imgboot.img直接对应out/.../system.imgsystem.img或system.img_0/1/2可能需要分割out/.../userdata.imguserdata.img直接对应out/.../recovery.imgrecovery.img直接对应无需单独提供prog_emmc_firehose_xxx.elf高通平台专用须从厂商获取无需单独提供partition.xml由设备树定义无由工具生成rawprogram0.xml由update_common_info.py生成无由工具生成patch0.xml由update_common_info.py生成关键设计这张表是编译产物 → 刷机包的映射速查。AOSP 编译产物只能直接提供镜像文件分区表和 XML 脚本需要高通工具链生成Firehose 文件需要从芯片厂商获取。五、常见问题排查问题一分区大小不匹配编译时BOARD_SYSTEMIMAGE_PARTITION_SIZE和partition.xml中的 system 分区大小不一致导致刷写失败。解决确保BoardConfig.mk中的分区大小和partition.xml中的定义一致# BoardConfig.mk BOARD_SYSTEMIMAGE_PARTITION_SIZE : 2684354560 # 2.5GB!-- partition.xml --partitionnamesystem/namesize2684354560/size!-- 必须一致 --/partition注意分区大小不一致是最常见的刷机失败原因之一。修改BoardConfig.mk后必须同步更新partition.xml否则会导致刷写失败或分区表损坏。问题二sparse 格式标记错误rawprogram0.xml中boot.img的sparse属性误标为true导致 QFIL 报Invalid sparse file format at header。解决boot.img和recovery.img不是 ext4 镜像不是 sparse 格式sparse属性必须为false。注意只有 ext4 文件系统镜像system、vendor、userdata、cache才可能是 sparse 格式。boot 和 recovery 是自定义打包格式永远是 raw 格式。问题三Firehose 文件不匹配prog_emmc_firehose_xxx.elf和芯片型号不匹配导致 QFIL 无法建立 Sahara 协议通信。解决确认芯片型号如 MSM8974、MSM8996从厂商获取对应的 Firehose 文件。警告Firehose 文件与芯片型号强绑定不同芯片甚至不同版本的芯片都可能不兼容。使用错误的 Firehose 文件会导致 Sahara 协议握手失败设备无法进入刷机模式。六、关键文件索引文件作用partition.xml分区表定义定义各分区名称、大小、类型rawprogram0.xml烧录指令脚本描述每个镜像的写入位置和方式patch0.xml分区表修补脚本更新 CRC 校验和动态分区prog_emmc_firehose_xxx.elfFirehose 协议通信程序gpt_main0.bin/gpt_backup0.bin主/备份 GPT 分区表update_common_info.py高通官方打包脚本关键设计这些文件构成了高通刷机包的完整结构。缺少任何一个文件都会导致刷机失败。建议将这些文件统一存放在版本控制系统中便于追溯和复用。七、本篇总结本篇覆盖了从编译产物到 QFIL 刷机包的完整打包流程partition.xml定义分区布局userdata的size0表示自动扩展rawprogram0.xml是 QFIL 的核心指令文件描述每个镜像的写入扇区、大小、sparse 标记patch0.xml负责烧录后的分区表修补和 CRC 校验打包脚本将编译产物、分区表文件、Firehose 文件收集到一起生成可刷写的包下一篇我们将进入 QFIL 工具本身——从 9008 模式进入、Firehose 协议握手到实际刷机操作的完整流程。