3421 字
17 分钟
Jetson Orin 强行 `apt upgrade` 翻车,从黑屏光标到 QSPI 归位的完整硬核复盘

Jetson Orin 强行 apt upgrade 翻车,从黑屏光标到 QSPI 归位的完整硬核复盘#

前言#

给 NVIDIA Jetson 执行系统升级永远是一场赌博。L4T 的内核、驱动与 QSPI 引导固件深度绑定,一旦升级中断或依赖不齐,黑屏、引导链损坏、QSPI 与 rootfs 版本断层便接踵而来。

本文记录 Jetson Orin Nano Super(L4T 36.4.3 → 36.4.7)升级失败后的完整救援过程:SD 卡跳板救数据 → GRUB 命令行手工引导 → 修复脚本重生引导链 → UEFI 强制 Normal 续命 → 深挖 Capsule update status: 0 死锁 → 找到 NVIDIA 自家密道一次刷齐 QSPI。全程无主机重刷、无 dd 到 mtd、无手工写 efivar——从”强撑 hybrid”到”版本完全对齐”,每一步都建立在真实日志与源码之上。


一、灾难降临:cpio 缺位,initrd 打包失败#

起因:在真系统里跑了一次看似普通的 sudo apt upgrade,L4T 全家桶从 36.4.3 拉到 36.4.7。

致命错误(升级中途弹出,被大量下载进度条淹没):

nv-update-initrd: 行 127: cpio: 未找到命令
dpkg: 处理软件包 nvidia-l4t-initrd (--configure) 时出错

cpio 是 initramfs 打包的核心工具,缺了它 → 新的 36.4.7 initrd 压根没生成成功 → 里面 NVMe 驱动没打进去 → 重启后内核起来但根挂载不上。

表象:UEFI 试图引导、Linux 内核静默死在 Attempted to kill init,几次启动尝试过后 boot-guard 把 OS chain A 标为 Unbootable——再次重启,屏幕左上角只剩一个孤零零的光标,键盘无响应。


二、SD 卡救援跳板:守住数据底线,但治不了本#

黑屏当下没有主机可以 SDK Manager 刷,唯一办法是先造一个能进的环境。

  1. 烧一张通用 Ubuntu ARM64 SD 卡(非 NVIDIA 原版镜像,手头没有);改 EEPROM/串口进 UEFI 把 SD 卡提到 BootOrder 首位。
  2. 挂载 NVMe 全部 15 个分区,lsblk 认出 nvme0n1p1 是 117 GB 根(ext4)、p2 是 A_kernel(vfat)、p10 是 ESP;mount /dev/nvme0n1p1 /mnt 后第一时间把 home 下核心代码 rsync 到外接 U 盘。
  3. 局限:普通 Ubuntu 里 nvbootctrl / tegra-mccompiler / nvidia-l4t-* 全都没有,apt 源也接不上 NVIDIA 的 t234 r36.4 feed——SD 卡里做任何操作都碰不到 Jetson 的 QSPI 与引导链。它只能救数据,救不了系统。

结论:必须想办法把 NVMe 里的真系统拉起来。


三、GRUB 命令行手工引导 + nomodeset 破冰#

真系统重启掉进了 GRUB grub> 命令行(因为 /boot/grub/grubenv 保存的 boot 项在 apt 中途被截断)。在命令行敲下救命四连:

set root=(hd0,gpt1)
linux /boot/Image root=UUID=bf28224b-a16a-4cdf-8017-8b04bd637f0f ro rootwait nomodeset
initrd /boot/initrd
boot

几个关键点:

  • UUID 必须是原系统根分区的真实物理 UUID(在 SD 卡救援环境里 sudo blkid /dev/nvme0n1p1 查出来抄进去),绝不能混用 SD 卡自己那个 p1 的 UUID。
  • nomodeset 是必需项——这台板子通过 DP 转 HDMI 转接头输出,Linux 内核在加载 nvidia-drm 时读不到 EDID 会静默死屏(不是黑屏,是光标停在刷新的假活状态)。加 nomodeset 强制走 UEFI 帧缓冲,屏幕才终于亮起。
  • rootwait 是保险:NVMe 枚举有时序抖动,等 3 秒再挂根。

进入桌面后,第一件正事不是修引导,是 sudo apt install cpio——把根子上缺的工具补齐。


四、第一轮修复:fix-boot-official.sh 与它的三个坑#

进真系统后跑精心准备的 fix-boot-official.sh,脚本设计覆盖三处风险:

  1. 不让 apt-get update 失败拖死——去掉 set -e,关键步显式判错;
  2. 找回被 nvidia-l4t-bootloader 重装覆盖的自定义 extlinux 项——暂存 JetsonIO 段(含双 IMX219 CSI 摄像头的 FDT 与 OVERLAYS)并回贴;顺便把 FDT 从已不存在的 p3767-0005-nv.dtb 自动重定向到 -nv-super.dtb;
  3. 收尾禁用 p2 里的 GRUB 劫持件、把当前 NVMe 启动项置顶。

4.1 一个”看似理所当然”的致命误判#

网上的一手经验都说:ESP 里 /boot/efi/EFI/BOOT/BOOTAA64.efi 是 GRUB 的 removable-media 兜底路径,必须改名禁掉。我们照做了,系统立刻不能启动。

事后用 strings + efibootmgr -v 做指纹验证才搞清楚:这个 BOOTAA64.efi 里全都是 L4TLauncher、ExtLinuxBoot、ApplyTegraDeviceTreeOverlay 等字符串,压根没有 GRUB 字样——它是 NVIDIA 原生的 L4TLauncher,是这台板子真正读 extlinux.conf 引导内核的东西。禁掉它 = 断引导。

真正的 GRUB 劫持件在另一个地方:p2 (A_kernel 分区) 上的 \EFI\BOOT\{bootaa64.efi, grubaa64.efi, grub.cfg} 三件套。Boot0007(Image 直连 p2)与 Boot0009(\EFI\BOOT\grubaa64.efi)才是元凶。

修正后的脚本动作:只 .disabled 掉 p2 那三件套;ESP 的 BOOTAA64.efi 反过来加校验、若被误禁则自动恢复;BootOrder 只重排不删除,把 0008(NVMe 磁盘级路径)顶到首位,把 0007 排出去。

4.2 跑完,还是黑屏#

引导链修好了、extlinux 正确、BootOrder 干净,sudo reboot 之后——又是孤零零的光标。


五、UEFI 的”神来之笔”:OS chain A status = Normal 强权续命#

黑屏说明 UEFI 的 boot-guard 还没被安抚——它把 OS chain A 记录成了 Unbootable,任你怎么换引导文件,UEFI 一看到这条就直接跳过 A 链、什么也不做。

于是走进最后一道门:

  1. 开机狂按 Esc 进 UEFI 设置;
  2. Device Manager → NVIDIA Configuration → L4T Configuration → OS chain A status;
  3. 从 Unbootable 强改成 Normal,F10 保存退出。

奇迹发生了:UEFI 忽略版本判定,强行加载 \EFI\BOOT\BOOTAA64.efi(L4TLauncher),L4TLauncher 读 extlinux.conf,内核起来、rootfs 挂上、桌面出来。

这是整个救援的关键转折点,也是这台板子第一次以”混合状态”活下来。


六、“强撑的 hybrid”:QSPI 36.4.3 × rootfs 36.4.7#

进系统后 nvbootctrl dump-slots-info:

Current version: 36.4.3 ← QSPI 引导固件版本
Capsule update status: 0
Current bootloader slot: A
Active bootloader slot: A
num_slots: 2
slot: 0, status: normal
slot: 1, status: normal

/etc/nv_tegra_release 与 dpkg -l nvidia-l4t-* 则显示 rootfs 完全是 36.4.7。QSPI 与 rootfs 劈叉 4 个小版本,靠 UEFI 手动 override 才能起来。

journalctl -b 看到 nv-l4t-bootloader-config.service 每次开机都在演:

Info: System version(deb version): 2360327, BSP version(QSPI version): 2360323
Info: Starting bootloader post-install procedure.
INFO. Trigger Capsule update is done. ← 装作已暂存
INFO. Copy L4tlauncher to /boot/efi/EFI/BOOT/ done.
Reboot the target system for updates to take effect. ← 让你重启
Info: variable BootChainFwStatus is not found. ← 但其实没挂起

做了一次 shutdown -h now + 拔电源 + 冷启动——QSPI 仍 36.4.3。做错了什么? 什么都没做错,只是根本没有 capsule 被暂存,UEFI 没东西可读。


七、假成功的真相:两个致命的软件 bug#

顺着”为什么没暂存”往下挖,读 /var/lib/dpkg/info/nvidia-l4t-bootloader.postinst:

Terminal window
trigger_capsule_update () {
...
create_capsule_uefi_variable
device_id=$(fwupdmgr get-devices | awk '/System Firmware/{getline; print}' | ...)
yes N | fwupdtool install-blob "${payload_dir}/${capsule_payload}" "${device_id}" \
> /dev/null 2>&1 # ← 错误被吞光!
echo "INFO. Trigger Capsule update is done." # ← 无论成败都打这行
}

把 > /dev/null 2>&1 拆掉,手工跑一次 sudo fwupdtool install-blob ...,真相暴露:

/boot/efi does not have sufficient space, required 102.6 MB, need additional 39.6 MB

Bug #1:ESP 空间物理不足#

  • Jetson 出厂 ESP 只有 64 MB(/dev/nvme0n1p10);
  • 我们的 capsule 文件 /opt/ota_package/t23x/TEGRA_BL_3767_super.Cap 48.4 MB;
  • fwupd 的 capsule-on-disk 流程要双份(工作副本 + 处理后镜像),需要 ~103 MB。永远装不下。

Bug #2:OsIndications 变量位写错#

同脚本里 create_capsule_uefi_variable() 写的是:

printf "\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

前 4 字节是 UEFI 属性 (NV+BS+RUNTIME),后 8 字节是变量值——全 0。UEFI 标准要求的是 EFI_OS_INDications_FIRMWARE_UPDATES 的 bit 2 = 1,全 0 意味着”OS 不需要 UEFI 做任何固件更新”。就算 fwupd 侥幸成功,UEFI 也不会去处理。

换句话说:apt install nvidia-l4t-bootloader 走的 fwupd 路径在这块板子上从设计上就不可能成功——ESP 尺寸没跟上 capsule 膨胀。这才是 Capsule update status: 0 死循环的根因。


八、NVIDIA 自家的密道:nv_bootloader_capsule_updater.sh#

既然 fwupd 那条路是死的,就在 /usr/sbin 里翻——find 出来三个工具:

/usr/sbin/nv_update_engine
/usr/sbin/nv_bootloader_capsule_updater.sh ← 名字就叫 capsule updater
/usr/sbin/nv_bootloader_payload_updater

打开 nv_bootloader_capsule_updater.sh,只有两行核心动作:

Terminal window
ESP_CAPSULE_DIR="EFI/UpdateCapsule/"
cp "${payload_file}" "${capsule_dir}" # 单份复制, 只要 1× 尺寸
printf "\x07\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00" > ... # 关键: bit2=0x04
dd if="${TMP_FILE}" of="${var_path}" > /dev/null 2>&1

对比 fwupd 路径的两个致命差异:

fwupd install-blobNVIDIA 自家 capsule_updater
落地位置EFI/BOOT/fwupd/EFI/UpdateCapsule/
ESP 空间需求双份 ≈ 103 MB(装不下)单份 48 MB(够!)
OsIndications 值全 0(UEFI 忽略)bit 2 = 1(UEFI 认这个位)

一次执行收工:

Terminal window
$ sudo /usr/sbin/nv_bootloader_capsule_updater.sh -q \
/opt/ota_package/t23x/TEGRA_BL_3767_super.Cap
Info: Copy capsule payload to /boot/efi/EFI/UpdateCapsule/ done.
Info: Set capsule UEFI variable .../OsIndications-8be4df61-... done.
Info: Firmware on QSPI will be updated on the next boot.
Info: Reboot the target system for updates to take effect.

验证:xxd /sys/firmware/efi/efivars/OsIndications-* 显示 0700 0000 0400 0000 0000 0000,bit 2 正确置位;ls /boot/efi/EFI/UpdateCapsule/ 里 .Cap 就位;ESP 剩 12 MB。


九、一次普通 sudo reboot 之后#

不需要冷启动、不需要拔电源、不需要再进 UEFI 改任何东西——普通 sudo reboot 就够了,因为这次是真的有 pending 状态。

起来后:

$ sudo nvbootctrl dump-slots-info
Current version: 36.4.7 ← QSPI 已升上来
Capsule update status: 1 ← capsule 已成功应用
Current bootloader slot: B ← 从 A 切到 B (双 slot 轮转)
Active bootloader slot: B
num_slots: 2
slot: 0, status: normal ← slot A 仍是 36.4.3 兜底
slot: 1, status: normal

再看环境细节:

  • /boot/efi/EFI/UpdateCapsule/ 已被 UEFI 清空(消费完成);
  • OsIndications bit 2 被 UEFI 归零(正常收尾);
  • df -h /boot/efi 回到 61 MB 可用;
  • 服务日志:System version(deb version): 2360327, BSP version(QSPI version): 2360327——版本劈叉彻底消失,Reboot the target system 假 log 也不再出现。

QSPI 与 rootfs 完全对齐到 36.4.7,从”强撑 hybrid”到”稳态生产机”。

最后跑一下 sudo nvbootctrl verify:

Info: variable BootChainFwStatus is not found.

这次这条不再是故障信号——UEFI 已经直接把启动判成 good,压根没有 try 状态要转正。退出码 0,无害。

至于之前上的 apt-mark hold(49 个 nvidia-l4t-* 包),保留。它现在的作用不是”锁死在旧版”,而是”防止未来某次 apt upgrade 又一次把 nvidia-l4t-bootloader 拉上去,然后再次掉进 fwupd 那个 ESP 装不下的死循环”。真要再升 36.4.8+,正确姿势是 unhold → apt upgrade → 立即手动跑 nv_bootloader_capsule_updater.sh -q <新 .Cap 路径> → reboot。


十、经验总结与避坑指南#

10.1 关于 apt upgrade 与 Jetson#

  • Jetson 不要盲目 apt upgrade——L4T 内核、驱动、QSPI 固件、initrd 深度绑定,跨版本升级要么走 SDK Manager 整机刷,要么一次做完、别中途断。
  • 升级前先装齐 cpio / initramfs-tools 等最底层的打包依赖——nv-update-initrd 依赖 cpio 却不在自身依赖里,缺了就静默失败。
  • 升级后如果引导包要重装,apt 完成 ≠ 引导链就绪,还需要 nvidia-l4t-bootloader-config.service 跑完、capsule 应用;这套自动化并不牢靠。

10.2 关于引导链的假坑#

  • ESP 的 BOOTAA64.efi 不是 GRUB,是 NVIDIA L4TLauncher。任何”看到 BOOTAA64.efi 就改名”的操作都会直接断引导。用 strings 提指纹(L4TLauncher / ExtLinuxBoot 有 = 原生;有 grub 字样 = 是劫持件)先确认再动。
  • GRUB 劫持件真实位置:p2 (A_kernel) 的 \EFI\BOOT\{bootaa64.efi, grubaa64.efi, grub.cfg},以及 Boot0007/Boot0009 这类直接指到 p2 的 Image/EFI 文件的 UEFI 入口。禁它们要临时 mount p2 才能改文件名。
  • efibootmgr 永远用 -o 重排序、不要用 -B 删条目,删错一个就没了。

10.3 关于 UEFI boot-guard#

  • OS chain A status = Unbootable 是内核能起来但 init 挂不上/根挂不上 时 UEFI 的判定;此时任你怎么改引导文件都不生效,只能进 UEFI 手动改回 Normal。
  • 这是最后一道人工通道——不是解决方案,只是续命。真正的解决方案必须消除版本劈叉。

10.4 关于 Capsule update status: 0 假成功#

这是全文最硬核的一节:

  • nv-l4t-bootloader-config.service 每次开机都会打 Trigger Capsule update is done.——但这是假 log,postinst 里 fwupdtool install-blob 用 > /dev/null 2>&1 吞掉了所有错误。
  • Jetson Orin Nano 出厂 ESP 只有 64 MB,L4T 36.4.7 的 .Cap 是 48.4 MB,fwupd 的 capsule-on-disk 要 2× 尺寸 → 物理上装不下;
  • 而且 create_capsule_uefi_variable() 写的 OsIndications = 全 0,UEFI 标准要求的 bit 2 根本没置位;
  • 因此热重启、冷启动、拔电源都不可能让 QSPI 升上来——不是”再等等就好”,是这个路径永远不会通。
  • 正确的系统内更新通道:/usr/sbin/nv_bootloader_capsule_updater.sh -q <.Cap 路径>——单份复制到 EFI/UpdateCapsule/,正确置 OsIndications bit 2。一次 sudo reboot 生效。
  • 判断真挂起 vs 假 log:xxd /sys/firmware/efi/efivars/OsIndications-* 第 5 字节应为 04;ls /boot/efi/EFI/UpdateCapsule/ 应有 .Cap。二者缺一 = 更新根本没挂起。

10.5 什么情况下还是得走主机重刷#

  • 单一物理引导链彻底损毁(比如 p2 里 L4TLauncher 和 GRUB 都被清掉,ESP 上 BOOTAA64.efi 也没了);
  • UEFI 变量集损坏(BootChainFwCurrent / TegraPlatformCompatSpec 值本身不对,不是”没挂起”而是”底账烂了”);
  • .Cap 文件本身缺失或校验不过(/opt/ota_package/t23x/ 目录被误删)。
  • 这三种情况下,只能进 RCM 用主机 SDK Manager / flash.sh 整机刷。

10.6 单砖环境的心态守则#

  • 只有一块板、无主机可刷——那就绝不做 dd of=/dev/mtd*、绝不手工 echo > efivars。任何”看起来能一步到位”的野路子都可能是不可逆砖板。
  • A/B slot 是 Jetson 仅存的自动化兜底:任何 capsule 更新只写非活动 slot,启动失败 UEFI 会自动回滚——但也仅止于此。
  • 能维持”强撑 hybrid 状态稳定运行”就是一种合格工程结局——不必强求一次到位;把真相挖到底,找一条正确的路,等维护窗口再来一次。

结语#

从黑屏时只剩光标的绝望,到 SD 卡里 blkid 抄 UUID 的冷静;从 GRUB 命令行敲 nomodeset 那一击的兴奋,到脚本跑完仍黑屏的崩溃;从 UEFI 里把 Unbootable 强改成 Normal 的孤注一掷,到 apt-mark hold 锁 49 个包的”就这么过吧”;再到翻开 postinst 看到 > /dev/null 2>&1 时的会心一笑——“我就说这是软件问题。”

底层逻辑永远不会骗人,只会把真相藏在你没看的那一行。fwupdtool 需要 103 MB、OsIndications 差一个 bit、NVIDIA 自家还有另一条密道——这三步每一步都不神秘,只是需要有人把静默吞掉的错误重新打印出来。

保持冷静,sudo reboot 之前多问一句 “log 里那句 Trigger Capsule update is done.,它真的做了什么吗?”

Jetson Orin 强行 `apt upgrade` 翻车,从黑屏光标到 QSPI 归位的完整硬核复盘
https://fuwari.vercel.app/posts/jetson/fix_boot/
作者
江湖一条鱼
发布于
2026-09-23
许可协议
CC BY-NC-SA 4.0