📺 B站 嵌入式孙老师博主个人介绍

📘 博主书籍-京东购买链接*:Yocto项目实战教程

📘 加博主微信,进技术交流群jerrydev


为什么有了 Device Tree,Jetson 还需要 BCT?从 .dts.bct 到 UEFI 讲清 Jetson 启动配置

在 Jetson BSP 中,经常能同时看到这些文件:

tegra264-br-bct-common-l4t.dts
tegra264-mb1-bct-pinmux-*.dts
tegra264-mb1-bct-pmic-*.dts
tegra264-p3834-0008-sdram-bct-l4t.dts

br_bct_BR.bct
applet_t264_sigheader.bin.encrypt

kernel-*.dtb
*.dtbo

在这里插入图片描述

这里很容易产生三个疑问:

  1. BCT 为什么也使用 .dts/.dtsi
  2. 已经有 Device Tree,为什么还需要这么多 BCT?
  3. 前面用了 BCT,为什么到了 UEFI 又开始使用 Device Tree?

问题的关键在于:

DTS 是配置的源文件语言,BCT 是早期启动固件使用的配置产物,DTB 是 UEFI 或 Linux 使用的设备树产物。

它们并不是同一层面的概念。
在这里插入图片描述


一、先看 Jetson 启动配置的完整分层

Jetson 并不是从上电开始就运行 UEFI 和 Linux,而是由多个启动阶段逐步建立运行环境。

上电
 │
 ▼
Boot ROM阶段
 │  读取BR-BCT
 │  定位、认证并加载后续启动组件
 ▼
MB1
 │  读取Mem-BCT和MB1-BCT
 │  初始化DRAM、Pinmux、电源、UPHY和安全环境
 ▼
MB2
 │  读取MB2-BCT
 │  完成后续平台初始化
 ▼
UEFI
 │  使用UEFI阶段的Device Tree配置
 │  选择Kernel、Kernel DTB和启动设备
 ▼
Linux Kernel
 │  解析Kernel Device Tree
 │  匹配驱动并管理设备
 ▼
用户空间

在 T264 的官方启动架构中,最早期 ROM 阶段还可进一步划分为 PSCROM、HPSEROM、SBROM 等组件。为了便于理解,本文将这些不能由用户替换、位于启动链最前端的 ROM 组件统称为 Boot ROM 阶段。(NVIDIA Docs)

这条流程说明了一件事:

Jetson 不是在 BCT 和 Device Tree 之间二选一,而是在不同启动阶段使用不同的配置机制。


二、为什么不能从上电开始就使用 Kernel Device Tree?

Linux Device Tree 描述的是:

系统中有哪些设备
设备占用哪些寄存器和中断
依赖哪些Clock、Reset和Regulator
应该匹配哪个Linux驱动

但要加载 Linux 和 Kernel DTB,系统首先必须具备:

可访问的启动存储
可工作的外部DRAM
基本电源和时钟
正确的Pinmux
已建立的内存安全区域
可运行的后续Bootloader

这就产生了一个典型的启动依赖问题:

想加载Kernel DTB
        ↓
需要先初始化内存和启动存储
        ↓
初始化内存和启动存储又需要配置数据

如果把所有配置都放在 Kernel DTB 中,就会陷入循环:

硬件没有初始化
    ↓
无法正常加载和使用Kernel DTB

没有配置数据
    ↓
又不知道如何初始化硬件

BCT 的作用,就是在 UEFI 和 Linux 尚未运行时,为早期启动组件提供能够直接使用的平台配置。

MB1 会根据 Memory BCT 初始化 SDRAM,并根据 MB1-BCT 完成 Pinmux、GPIO、Pad Voltage、安全配置、内存 carveout 等工作。此时 Linux 尚未启动。(NVIDIA Docs)

可以用一句话概括:

BCT 负责建立 UEFI 和 Linux 能够运行的基础环境;Kernel Device Tree 负责描述 Linux 接管之后的硬件。


三、为什么 Jetson 需要多个 BCT?

因为 Jetson 启动不是一个程序完成的。

不同阶段运行环境不同、权限不同、任务也不同。每一个启动组件只应该读取自己需要的数据。

配置类型 主要消费者 解决的问题
BR-BCT Boot ROM阶段 后续组件在哪里、如何加载和验证
Mem-BCT MB1 DRAM、MC、EMC如何初始化
MB1-BCT MB1及相关早期流程 Pinmux、GPIO、电源、UPHY、安全等如何配置
MB2-BCT MB2 MB2阶段的平台与安全配置
UEFI DT UEFI UEFI如何认识平台并选择启动资源
Kernel DT Linux Kernel Linux如何识别和驱动设备

1. BR-BCT:告诉 Boot ROM 从哪里继续启动

BR-BCT 主要保存 Boot ROM 所需的启动信息,例如:

启动介质配置
启动组件的位置
组件大小
加载地址
入口地址
Hash和认证相关信息
启动链相关状态

它解决的是:

芯片刚上电时,如何找到、验证并加载下一级启动程序?

这类数据并不是 Linux 设备拓扑,因此不适合放进 Kernel Device Tree。


2. Mem-BCT:先让 DRAM 工作

MB1 要加载体积更大的固件、UEFI 和 Linux,首先必须初始化外部内存。

Mem-BCT 中主要包含:

SDRAM配置
Memory Controller配置
External Memory Controller配置
内存训练参数
不同RAM Code对应的配置
Warmboot和频率切换相关参数

在实际 T264 BSP 配置目录中,可以看到类似文件:

tegra264-p3834-0008-sdram-bct-l4t.dts
tegra264-p3834-0008-sdram-bct-warmboot-l4t.dts
tegra264-p3834-0008-sdram.dtsi
tegra264-p3834-0008-sdram-dfs.dts

tegra264-p3834-0005-rc00.dtsi
...
tegra264-p3834-0005-rc15.dtsi

这些文件反映了不同模组、内存配置和 RAM Code 对应的参数组合。


3. MB1-BCT:完成早期平台初始化

这是 BSP Bring-up 中最常接触的一类 BCT。

从实际 T264 BSP 文件名可以看出,MB1-BCT 配置被拆分成多个功能模块:

tegra264-mb1-bct-pinmux-*.dts
tegra264-mb1-bct-gpio-*.dts
tegra264-mb1-bct-padvoltage-*.dts
tegra264-mb1-bct-pmic-*.dts
tegra264-mb1-bct-uphy-lanes-*.dts
tegra264-mb1-bct-prod-*.dts
tegra264-mb1-bct-cprod-*.dts
tegra264-mb1-bct-carveout-*.dtsi
tegra264-mb1-bct-ratchet-*.dts
tegra264-mb1-bct-misc-*.dts

对应的功能大致包括:

MB1-BCT
├── Pinmux和GPIO初始状态
├── Pad Voltage
├── PMIC与电源配置
├── UPHY Lane分配
├── Controller Prod参数
├── 启动存储配置
├── 内存Carveout
├── 安全与防回滚配置
└── 其他平台静态参数

它解决的是:

Linux 启动以前,SoC 和板级硬件应该处于什么状态?

NVIDIA 将 BCT 定义为供特定启动组件使用的平台配置数据;MB1 和相关启动阶段通过 MB1-BCT 获取平台静态配置。(NVIDIA Docs)


4. MB2-BCT:服务于后续启动阶段

实际 BSP 中还可以看到:

tegra264-mb2-bct-common.dtsi
tegra264-mb2-bct-misc-*.dts
tegra264-mb2-bct-firewall-*.dts

这些配置主要服务于 MB2 阶段的平台、安全和其他启动参数。

需要注意的是:

不能只凭文件名判断一份配置最终属于哪个 BCT。

正确的判断方法是继续查看:

它被哪个Board Config变量引用
由哪个工具处理
最终生成哪个二进制文件
由哪个启动组件读取

例如,某些文件名中虽然带有 mb1-bct,但最终可能通过特定配置变量进入 MB2 阶段的生成流程。


四、BCT 的 .dts/.dtsi.bct 到底有什么区别?

这是最容易混淆的地方。

.dts/.dtsi 是源文件

例如:

tegra264-br-bct-common-l4t.dts
tegra264-mb1-bct-pinmux-*.dts
tegra264-mb1-bct-pmic-*.dts

它们是便于工程师阅读、组合和维护的文本配置。

其中:

.dts  :通常作为一个完整配置的入口
.dtsi :通常作为可复用或可覆盖的配置片段

.bct 是生成后的二进制配置

例如:

br_bct_BR.bct

它是经过 NVIDIA BCT 工具链处理后生成的二进制 Boot Configuration Table,供早期启动组件读取。

完整关系是:

BCT .dts/.dtsi
       │
       │  include、合并和覆盖
       ▼
 tegrabct_v2 / TegraFlash工具链
       │
       ▼
     二进制.bct
       │
       ▼
 Boot ROM / MB1 / MB2读取

从 T23x 开始,NVIDIA 将 BCT 的源配置格式改为 DTS。Boot ROM 和 MB1 实际消费的仍是由 tegrabct_v2 生成的二进制 BCT,而不是 Linux 使用的普通 DTB。(NVIDIA Docs)

因此:

.dts/.dtsi = 工程师维护的BCT源码

.bct       = 启动固件读取的二进制产物

br_bct_BR.bct 并不是改了后缀的 DTB,也不能使用 Linux Device Tree 的方式理解或直接修改。


五、既然不是 Linux Device Tree,为什么 BCT 也使用 DTS?

因为 DTS 在这里首先是一种结构化配置语言

它具备几个明显优势:

支持树状组织
支持#include
支持公共配置复用
支持板级差异覆盖
适合不同模组、载板和SKU组合
已有成熟的解析和检查工具

例如,同一份公共配置可以被不同板级入口文件包含:

#include "tegra264-mb1-bct-defaults.dtsi"
#include "tegra264-mb1-bct-pmic-common.dtsi"
#include "tegra264-board-specific.dtsi"

这比大量平铺的:

parameter_a = value_a;
parameter_b = value_b;
parameter_c = value_c;

更容易维护复杂 SoC 的平台配置。

但必须注意:

使用 DTS 语法,不代表它就是 Linux Device Tree。

决定一份 DTS 性质的,不是文件后缀,而是:

谁读取它
什么时候读取
遵循什么Binding
由什么工具处理
最终生成什么

可以类比为:

JSON 既可以保存应用程序设置,也可以描述网络协议。格式相同,不代表内容、消费者和生命周期相同。


六、applet_t264_sigheader.bin.encrypt 又是什么?

它与 BCT 不是同一类文件。

br_bct_BR.bct

是配置数据。

而:

applet_t264_sigheader.bin.encrypt

是经过启动安全工具链处理的可执行固件镜像。

文件名可以分成三部分理解:

applet_t264
    └── Applet可执行程序

sigheader
    └── 添加了启动认证相关头部

encrypt
    └── 经过签名/加密处理流程生成的产物

Applet 通常参与 RCM 刷机阶段:

Host上的TegraFlash
        │
        │ USB Recovery/RCM
        ▼
发送Applet到目标SoC
        │
        ▼
Applet在早期环境中运行
        │
        ├── 读取芯片信息
        ├── 读取Fuse和RAM Code
        ├── 获取板级信息
        └── 协助后续刷机流程

因此:

文件 本质
br_bct_BR.bct Boot ROM使用的配置数据
applet_t264_sigheader.bin.encrypt 经过安全封装的可执行程序
mb1_t264_prod.bin MB1可执行固件
mb2_t264.bin MB2可执行固件
kernel-*.dtb Linux使用的设备树
*.dtbo Device Tree Overlay

TegraSign 工具负责生成签名、Hash以及特定模式下的加密数据。不过,仅凭文件名中的 .encrypt,不能直接判断设备是否已经烧写 OEM Key 或启用了特定量产安全状态,最终状态还取决于使用的密钥、Fuse和刷机参数。(NVIDIA Docs)


七、为什么到了 UEFI 又开始使用 Device Tree?

因为运行到 UEFI 时,启动环境已经完全不同了。

在前面的 BCT 和早期固件协助下,系统已经完成:

DRAM初始化
基本电源和时钟初始化
早期Pinmux配置
启动存储访问
内存安全区域建立
UEFI镜像加载和认证

此时 UEFI 已经具备:

可用的外部内存
较完整的执行环境
访问启动存储的能力
解析FDT的能力
加载文件和启动操作系统的能力

所以 UEFI 可以使用更通用的 Device Tree 机制。

Jetson UEFI 中的 Device Tree 主要有两个角色。

1. 服务于 UEFI 自身

UEFI 需要认识当前平台的部分信息,例如:

启动设备
平台配置
固件相关节点
Reserved Memory
板级识别信息
启动模式

这些信息可以通过 UEFI 阶段的 DTB 或 DTBO 提供。

2. 为 Linux 准备 Kernel DTB

Jetson 的 L4tLauncher 负责加载 Kernel 和 Kernel DTB。

UEFI
 │
 ├── 读取启动配置
 ├── 选择Kernel Image
 ├── 选择Kernel DTB
 ├── 应用相关Overlay
 ▼
将Kernel和最终DTB交给Linux

UEFI 可以根据启动模式和配置选择独立的 Kernel DTB。在部分 Jetson 启动配置中,如果没有找到指定的 Kernel DTB,还可能使用 UEFI 当前持有的 DTB作为后备。(NVIDIA Docs)

所以不能简单地说:

BCT用于Bootloader
Device Tree用于Linux

更准确的说法是:

BCT
负责最早期的平台建立

UEFI Device Tree
服务于固件阶段的平台描述和启动选择

Kernel Device Tree
服务于Linux驱动模型

八、BCT、UEFI DT 和 Kernel DT 是否存在重复配置?

会存在少量重叠,但它们承担的职责不同。

以一个 GPIO 控制的外设复位信号为例:

MB1-BCT
├── 将PAD配置为GPIO
├── 配置Pull
├── 配置Tristate
├── 设置输入输出能力
└── 设置启动早期状态

UEFI Device Tree
└── UEFI需要使用该设备时,描述固件阶段资源

Kernel Device Tree
├── 指定哪个Linux设备使用该GPIO
├── 定义高有效或低有效
└── 由Linux驱动申请并控制

Kernel DTS 中即使写了:

reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;

也不能自动替代 MB1-BCT 中的 Pad 和 Pinmux 配置。

如果 PAD 在早期阶段仍然被配置为其他 SFIO 功能,或者处于不正确的 Tristate 状态,那么 Linux 驱动即使成功申请 GPIO,物理引脚也可能无法产生预期电平。

这不是配置重复,而是同一硬件在不同生命周期阶段分别被管理。


九、如何快速判断一个 Jetson BSP 文件属于哪一层?

不要只看 .dts 后缀,建议按照以下顺序判断。

第一步:看文件名和目录

*-br-bct-*       → 很可能是BR-BCT源码
*-mb1-bct-*      → 很可能是MB1-BCT源码
*-mb2-bct-*      → 很可能是MB2-BCT源码
*-sdram-*        → 很可能属于Mem-BCT
kernel/*.dts     → 很可能属于Kernel Device Tree
uefi/*.dts       → 很可能属于UEFI配置或Overlay

文件名只能作为线索,不能作为最终依据。

第二步:看 Board Config 中的引用变量

例如:

BCTFILE
PINMUX_CONFIG
PMIC_CONFIG
UPHY_CONFIG
EMC_BCT
MB2_BCT
SCR_CONFIG
OVERLAY_DTB_FILE
DTB_FILE

这些变量决定配置进入哪条生成链。

第三步:看处理工具

tegrabct_v2 / TegraFlash
    → BCT生成链

dtc / Kernel Build
    → Kernel DTB生成链

EDK2 Build
    → UEFI DTB/DTBO生成链

第四步:看最终产物

.bct
    → 启动配置二进制

.dtb/.dtbo
    → Flattened Device Tree

.bin/.bin.encrypt
    → 固件或可执行镜像

第五步:确认最终消费者

Boot ROM
MB1
MB2
UEFI
Linux Kernel

最终消费者才真正决定这个文件的性质。


十、完整生成关系

将这些文件放在一起,可以得到一条比较清晰的生成链:

板级配置文件
│
├── BR-BCT .dts/.dtsi
├── Mem-BCT .dts/.dtsi
├── MB1-BCT .dts/.dtsi
├── MB2-BCT .dts/.dtsi
│
▼
tegrabct_v2 / TegraFlash
│
├── br_bct*.bct
├── mem_bct*.bct
├── mb1_bct*.bct
└── mb2_bct*.bct
│
▼
签名、Hash和安全封装
│
▼
启动分区或刷机镜像

另一条链是:

UEFI .dts/.dtsi
        │
        ▼
UEFI DTB/DTBO
        │
        ▼
       UEFI

Linux 则使用:

Kernel .dts/.dtsi
        │
        ▼
       dtc
        │
        ▼
Kernel DTB/DTBO
        │
        ▼
 Linux Kernel

三条链都可能使用 DTS 语法,但最终产物和消费者完全不同。


总结

Jetson 使用多个 BCT,并不是因为 Device Tree 能力不足,而是因为最早期启动阶段还没有运行 UEFI 和 Linux,也没有完整的内存、存储及驱动环境。

BR-BCT、Mem-BCT、MB1-BCT 和 MB2-BCT 分别服务于不同启动阶段:

BR-BCT
解决“如何找到并验证后续启动组件”

Mem-BCT
解决“如何让DRAM工作”

MB1-BCT
解决“如何建立SoC和板级硬件的早期状态”

MB2-BCT
解决“如何完成后续平台初始化”

UEFI Device Tree
解决“固件阶段如何认识平台并加载操作系统”

Kernel Device Tree
解决“Linux如何识别和管理硬件”

BCT 之所以也使用 .dts/.dtsi,只是因为 NVIDIA 选择了 DTS 作为结构化配置语言:

DTS决定配置怎么写。

BCT决定配置给哪个启动组件使用。

DTB决定哪棵设备树交给UEFI或Linux。

最准确的理解是:

启动越早,配置越专用、固定并强调安全验证;启动越晚,平台描述越通用、动态,并逐渐交给 UEFI 和 Linux 管理。

因此,BCT 与 Device Tree 并不是相互替代的两套机制,而是 Jetson 分阶段启动架构中的上下游关系:

BCT先建立平台
    ↓
UEFI组织启动
    ↓
Kernel Device Tree完成Linux接管
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐