背景与动机

手头有一块 Thundercomm 的 高通跃龙IQ-9075 DevKit,出厂自带 Ubuntu x06 固件,但内核版本无法满足项目需求。而高通官方开发板IQ-9075 EVK的 Dragonwing Ubuntu x08 镜像搭载了更新的内核(6.8.0-1064-qcom),且官方提供了完整的刷机流程

两块开发板均基于高通跃龙 IQ-9075 SoC(内部代号 KODIAK / LeMans,SA8775P),运行 Ubuntu 24.04 ARM64。既然 SoC 和 OS 一致,理论上可以将高通(Qualcomm)的EVK开发板镜像刷入Thundercomm的DevKit开发板。

开发平台对比

官方高通跃龙IQ-9075 EVK 开发板

在这里插入图片描述

Thundercomm的高通跃龙IQ-9075 DevKit

在这里插入图片描述

刷机包结构对比

Thundercomm的刷机包为 FlatBuild 格式(约 3.2 GB),Qualcomm的刷机包为 iq9_ubuntu_images 目录结构。

逐文件对比发现:

  • rawprogram1-4.xmlpatch1-4.xml 内容完全一致,分区标签、扇区偏移、分区大小无差异
  • 唯一区别在于 rawprogram0.xml 中引用的 Ubuntu 镜像文件名不同
  • 分区布局(UFS LUN0-4)完全相同,存储层面兼容

dtb.bin(64 MB 的多 DTB 容器)进行解析后,在 高通开发板镜像的设备树中找到 DTB#7Qualcomm QCS9100 Ride),其 qcom,board-id 包含 PlatformID 37、Subtype 1。后续在 Thundercomm开发板的启动日志中也看到了完全一致的标识,说明 Thundercomm开发板大概率可以使用高通开发板的镜像。

尝试一:直接刷入 QC 全套固件

将开发板拨码进入 EDL 模式(第一组第 1、6 位 ON),使用 QDL 工具刷入 高通的全套固件:

qdl.exe --storage ufs --include .\.\iprog_firehose_ddr.elf .\rawprogram\*.xml .\patch\*.xml

刷写过程顺利完成。切换启动拨码后上电,串口(COM9,115200)日志显示启动链已走通大部分:

PBL → XBL (DDR 36GB LPDDR5 @3196MHz) → UEFI → Gunyah Hypervisor → GRUB → Kernel

但在 UEFI 阶段出现异常:

[RM] Error: unexpected value in ehdr
[RM] Static VM (16) not in READY state
[RM] GearVM (16) not started
[...]
gNpaClientSSxBus cannot be created

GearVM(Gunyah Hypervisor 管理的虚拟机 #16)启动失败,NPA(Node Power Architecture)框架报错。

GRUB 菜单在串口上出现大面积乱码(gfxterm 模式输出图形终端控制字符),3 秒后自动引导进入内核,随后内核崩溃:

Unable to handle kernel paging request at virtual address ffff800080e980c0
Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP
Call trace:
 dwc3_read1+0x1c/0x38 [dwc3]
 dwc3_core_soft_reset+0x18/0xc4 [dwc3]
 dwc3_core_init+0x78/0xb94 [dwc3]
 dwc3_qcom_probe+0x31c/0x470 [dwc3_qcom]

多个 CPU 随后报 soft lockup,系统挂死。

COM8 串口显示 SAIL(Safety Island)子系统运行正常,说明 SAIL 不受 UFS 刷机影响。

崩溃根因分析

表面上是 USB DWC3 驱动的页面错误,但实际崩溃链路从 UEFI 阶段就已开始:

hypvm.mbn 板级不匹配
  → GearVM ELF 加载失败(unexpected value in ehdr)
  → GearVM 未启动
  → NPA 框架缺少 GearVM 提供的资源节点
  → USB DWC3 控制器的电源/时钟域未使能
  → dwc3_read1 访问未映射寄存器(pte=0000000000000000)
  → Kernel Oops → soft lockup

GearVM 负责电源管理,由 hypvm.mbn(Gunyah Hypervisor 镜像)配置和启动。QC 的 hypvm.mbn 是为其 EVK 编译的,GearVM 的资源分配(内存映射、中断路由、设备直通)与 TC 板不匹配。

对 10 个关键固件做 MD5 校验,Thundercomm 和 高通 全部不同:

固件文件Thundercomm 大小 (B)Qualcomm 大小 (B)相同?
hypvm.mbn1,765,4081,773,600
tz.mbn3,789,8243,953,664
uefi.elf4,005,7764,202,384
xbl.elf872,768876,864
xbl_config.elf401,536401,536
dtb.bin67,108,86467,108,864
aop.mbn208,976208,976
devcfg_iot.mbn61,37661,376
cpucp.elf114,848114,848
shrm.elf36,89636,896

即使文件大小相同,哈希也不同——每个固件都是板级定制的。

尝试二:混合方案(Thundercomm固件 + Qualcomm的系统镜像)

问题出在 NHLOS 固件与硬件不匹配,Ubuntu 系统本身是通用的。因此改用Thundercomm的固件做硬件初始化,仅刷入 Qualcomm 的 Ubuntu 镜像。

组装混合刷机包

目录结构如下:

hybrid_flash/
├── [Thundercomm] xbl.elf, uefi.elf, hypvm.mbn, tz.mbn ...   # 全套 NHLOS 固件
├── [Thundercomm] dtb.bin                                     # 设备树
├── [Thundercomm] rawprogram1-4.xml, patch1-4.xml            # 分区配置
├── [Thundercomm] gpt_main/*.bin, gpt_backup/*.bin            # GPT 分区表
├── [Thundercomm] prog_firehose_ddr.elf                       # Firehose 烧录器
├── [改] rawprogram0.xml                             # filename 指向 QC 镜像
└── [Qualcomm] iot-qualcomm-dragonwing-...-x08.img        # Ubuntu 系统镜像

使用 PowerShell 从 Thundercomm 的 FlatBuild zip 中提取固件(跳过 8.7 GB 的 Ubuntu 镜像),修改 rawprogram0.xml 中的镜像文件名,并复制 Qualcomm镜像到同目录:

# 从 Thundercomm zip 提取(跳过大镜像)
Add-Type -Assembly System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::OpenRead($tcZipPath)
foreach ($entry in $zip.Entries) {
    if ($entry.Name -ne "" -and $entry.Name -notlike "*.img") {
        [System.IO.Compression.ZipFileExtensions]::ExtractToFile(
            $entry, (Join-Path $hybridDir $entry.Name), $true)
    }
}
$zip.Dispose()

# 修改 rawprogram0.xml 中的镜像引用
(Get-Content "$hybridDir\rawprogram0.xml") -replace
    'filename="ubuntu-24.04-classic-desktop-arm64-x06.img"',
    'filename="iot-qualcomm-dragonwing-classic-desktop-2404-x08-20260210.4096b.img"' |
    Set-Content "$hybridDir\rawprogram0.xml"

# 复制 Qualcomm 镜像(8.9 GB)
Copy-Item "$qcDir\iot-qualcomm-dragonwing-*.img" $hybridDir

刷机与结果

cd hybrid_flash
qdl.exe --storage ufs --include .\prog_firehose_ddr.elf .\rawprogram0.xml .\rawprogram1-4.xml .\patch\*.xml

启动成功:

  • Thundercomm 的 hypvm.mbn 正确初始化 Gunyah Hypervisor
  • GearVM 正常启动
  • NPA 框架完整
  • USB DWC3 控制器不再崩溃
  • Qualcomm 的 Ubuntu x08 系统正常引导至登录界面

修复首次登录密码问题

串口出现 Ubuntu 登录提示后,输入默认账号密码(ubuntu / ubuntu):

You are required to change your password immediately (administrator enforced).
Changing password for ubuntu.
Current password:
Authentication token manipulation error

密码修改失败,原因是根文件系统以 ro 模式挂载。GRUB 内核命令行默认带 ro 参数,而 PAM 强制改密在 systemd 重新挂载为 rw 之前就已触发。

串口 GRUB 菜单换行错乱,无法手动编辑启动项。因此选择直接修改磁盘镜像中 EFI 分区的 grub.cfg

镜像 GPT 结构

Qualcomm 的 Ubuntu 磁盘镜像使用 4096 字节扇区的 GPT:

分区LBA 范围大小格式
efi256 - 131,327512 MBFAT32
persist131,328 - 139,00730 MBext4
writable139,008 - 2,266,6418.3 GBext4

EFI 分区中 /EFI/ubuntu/grub.cfg 只有 117 字节,作用是搜索 writable 分区后加载真正的 /boot/grub/grub.cfg

search.fs_uuid d9ceb9b7-d25f-4edc-b06c-cee825cb7519 root
set prefix=(root)'/boot/grub'
configfile $prefix/grub.cfg

修改 EFI grub.cfg

编写 Python 脚本 patch_efi_grub.py,直接操作镜像二进制:

  • 解析 GPT,定位 EFI 分区
  • 解析 FAT32 BPB,找到集群参数
  • 遍历目录项,定位 grub.cfg
  • 覆写文件内容,更新目录项中的文件大小字段

FAT32 集群大小为 4096 字节,原文件 117 字节,新内容 285 字节,单集群足够容纳。

替换后的 grub.cfg 绕过原始 /boot/grub/grub.cfg,直接定义启动项:

search.fs_uuid d9ceb9b7-d25f-4edc-b06c-cee825cb7519 root
set prefix=(root)'/boot/grub'
set timeout=5

menuentry 'Ubuntu (rw fix)' {
    linux (root)/boot/vmlinux root=UUID=d9ceb9b7-d25f-4edc-b06c-cee825cb7519 rw console=ttyMSM0,115200n8 earlycon
    initrd (root)/boot/initrd.img
}

关键变化:内核参数 rorw,根文件系统可写挂载。

$ python patch_efi_grub.py patch
找到 grub.cfg:
Cluster: 1149, Size: 117 bytes
Dir entry offset in image: 0x222040

grub.cfg 已更新!新大小:285 bytes
验证成功!

只刷磁盘镜像

修改后只需重刷 LUN0(磁盘镜像),固件保持不动:

qdl.exe --storage ufs --include .\prog_firehose_ddr.elf .\rawprogram0.xml

注意只指定 rawprogram0.xml,不包含 rawprogram1-4.xmlpatch/*.xml

重启后串口显示 Ubuntu (rw fix) 菜单项,5 秒后自动启动。密码修改成功,登录系统后执行 sudo update-grub 恢复正常 GRUB 配置。

总结

分区兼容 ≠ 固件兼容

两个包的 rawprogram*.xml 完全一致,但 NHLOS 固件是板级定制的。即使同一颗 SoC,不同厂商的板子在电源管理、Hypervisor 配置、设备树等方面仍存在差异。

Hypervisor 层的级联故障

使用错误的 hypvm.mbn 不会阻止 PBL/XBL/UEFI 启动,但 GearVM 启动失败会导致 NPA 框架不完整,进而使依赖 NPA 的外设(如 USB DWC3)在内核阶段崩溃。排查此类问题应从启动日志的最早异常开始追溯,而非仅关注内核 Oops。

混合方案可行

对于同 SoC 不同开发板,「厂商固件 + 第三方 OS 镜像」的组合是可行的,前提是分区布局一致。本质上是将 SoC 固件初始化与操作系统解耦。

选择性刷写

QDL 支持按 rawprogram*.xml 选择性刷写。只更新磁盘镜像时,仅指定 rawprogram0.xml 即可,省去固件分区的写入时间,在反复调试镜像配置时非常实用。

附录

启动链路

PBL (ROM) → XBL → UEFI → Gunyah Hypervisor → GRUB → Linux Kernel → Ubuntu
                              ↑
                          GearVM / PVM

NHLOS 固件清单

文件功能
xbl.elf可扩展引导加载器,DDR/UFS 初始化
uefi.elfUEFI 固件,设备枚举
tz.mbnTrustZone 安全世界 (EL3)

实测时间:2026年3月
固件版本

  • Thundercomm FlatBuild x06(2024.12.10)
  • Qualcomm Dragonwing x08(2026.02.10)

如果你跨开发板刷写镜像的过程中遇到任何问题,欢迎在评论区留言讨论!

如果你觉得本文对你有帮助,欢迎点赞、收藏、关注!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐