系列目录 :Android 7系统编译与刷机(一):源码环境搭建与完整编译流程 | (二):Image镜像生成机制深度解析 | (三):从编译产物到高通刷机包 | (四):QFIL刷机工具详解 | (五):特定分区镜像的单独导出 | (六):特定分区刷入实战
一、为什么需要深入理解 QFIL
在前面三篇中,我们完成了源码编译、理解了镜像生成机制、打包好了刷机包。现在到了最后一步:把刷机包烧进设备。
但 QFIL 不是那种"打开文件 → 点开始 → 等进度条走完"的傻瓜工具。如果你不理解它的底层协议和操作模式,会遇到各种诡异问题:
- 设备插上 USB 后 QFIL 毫无反应------明明驱动都装了,到底哪里出了问题?
- 刷机到一半报
Sahara Fail,然后设备就彻底黑屏了------还能救回来吗? - 只想刷
system分区,不想动userdata------怎么配置?
这篇就来把 QFIL 的底层协议、操作模式、常见问题逐一拆解。
二、QFIL 的底层协议:Sahara + Firehose
QFIL 不是一个简单的"文件传输工具",它背后是一套完整的高通设备通信协议栈,分为两个阶段:
设备上电 / 进入 EDL 模式
│
▼
┌──────────────────────────┐
│ 阶段一:Sahara 协议 │
│ - 握手,确认设备身份 │
│ - 下载 Firehose 程序到 │
│ 设备内存 │
│ - 设备端出现 "Qualcomm │
│ HS-USB QDLoader 9008" │
└──────────────────────────┘
│
▼
┌──────────────────────────┐
│ 阶段二:Firehose 协议 │
│ - 解析 rawprogram0.xml │
│ - 按扇区写入镜像数据 │
│ - 执行 patch 修补 │
│ - 复位设备 │
└──────────────────────────┘
2.1 Sahara 协议 --- 握手与引导
Sahara 协议是第一阶段,负责建立 PC 与设备的通信通道,并将 Firehose 程序下载到设备内存中运行。
关键步骤:
- 设备上电 → 设备 ROM 中的 PBL(Primary Boot Loader)启动,检测 USB 连接
- PBL 发送 Hello → 设备向 PC 发送
Sahara Hello包,包含设备序列号和芯片型号 - PC 回应 Hello → QFIL 确认设备身份,准备发送 Firehose 程序
- 下载 Firehose → QFIL 将
prog_emmc_firehose_xxx.elf发送到设备内存 - 跳转执行 → PBL 跳转到 Firehose 程序,进入第二阶段
关键设计 :Sahara 协议中的 Firehose 程序是单片机的程序 (通常以
.elf或.mbn结尾),运行在设备的早期内存环境中,不依赖任何操作系统。这就是为什么即使设备完全无法启动,只要 PBL 还在,就能通过 EDL 模式救砖。
2.2 Firehose 协议 --- 分区操作
Firehose 程序启动后,PC 通过 Firehose 协议向设备发送 XML 指令。支持的指令类型:
| 指令 | 作用 | 对应 XML 标签 |
|---|---|---|
| Program | 写入镜像数据到指定扇区 | <program> |
| Read | 读取指定扇区数据 | <read> |
| Erase | 擦除指定扇区 | <erase> |
| Patch | 修补分区表/校验和 | <patch> |
| NOP | 空操作,用于连接测试 | <nop> |
| Reset | 复位设备 | <reset> |
关键设计 :Firehose 协议支持
Read指令,这意味着 QFIL 不仅可以"写",还可以"读"------这是后面导出分区镜像的技术基础。
三、进入 EDL 模式的三种方法
EDL(Emergency Download Mode)是 Sahara 协议生效的前提。设备必须进入 EDL 模式,PC 才能与之通信。
方法一:ADB 命令(推荐,无需拆机)
bash
adb reboot edl
关键设计:ADB 命令是最安全的 EDL 入口方式,不需要拆机,不需要物理按键组合。前提是设备能正常启动进入系统,且已开启 USB 调试。
方法二:按键组合(部分机型)
不同品牌有不同的按键组合,常见的有:
| 品牌 | 按键组合 |
|---|---|
| 小米 | 音量下 + 电源键,长按 10 秒 |
| 一加 | 音量上 + 音量下 + 电源键 |
| 联想 | 音量上 + 电源键 |
注意:按键组合与具体机型高度相关,须查阅设备的技术手册。按键方式适合设备无法启动但还能响应按键的场景。
方法三:硬件短接(终极方案)
当设备完全黑屏、无法进入任何模式时,需要拆机短接主板的测试点。
警告:硬件短接有短路风险,仅在设备完全"变砖"且其他方法无效时使用。操作前断开电池连接,短接时使用绝缘镊子。
关键设计:三种 EDL 入口方式按优先级选择:ADB 命令(最安全)> 按键组合(设备无法启动时)> 硬件短接(终极方案)。优先使用非侵入式方法,避免拆机风险。
四、QFIL 操作全流程
4.1 准备工作
必备文件:
| 文件 | 用途 | 来源 |
|---|---|---|
prog_emmc_firehose_xxx.elf |
Firehose 通信程序 | 厂商提供,需匹配芯片型号 |
rawprogram0.xml |
烧录指令脚本 | 由打包脚本生成 |
patch0.xml |
分区表修补脚本 | 由打包脚本生成 |
| 各镜像文件 | 系统数据 | 编译产物 |
关键设计 :Firehose 程序必须与芯片型号精确匹配,不同芯片甚至不同版本的芯片都可能不兼容。
rawprogram0.xml和patch0.xml由打包脚本生成,包含了分区布局、镜像路径、写入参数等关键信息。
驱动确认 :设备管理器中应出现 Qualcomm HS-USB QDLoader 9008(EDL 模式)或 Qualcomm HS-USB Diagnostics 900E(诊断模式)。
4.2 操作步骤
步骤一:连接设备
└── USB 连接 PC,设备进入 EDL 模式
└── QFIL 中端口显示 "Qualcomm HS-USB QDLoader 9008 (COMx)"
步骤二:选择配置
└── Select Build Type: Flat Build
└── Select Programmer: 浏览选择 prog_emmc_firehose_xxx.elf
└── Load XML: 选择 rawprogram0.xml 和 patch0.xml
步骤三:开始烧录
└── 点击 Download
└── 等待 Sahara 握手完成(通常 3-5 秒)
└── 等待 Firehose 写入完成(取决于镜像大小,通常 2-5 分钟)
步骤四:完成验证
└── 状态栏显示 "Download Succeed"
└── 设备自动重启
关键设计:QFIL 操作流程的核心是"连接 → 配置 → 烧录 → 验证"四步走。Sahara 握手通常只需 3-5 秒,Firehose 写入时间取决于镜像大小,通常 2-5 分钟。
4.3 配置示意图
QFIL 主界面
┌────────────────────────────────────┐
│ Select Port │
│ ┌──────────────────────────────┐ │
│ │ Qualcomm HS-USB QDLoader 9008│ │
│ └──────────────────────────────┘ │
│ │
│ Select Build Type │
│ ● Flat Build ○ Meta Build │
│ │
│ Select Programmer │
│ ┌──────────────────────────────┐ │
│ │ .../prog_emmc_firehose_xxx.elf│ │
│ └──────────────────────────────┘ │
│ │
│ Load XML │
│ ┌─ rawprogram0.xml ────────────┐ │
│ └─ patch0.xml ─────────────────┘ │
│ │
│ ┌──────────┐ │
│ │ Download │ ← 点击开始 │
│ └──────────┘ │
└────────────────────────────────────┘
关键设计 :QFIL 界面配置的关键是三个文件的选择------Firehose 程序(必须匹配芯片型号)、
rawprogram0.xml(烧录指令)、patch0.xml(分区表修补)。配置错误是导致刷机失败的最常见原因。
五、常见错误排查
5.1 Sahara 握手失败
现象 :QFIL 日志显示 ERROR: function: sahara_rx_data: ... Sahara protocol error。
原因与解决:
| 原因 | 解决方法 |
|---|---|
| Firehose 文件与芯片不匹配 | 确认芯片型号,使用正确的 elf 文件 |
| 设备未进入 EDL 模式 | 重新进入 EDL,确认设备管理器显示 9008 |
| USB 端口供电不足 | 换用主板后置 USB 口,避免使用集线器 |
| 驱动冲突 | 关闭其他手机管理软件(小米助手、华为 HiSuite 等) |
关键设计:Sahara 握手失败的根本原因是 PC 与设备无法建立通信通道。排查顺序应该是:驱动 → EDL 模式 → USB 端口 → 软件冲突。
5.2 Firehose 写入失败
现象 :QFIL 日志显示 ERROR: function: firehose_download: ... Failed to send Firehose NOP。
原因与解决:
| 原因 | 解决方法 |
|---|---|
| 分区表不匹配 | 检查 partition.xml 和 rawprogram0.xml 的扇区配置 |
| 镜像大小超过分区大小 | 减小镜像或增大 partition.xml 中的分区大小 |
| sparse 格式标记错误 | 确认 boot.img 的 sparse 属性为 false |
| 存储空间不足 | 检查 userdata 是否为"自动扩展"模式 |
关键设计:Firehose 写入失败通常与分区表配置或镜像格式有关。排查顺序应该是:分区表匹配 → 镜像大小 → sparse 格式 → 存储空间。
5.3 Invalid sparse file format at header
现象 :QFIL 在解析镜像时报 Invalid sparse file format at header。
根本原因 :rawprogram0.xml 中某个镜像的 sparse 属性标记为 true,但实际镜像文件不是 sparse 格式。sparse 文件的魔数是 0xed26ff3a(小端序存储为 3a ff 26 ed),如果文件头部没有这个魔数,QFIL 就会报错。
解决:
bash
# 检查文件是否为 sparse 格式
hexdump -C system.img | head -n 1
# 如果前 4 字节是 3a ff 26 ed,说明是 sparse 格式
# 否则是 raw 格式
# 如果有误,修正 rawprogram0.xml 中对应的 sparse 属性
关键设计 :sparse 格式标记错误是最常见的刷机失败原因之一。根本原因是
rawprogram0.xml中的sparse属性与实际镜像格式不匹配。只有 ext4 文件系统镜像(system、vendor、userdata、cache)才可能是 sparse 格式,boot 和 recovery 永远是 raw 格式。
六、QFIL 的"读"功能:分区镜像导出
除了刷入,QFIL 还支持读取设备分区数据。这在以下场景中非常有用:
- 备份设备当前的完整分区数据
- 从正常设备提取特定分区的镜像,用于研究或修复
- 在刷机前保存原始固件,方便回退
操作方式:在 QFIL 的 Tools 菜单下选择 Partition Manager,连接到设备后可以浏览所有分区,右键选择 Read 即可导出镜像。
注意 :QFIL 的 Partition Manager 读取的是 raw image,不是 sparse image。如果要将读取到的镜像挂载到 Linux 上查看内容,可以直接使用
mount -o loop。
关键设计 :QFIL 的读取功能通过 Firehose 协议的Read指令实现,绕过了 Android 操作系统的权限体系,所以不需要 root。这是 QFIL 相比 fastboot 和 dd 的独特优势。
七、关键文件索引
| 文件 | 作用 |
|---|---|
QFIL.exe |
QFIL 主程序 |
QSaharaServer.exe |
Sahara 协议服务端 |
fh_loader.exe |
Firehose 协议加载器 |
prog_emmc_firehose_xxx.elf |
Firehose 程序,需匹配芯片型号 |
rawprogram0.xml |
烧录指令脚本 |
patch0.xml |
分区表修补脚本 |
关键设计 :这些文件构成了 QFIL 刷机的完整工具链。
QFIL.exe是图形界面,底层调用QSaharaServer.exe和fh_loader.exe执行 Sahara 和 Firehose 协议。
八、本篇总结
本篇覆盖了 QFIL 工具的完整底层原理和操作流程:
- 双协议栈:Sahara(握手 + 下载 Firehose)→ Firehose(分区写入 + 修补)
- 三种 EDL 入口:ADB 命令 > 按键组合 > 硬件短接,按优先级选择
- 操作四步走:连接 → 选配置 → 下载 → 验证
- 常见错误:Sahara 握手失败(驱动/Firehose 不匹配)、Firehose 写入失败(分区表不匹配)、sparse 格式错误
下一篇我们将聚焦实战------如何单独导出特定分区(如 data 分区)的镜像,以及如何单独刷入特定分区,实现"模块级"的刷写操作。