4. Jetson系统启动流程与BSP定制

Jetson 的启动不是“上电即进 Linux”这么简单,而是一条由多级固件逐棒交接的链:BootROM → MB1 → MB2 → UEFI → 内核 → 根文件系统。理解这条链,才能知道 BSP 定制应该改哪一层、启动画面为什么出现在某个时刻、分区表为什么不能随意挪动,以及 OTA 升级究竟在替换什么。

一、五级启动链与各阶段职责

上电后,芯片内部固化的 BootROM 首先运行。它只做最少的事:初始化最基础的时钟与存储接口,按熔丝和 strap 引脚决定的顺序(如 QSPI、SD、USB 恢复模式)寻找 MB1。BootROM 代码不可修改,因此它决定了“设备能从哪些介质启动”这一硬约束。

MB1 运行在 BPMP(Boot and Power Management Processor)上,负责初始化 DRAM、配置电源与时钟、加载并校验后续镜像。MB2 继续完成内存与安全相关初始化,然后跳转到 UEFI(EDK2 固件)。UEFI 阶段承担设备枚举、PCIe/USB/网络初始化、读取 extlinux.conf 或 UEFI 启动项、加载内核 Image、设备树 *.dtb 与 initrd,最后把控制权交给内核。内核挂载根文件系统后启动 init,用户空间才真正跑起来。

Jetson 启动链与 BSP 可定制层 BootROM 芯片固化·不可改 MB1 DRAM/时钟/电源 MB2 安全/内存初始化 UEFI (EDK2) 设备枚举/加载内核 Linux 内核 Image + dtb + initrd 根文件系统 init / systemd BSP 可定制:MB1/MB2 配置 内存参数、启动介质、安全启动 BSP 可定制:UEFI + 启动项 extlinux.conf、启动画面、超时 BSP 可定制:内核 + 设备树 驱动裁剪、pinmux、外设节点 BSP 可定制:根文件系统 分区布局、只读根、OTA 槽位 典型 eMMC 分区布局(A/B 双槽 OTA) QSPI/BL A_kernel A_rootfs B_kernel B_rootfs APP/数据 recovery UDA/用户

二、BSP 定制的四个落点

NVIDIA 提供的 Jetson Linux BSP 包含源码包(sources/)与预编译包(Linux_for_Tegra/)。定制通常落在四处:

  • MB1/MB2 配置:通过 p3701.conf 之类的板级配置与 bpmp 相关 DTB 调整内存、启动介质顺序、安全启动开关。这一层改动风险最高,出错往往表现为“上电无任何输出”。
  • UEFI 与启动项:修改 Linux_for_Tegra/bootloader/ 下的 UEFI 镜像或 extlinux.conf,控制内核参数、启动超时、默认启动项与启动画面。
  • 内核与设备树:在 sources/kernel/kernel-jammy-src 中裁剪驱动、修改 tegra234-p3701-*.dts 中的 pinmux、I2C/SPI 节点、GPIO 定义。
  • 根文件系统与分区:通过 flash.xml(或 *.xml 分区表)定义各分区偏移与大小,决定是否启用 A/B 冗余。

三、定制启动画面

启动画面分两段:UEFI 阶段的 logo 与内核阶段的 bootlogo。UEFI logo 由 Linux_for_Tegra/bootloader/ 中的 bmp.blob 提供,可用 NVIDIA 提供的 build_bmpblob.py 把自定义 BMP 打包替换。内核 logo 则通过内核配置 CONFIG_LOGO 与 logo_linux_clut224.ppm 替换,并在 extlinux.conf 中加 video=tegrafb0:1920x1080 等参数固定分辨率,避免 logo 拉伸。

若启动画面出现“先花屏再正常”,通常是 UEFI 分辨率与内核 framebuffer 分辨率不一致,需让两者对齐到同一模式。

四、分区布局与 OTA 基础

OTA 的本质是“把新镜像写入非活动槽,再切换启动指针”。因此分区表必须预留 A/B 两套 kernel 与 rootfs。以 32GB eMMC 为例,一种可参考的示例划分如下(数值为示例,实际需按镜像大小调整):

分区大小示例作用
A_kernel64 MB槽 A 内核 + dtb
A_rootfs6 GB槽 A 根文件系统
B_kernel64 MB槽 B 内核 + dtb
B_rootfs6 GB槽 B 根文件系统
recovery1 GB恢复镜像,用于回滚
APP / UDA剩余应用数据与用户数据

NVIDIA 提供 nv_update_engine 与 ota_tools 实现 A/B 升级。一次典型 OTA 流程是:设备从服务器下载 ota_payload → 校验签名 → 写入 B 槽 → 设置 bootloader 环境变量指向 B → 重启验证 → 成功后标记 B 为活动槽。若验证失败,recovery 分区可回滚到 A 槽。

# 生成 OTA 包(示例)
sudo ./ota_tools/nv_ota_package.sh \
  --board jetson-orin-nano \
  --rootfs A \
  --output ota_payload.tar.gz

# 设备端应用
sudo nv_update_engine --install ota_payload.tar.gz
sudo reboot

五、实操检查点

定制完成后,建议按以下顺序验证:先用 flash.sh 烧录并观察串口日志,确认 BootROM→MB1→MB2→UEFI 各阶段无报错;再检查 /proc/device-tree 中自定义节点是否生效;最后用 nvbootctrl 查看当前活动槽与槽状态。若串口在 MB1 阶段就停止,优先怀疑内存参数或启动介质配置;若在 UEFI 后停止,则检查 extlinux.conf 路径与内核镜像是否匹配。

修改分区表后必须重新烧录全盘,不能只更新单个分区,否则 GPT 与镜像偏移不一致会导致启动失败。

掌握这条启动链后,后续的驱动开发、系统裁剪与量产烧录都会变得有据可依:每一层改动都能对应到具体的镜像、配置文件和验证手段,而不是盲目试错。