较早时期就有技术大佬研究,推出适用于高通主板的 U-Boot 第一个二进制版本。
印象里是基于 2024.10-rc2 版本,所有列出的平台均支持核心功能(EFI、从内部存储启动、从 USB 启动),部分主板有条件地支持 USB 小工具模式、SD 卡访问和以太网。此外,RB5 主板可以通过特殊的 U-Boot 构建替换原有的虚拟机管理程序映像,从而支持 KVM 启动。
本文将引导您完成二进制版本的刷新、从源代码构建,并希望提供一些有关 Qualcomm 的启动过程以及 U-Boot 如何融入 Qualcomm 生态系统的背景信息。
Linaro 继续其先前的努力,在机器人开发平台上提供统一的启动流程,并通过与高通的物联网团队合作,Linaro 工程师现在已经支持在 7 个平台上使用 U-Boot 进行 EFI 启动(并且还在不断增加!)。
假设您的发行版启用了正确的内核模块,您现在可以简单地启动闪存到 USB 记忆棒的映像,甚至将其安装到内部存储(但请注意不要擦除带有 eMMC 的设备上分区表,您必须使用自定义分区并且只触及 rootfs 和 ESP 分区!)。
安装方法
有 3 种方法可以在 Qualcomm 主板上安装 U-Boot: 将其刷入启动分区并从 ABL 进行链式加载 二进制修补 xbl 以用 U-Boot 替换 EDK2 映像 将其刷入 hyp 分区并在 EL2 中启动 Linux(使用 KVM)
哪个平台支持其中哪个以及哪个对您有意义取决于几个不同的因素(但请注意,这里提到的问题有望在将来得到改进):如果您选择选项 a,则安全启动目前只是很简单,因为可以使用 custom_avb_key。然而,这可能会改变,如果您对此感兴趣,请与我们联系。U-Boot 中的快速启动实现不符合高通的标准,它的速度较慢并且根本不支持 UFS 存储。这可能会使选项 b 和 c 变得没有吸引力,尽管您可能对 PXE 和 HTTP 支持作为替代方案感兴趣。KVM 支持是实验性的,并且会破坏 DSP(以及可能的其他东西)。目前,仅为 RB5 提供 KVM 二进制文件,因为它使用 PCIe WiFi 并且仅失去对音频和 NPU 的支持。
支持的平台
具体支持状态取决于设备。当前的集成分支不支持 SM8550/SM8650 上的 UFS。其他设备可以从内部存储或 USB 启动。

刷写 U-Boot
可以从https://git.codelinaro.org/linaro/qcomlt/u-boot/-/releases下载版本
所有方法的安装过程几乎相同。对于 boot、uefi 或 hyp 镜像,只需使用 fastboot 或 EDL 刷入相应的分区即可。对于 RB1/2/3/5 上的 xbl 镜像,您需要获取一个原版 xbl 分区镜像,并使用 patchxbl.py 和 qtestsign.py,具体操作步骤如下。

从源代码构建
U-Boot 可从https://git.codelinaro.org/linaro/qcomlt/u-boot获取,并按如下方式编译。更多常规信息和依赖项,请参阅 U-Boot 文档。更多 Qualcomm 特定文档可在此处找到。链式加载时,您还需要 android-tools 来执行 mkbootimg 命令。
cd u-boot
git reset --hard QU-0.1.0 # check the latest tag, or use master
make CROSS_COMPILE=aarch64-linux-gnu- O=.output qcom_defconfig
make CROSS_COMPILE=aarch64-linux-gnu- O=.output -j$(nproc) DEVICE_TREE=qcom/qrb5165-rb5```将上面的 DEVICE_TREE 替换为您的主板的 DTB 名称,例如对于 RB5,它将是 DEVICE_TREE=qcom/qrb5165-rb5。制作启动映像现在我们必须获取生成的 U-Boot 二进制文件和 DTB,并将它们放入 Android 启动映像中,就像 Linux 内核一样(查看运行文件 .output/u-boot.bin 时会发生什么)。
gzip .output/u-boot-nodtb.bin -c > .output/u-boot-nodtb.bin.gz
cat .output/u-boot-nodtb.bin.gz .output/dts/upstream/src/arm64/qcom/qrb5165-rb5.dtb > /tmp/uboot-dtb
mkbootimg --kernel_offset '0x00008000' --pagesize '4096' --kernel /tmp/uboot-dtb -o .output/u-boot.img生成的镜像现在可以像任何常规 Android 启动镜像一样启动或烧写。再次提醒,请更改 DTB 以匹配您的设备。自定义 XBL 镜像:将 U-Boot 构建为 XBL 镜像的方法相同,构建完成后,我们需要将其修补到现有的 XBL 镜像中。您可以直接从您的开发板中拉取该镜像,或者对于 RBx 开发板,下载其中一个 Linaro 救援包(FIXME:链接救援包)。
您需要从 qtestsign 仓库安装 qtestsign 和 patchxbl。这两个工具只能用于为 rb3、rb5、db410c 和 db820c 的镜像签名,不适用于使用 v7 签名的 rb1 和 rb2 镜像。目前,您需要访问 sectools 或类似程序才能进行此操作。对于 rb3,应将 -v5 传递给 qtestsign;对于 db410c,应将 -v3 传递给 qtestsign。
git clone https://github.com/msm8916-mainline/qtestsign
ln -s $PWD/qtestsign.py ~/.local/bin/qtestsign
ln -s $PWD/qtestsign.py ~/.local/bin/patchxbl或者使用EDL进行缓存
RB3 Gen2 UEFI
对于该板,EDK2/U-Boot 从专用分区加载,可以按如下方式构建和刷新:
make CROSS_COMPILE=aarch64-linux-gnu- O=.output qcm6490_defconfig
make CROSS_COMPILE=aarch64-linux-gnu- O=.output -j$(nproc)
qtestsign -v6 hyp -o .output/u-boot.mbn .output/u-boot.elf
fastboot flash uefi_a .output/u-boot.mbnRB3 Gen 2 UEFI
对于虚拟机管理程序的情况,我们必须将 U-Boot 构建为 ELF 二进制文件,其加载偏移量与原始 hyp.elf 相同。仅包含 rb5 的配置片段,但它在 rb1/2/3 上的工作方式类似。
make CROSS_COMPILE=aarch64-linux-gnu- O=.output qcom_defconfig hyp-sm8250.config
make CROSS_COMPILE=aarch64-linux-gnu- O=.output -j$(nproc) DEVICE_TREE=qcom/qrb5165-rb5
qtestsign -v6 hyp -o .output/u-boot-hyp.mbn .output/u-boot.elf
fastboot flash hyp .output/u-boot-hyp.mbn深入了解高通的启动流程
高通的 Android 启动流程和 Chainloading

高通设备上的标准启动流程大致如下(来源:高通安全启动和图像认证)。
图中的数字 7 实际上是 EDK2 启动的地方,它执行自己的驱动程序初始化,然后运行已经加载到内存中的 ABL EFI 应用程序(LinuxLoader.efi)。
最初,我们利用 U-Boot 的功能来实现对 Linux 内核的支持,这样我们就可以在现有的启动链之后运行它(也就是链式加载)。ABL 认为它能正常启动到 Linux 系统,但啊哈!它其实只是另一个引导加载程序而已 :D
这对于启动和开发非常有帮助,因为我们可以在这些主板上使用我们熟悉的 fastboot 启动模型。然而,这并不能提供特别无缝的用户体验;它会导致启动时间变慢,并且仍然需要处理 ABL 如何修补 DTB、尝试从 dtbo 加载覆盖层,甚至执行其他烦人的操作,例如填充内核命令行。简而言之:它并不是理想的模型。U-Boot 作为主引导加载程序:XBL 值得庆幸的是,我们可以做得更好。由于 XBL 实际上由多个组件组成:sbl1(EL1 中的辅助引导加载程序)负责加载 TrustZone、Hypervisor 和 EDK2 二进制文件(分别来自 tz、hyp 和 xbl 分区),然后它会设置 HOB 列表并移交给 EDK2。这让我们可以非常轻松地更进一步,用 U-Boot 替换 EDK2 二进制文件!
XBL 图像实际上只是一个 ELF 二进制文件,具有特定于平台的加载地址,这是 readelf 的输出,其中带有相关部分的注释。
$ aarch64-linux-gnu-readelf -Wl rb5-xbl.elf
Elf file type is EXEC (Executable file)
v-- As described in the diagram above, the entrypoint is in XBL_SEC
Entry point 0x148157a8
There are 15 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
v-- These first two are just signatures
NULL 0x000000 0x0000000000000000 0x0000000000000000 0x000388 0x000000 0
NULL 0x001000 0x000000009fe65000 0x000000009fe65000 0x001c78 0x002000 0x1000
v-- This is XBL_SEC, it's actually a whole other ELF binary!
LOAD 0x003000 0x0000000014815000 0x0000000014815000 0x05d010 0x05d010 R E 0x1000
LOAD 0x060010 0x000000001487e000 0x000000001487e000 0x000000 0x003000 RW 0x1000
LOAD 0x060010 0x0000000014873000 0x0000000014873000 0x00a2b2 0x00a2b2 R 0x1000
LOAD 0x06a2d0 0x0000000014881000 0x0000000014881000 0x01538c 0x01538c RW 0x1000
LOAD 0x07f660 0x0000000014897000 0x0000000014897000 0x000000 0x02c5e8 RW 0x1000
LOAD 0x07f660 0x0000000080704000 0x0000000080704000 0x000000 0x028f38 RW 0x1000
v-- This is EDK2
LOAD 0x07f660 0x000000009fc00000 0x000000009fc00000 0x265000 0x265000 RWE 0x1000
LOAD 0x2e4660 0x0000000014698000 0x0000000014698000 0x016000 0x016000 R E 0x1000
LOAD 0x2fa660 0x000000008072f000 0x000000008072f000 0x04f404 0x04f404 R E 0x1000
LOAD 0x349a70 0x00000000807a0000 0x00000000807a0000 0x007468 0x05c530 RW 0x1000
LOAD 0x350ee0 0x0000000080791000 0x0000000080791000 0x000000 0x001fe0 RW 0x1000
LOAD 0x350ee0 0x00000000146ae000 0x00000000146ae000 0x00208c 0x00208c R E 0x1000
LOAD 0x352f70 0x00000000146b0800 0x00000000146b0800 0x002b48 0x002b48 RW 0x1000所以我们只需要用 U-Boot 替换这一段代码就行了,对吧?没错!就是这么简单……不过,二进制文件确实需要再次进行哈希值计算和签名,幸运的是,大多数平台(至少是 2022 年以后的平台)都可以使用开源工具完成。实际的签名验证在开发板上是禁用的,因为它们没有编程公钥(通常称为“安全启动关闭”),但仍然需要有一个有效的 Qualcomm 镜像头文件和正确的哈希值。qtestsign 工具支持 Qualcomm 固件签名格式 v6 及更高版本(最新版本是 v7,更多信息请参阅 Qualcomm 发布的这份技术概述文档)。
qtestsign 工具现在还包含一个 patchxbl.py 程序,它执行我们上面描述的操作,您为它提供一个用于您的主板的 XBL 图像、一个兼容的 U-Boot 二进制文件(带有嵌入式 DTB),它用 U-Boot 二进制文件替换包含 EDK2 的部分。
最后,使用 qtestsign 再次对生成的图像进行签名并将其刷入 xbl 分区,然后当您启动主板时,您将看到类似以下内容:
B - 1011898 - SBL1, End
D - 937631 - SBL1, Delta
S - Flash Throughput, 262000 KB/s (7544028 Bytes, 28714 us)
S - DDR Frequency, 1555 MHz
<debug_uart>
U-Boot 2024.07-rc4-00682-g73d0e577fe7b (Jun 25 2024 - 16:17:33 +0200)
Model: Qualcomm Technologies, Inc. Robotics RB5
DRAM: 8 GiB (effective 7.9 GiB)U-Boot 作为主引导加载程序:RB3 Gen 2
高通的全新 RB3 Gen 2 主板是首款支持Qualcomm Linux 的主板,其构建版本与上游 Yocto 非常接近。它开箱即用 systemd-boot 存根启动,并且可以运行 systemd-boot 或 GRUB。然而,原厂引导加载程序包含大量自定义设置、自定义 DTB 存储以及由引导加载程序进行的各种非标准 DTB 更改。

从长远来看,预计高通的 EDK2 将兼容 SystemReady。但人们对 U-Boot 的兴趣依然浓厚(一方面是为了在短期内提供标准的 EFI 接口,另一方面也是为了给高通的客户提供更多样的功能)。
作为这种新的以上游为重点的方法的一部分,RB3 Gen 2 的固件架构发生了许多变化,对于我们来说,最有趣的是引导加载程序的 EDK2“xbl_core”阶段已移至其自己的分区(名为 uefi_a(和 _b))。它包含一个 Qualcomm 签名的 ELF 映像,带有一个程序头,即 EDK2 映像。
这项简单的更改使得 U-Boot 作为第一阶段引导加载程序在此主板上支持变得非常简单。我们只需将 U-Boot 构建为 ELF 格式,使用 qtestsign 签名,然后将其刷入 uefi 分区即可(如上所述)。
请注意,您需要使用 EDL 模式来恢复库存固件。
RB3 Gen 2 的初始上游支持正在等待此处的上游支持。对于 UFS 支持,建议使用 CodeLinaro 集成分支。
虚拟机
使用 KVM 启动实际上比构建自定义 XBL 镜像更简单。它与 RB3 Gen 2 的方法非常相似。上图中略去了额外的步骤。在 sbl1 和 edk2 之间,虚拟机管理程序启动时,它之前已经在 sbl1 中加载并进行了身份验证。sbl1 实际上不是直接跳转到 EDK2,而是进行 SMC 调用(进入 trustzone),并传入 EDK2 的起始地址。然后,trustzone 进行一些初始化,然后跳转到虚拟机管理程序,最终跳转到起始地址。此时,我们已经“启动”了主虚拟机。
这使得在大多数平台上以 EL2 启动 U-Boot 变得非常容易,因为虚拟机管理程序只是一个具有单个加载部分的 ELF 二进制文件,我们可以直接用我们的 U-Boot 二进制文件替换它,从而尽早并在 EL2 中接管启动链。
在 EL2 中运行时,最显著的区别在于我们破坏了用于加载和验证 DSP 固件映像的外设映像加载器 (PIL) 机制。因此,音频、计算和传感器 DSP 不可用。
Qualcomm Chromebook 平台也运行在 EL2 架构下,它们完全绕过音频 DSP,直接驱动音频子系统。目前关于如何在这种启动方式下支持计算或传感器 DSP 尚不清楚。
固件镜像验证方式的变化意味着此方法仅适用于 RB3 和 RB5 主板。RB1、RB2 和 RB3 Gen 2 都设置了额外的检查,因此无法替换虚拟机管理程序。但这并不意味着 KVM 完全不可能……
让我们启动一些 Linux
Debian 内核最近已合并对 Qualcomm 平台的支持,预计将包含在 6.10 中。Fedora 最近也在其内核配置中启用了必要的驱动程序。诚然,尽管上游硬件支持相对较好,但许多发行版内核中仍然明显缺少启用的 Qualcomm 驱动程序。希望通过 EFI 友好的引导加载程序,我们可以扭转这一局面,让更多发行版能够在 Qualcomm 主板上顺利运行。
如果您计划安装像 Fedora 这样的发行版,仍然需要注意一些不幸的警告:
大多数高通物联网开发板大量使用内部存储(eMMC 或 UFS)来存储代码和数据,这对于“非 HLOS”(高级操作系统,又称引导加载程序、信任区以及 DSP 和协处理器的固件)至关重要。在 rb3 和 rb5 等采用 UFS 存储的开发板上,重要的分区位于单独的磁盘(SCSI 术语中称为逻辑单元 (Logical Unit))上,第一个磁盘用于操作系统。在这些平台上,擦除(通常)第一个磁盘上的分区表是完全安全的,因为它是最大的磁盘。您可以让发行版安装程序自行执行此操作(请注意,智能手机可能并非如此,除非您可以从 EDL 模式重新刷新设备,否则请勿尝试此操作)。
Fedora 上有一个已知的错误,其 Anaconda 安装程序无法将 UFS 存储设备识别为可以安装的磁盘。
然而,在使用 eMMC 存储的 RB1/RB2 主板上,这种区分是不可能的。如果您允许安装程序擦除磁盘,您的主板将无法启动。使用自定义分区时请务必谨慎,最好安装到 USB 驱动器。遗憾的是,这些主板的 U-Boot 尚未完全启用 SD 卡支持,否则这将是一个合理的解决方案。
介入
IRC:libera.chat 上的 #u-boot-qcom
或者通过 Matrix:#u-boot-qcom:postmarketos.org
去年,U-Boot 对 Qualcomm 平台的支持呈爆炸式增长,从使用非标准设备树和缺乏内部存储访问权限的少数构建目标,到现在对 ~9 SoC 具有通用支持、完全上游兼容 DT 和(几乎)没有特定于主板的解决方法。
一些重要功能(例如 UFS 存储支持)仍然依赖于额外的补丁,但总体而言,上游工作进展顺利。对于任何有 Linux 内核经验的人来说,U-Boot 都非常熟悉,这同样适用于驱动程序代码本身,因为从 Linux 移植对 RPMh 调节器等复杂外设的支持非常简单。上游 DT 有了第二个用户,并且许多重要驱动程序有了第二个实现,从长远来看,这应该有助于我们解决设备树整体上的不一致问题。
您无需开发板即可使用,许多搭载 Linux 设备树的高通智能手机都能轻松启动。不过,如果 SoC 首次支持 U-Boot,您可能需要编写时钟和引脚控制驱动程序。edk2 设置的帧缓冲区输出可用于显示启动菜单,其中包含导航按钮,甚至可以通过 USB 串行小工具模式进行更深入的访问。
一旦您的 SoC 获得支持,任何具有上游兼容 DTB 的设备很可能都能“正常工作”。只需确保正确描述帧缓冲区,并在 DT 的 /chosen 节点中指定 stdout-path = “serial0:115200n8”; 来启用串行端口(如果有)。
有了 U-Boot 支持,我们就能享受 EFI 的诸多优势:GRUB 和 systemd-boot,以及对多内核甚至多发行版的支持!最主要的好处当然是,更新内核时无需再创建和烧录 Android 启动映像。甚至可以使用 wget 实用程序通过 HTTP 进行引导,从而通过 USB 网络适配器实现真正无缝的开发周期。
评论区