3. Linux BSP源码树结构

高通平台的 Linux BSP 并不是一个“把内核源码解压出来就能编译”的简单工程,而是一套由多个仓库、多个层级、多个来源共同拼装出来的源码体系。理解它的组织结构,是后续做内核裁剪、设备树修改、启动优化和补丁维护的前提。本节从整体布局入手,逐层拆解高通 BSP 源码树的组成,并给出实际工程中如何定位文件、如何管理厂商补丁的方法。

一、高通 BSP 源码树的整体分层

在典型的骁龙平台 BSP 交付中,源码通常以“平台基线 + 厂商补丁 + 项目定制”三层结构组织。最上层是 Qualcomm 发布的 BSP 基线包,包含内核、引导加载程序、固件和专有驱动;中间层是 OEM/ODM 或方案商叠加的板级适配补丁;最下层是具体产品项目的配置与设备树覆盖。三者通过 Git 仓库和补丁队列串联,而不是简单合并成一个目录。

常见的顶层目录布局如下(以某骁龙平台 BSP 为例,具体名称随版本略有差异):

bsp-root/
├── kernel/                 # Linux 内核源码(含高通补丁)
│   ├── arch/arm64/boot/dts/vendor/
│   ├── drivers/soc/qcom/
│   ├── drivers/clk/qcom/
│   └── techpack/           # 高通音频、显示、摄像头等外挂模块
├── bootloader/
│   ├── abl/                # Application Boot Loader(UEFI 风格)
│   ├── xbl/                # eXtensible Boot Loader
│   └── tz/                 # TrustZone 相关镜像
├── firmware/
│   ├── adsp/               # 音频 DSP 固件
│   ├── cdsp/               # 计算 DSP 固件
│   ├── modem/              # 基带固件
│   └── wpss/               # 无线连接子系统固件
├── vendor-patches/         # 厂商补丁队列
└── build/                  # 编译脚本与产品配置

这个布局的关键在于:内核源码、引导加载程序、固件三者是独立交付、独立编译、最终在镜像打包阶段才组合到一起的。很多初学者误以为“改内核就能改启动”,实际上 XBL/ABL 阶段的问题必须回到 bootloader 目录处理。

高通 Linux BSP 源码树分层与镜像组装关系 源码仓库层(独立 Git 仓库) kernel/ arch/arm64/boot/dts/vendor/ → 设备树 drivers/soc/qcom/ → 平台驱动 techpack/ → 音频/显示/摄像头外挂模块 bootloader/ xbl/ → 第一阶段引导(芯片初始化) abl/ → UEFI 风格引导,加载内核 tz/ → TrustZone 安全镜像 firmware/ adsp/ cdsp/ → 音频与计算 DSP 固件 modem/ → 基带固件 wpss/ → Wi-Fi/蓝牙子系统固件 vendor-patches/ 按平台/项目组织的补丁队列(quilt / git am) 记录基线版本、补丁顺序与冲突处理 镜像组装与启动链 编译产物 Image / dtb / xbl.elf / abl.elf / *.mbn 镜像打包 boot.img / dtbo.img / super.img / vendor_boot.img 启动链(从上电到用户空间) ① PBL(芯片内 ROM) ② XBL → 初始化 DDR、时钟、电源 ③ ABL → 加载 boot.img,解析 dtb ④ Kernel → 挂载 rootfs,启动 init 运行时固件加载 内核启动后通过 PIL/SCM 加载 adsp、cdsp、modem 固件 固件版本与内核驱动必须匹配,否则子系统无法上电

二、内核源码与高通外挂模块

高通内核源码基于 Linux LTS 版本,但叠加了大量平台补丁。最显著的特点是 techpack/ 目录:音频、显示、摄像头等模块被从标准 drivers/ 中剥离出来,以独立仓库形式维护。这样做的好处是这些模块可以独立升级,不必跟随内核版本整体迁移;代价是编译时需要额外同步这些仓库,且它们的 API 与内核版本强绑定。

实际定位一个驱动时,建议按以下顺序查找:先看 arch/arm64/boot/dts/vendor/ 下对应平台的 dtsi 文件,确认设备节点和 compatible 字符串;再用 compatible 字符串在 drivers/ 和 techpack/ 中反查驱动源码。例如某音频编解码器节点写的是 compatible = "qcom,waipio-sndcard",那么驱动大概率在 techpack/audio/asoc/ 下,而不是标准 sound/soc/ 中。

三、设备树的分层组织

高通设备树采用“平台 dtsi + 板级 dtsi + 项目 dts”三层叠加。平台层描述 SoC 内部控制器,板级层描述具体单板的引脚、电源和外围器件,项目层只做最小覆盖。编译时通过 #include 链合并,最终生成 .dtb。

// 平台层:soc.dtsi
&qup_i2c0 {
    status = "okay";
    clock-frequency = <400000>;
};

// 板级层:board.dtsi
#include "soc.dtsi"
&qup_i2c0 {
    touch@38 {
        compatible = "vendor,touch-ft3518";
        reg = <0x38>;
    };
};

// 项目层:project.dts
#include "board.dtsi"
/ {
    model = "MyProduct EVT1";
};

调试设备树时,最有效的手段是反编译运行中的 dtb:dtc -I fs -O dts /proc/device-tree,对比编译产物与源码,确认覆盖是否生效。若某个节点在最终 dtb 中缺失,通常是 status 被上层覆盖为 "disabled",或 include 顺序导致后定义覆盖了前定义。

四、引导加载程序与固件的角色划分

XBL 负责最底层的芯片初始化,包括 DDR 训练、时钟树配置和电源域上电,它运行在芯片内部的 SRAM 中。ABL 则是一个 UEFI 风格的引导程序,负责读取 boot.img、解析设备树、校验镜像签名,最后跳转到内核入口。两者都属于 bootloader 仓库,但编译工具链和产物格式完全不同。

固件目录中的 .mbn 文件是各子系统的可执行镜像。它们不参与内核编译,而是在内核启动后由 PIL(Peripheral Image Loader)通过 SCM 调用加载到对应 DSP 的内存中。一个常见的排障场景是:音频无声,内核日志显示 adsp: failed to load firmware,此时应检查 firmware/adsp/ 下的固件版本是否与当前内核驱动匹配,而不是去改设备树。

五、厂商补丁管理

厂商补丁管理的核心原则是:基线不动,补丁叠加,顺序可追溯。推荐使用 Git 分支 + 补丁队列的方式:为每个平台基线打一个 tag,所有厂商修改以 commit 形式叠加在独立分支上,每个 commit 附带平台、模块、修改原因和影响范围。当基线升级时,用 git rebase 或 git am 将补丁队列迁移到新基线,冲突逐个解决并记录。

补丁提交信息建议包含以下字段,便于后续追溯:

字段说明示例
Platform适用平台waipio / kalama
Module所属模块kernel / bootloader / firmware
Baseline基线版本BSP-2023.06
Reason修改原因修复 I2C 触摸中断丢失
Impact影响范围仅影响 EVT1 触摸功能
补丁队列不要直接在主分支上修改。建议为每个项目建立独立分支,基线升级时先 rebase 到新 tag,再逐个解决冲突。冲突解决后务必重新编译并验证启动链,因为补丁顺序变化可能改变 include 覆盖关系。
固件与内核驱动存在版本绑定关系。升级内核时若未同步升级 firmware/ 下的 .mbn,可能导致 DSP 子系统加载失败,表现为音频、摄像头或传感器不可用。建议在 BSP 发布说明中明确记录固件版本号,并在启动日志中打印实际加载的固件版本以便核对。

总结来看,高通 BSP 源码树的管理本质上是“多仓库协同 + 分层覆盖 + 补丁追溯”。理解 kernel、bootloader、firmware 三者的独立性和组装关系,掌握设备树的三层叠加逻辑,并建立规范的补丁队列,才能在后续的内核裁剪、启动优化和问题定位中做到有的放矢。