手头要给一批 Jetson Orin Nano Super 做无人值守刷机:预置账号、跳过首次开机向导、最好还能一条命令刷多台。NVIDIA 的命令行流程默认你在 Ubuntu x86_64 主机上操作,换成 Arch 会有几个包要自己映射,另有一些参数在中文教程里传错了。这篇把在 Arch 上跑通整套流程的步骤、参数修正和批量方案记下来,最后评估一下做跨平台 GUI 的可行性。
先对齐 2026 年的现状,免得照着旧教程白折腾:
- JetPack 7.2(L4T r39.2,Ubuntu 24.04 基础)起,官方主推 ISO 安装器 :U 盘启动、设备自己安装,做盘在 Windows / Mac / Linux 都行,SDK Manager 不再是必须。
- SD 卡镜像从 7.2 起不再提供。7.2 还要求设备里已有 36.x 代的 UEFI/QSPI 固件,出厂固件太新老(低于 36.0)得先走一遍 JetPack 6.x 升级路径。
- 但凡是批量、无头、要定制 rootfs 的场景,还得用命令行
l4t_initrd_flash.sh,它没被砍,参数与 6.2 基本一致。下面全部围绕这条路。
0. 先纠正几个到处流传的说法
这几条是核对官方文档和论坛时发现的,先列出来:
l4t_create_default_user.sh的-a不是"自动接受许可"。官方参数表写得明白,-a是--autologin(自动登录桌面);接受 EULA 的是--accept-license,漏掉它脚本会停在 EULA 交互确认上等输入,CI 或无人值守环境直接卡死。- massflash 产物是
mfi_<板型>.tar.gz,不是.tbz2。网上有教程写.tbz2,照做出不来文件。 - 刷 NVMe 的命令结尾参数,官方示例统一写
internal(SDK Manager 生成的就是这个)。社区有人写external,NVIDIA 工程师在论坛里回复过 两者在该场景等价,但没必要标新立异,跟官方对齐。 --massflash N里的 N 是并发上限。实际接的设备少于 N 没事,文档原话如此,不用为了数量重新打包;超过 N 的话要么加 N,要么分批。- “JetPack 7 = R38.x” 这个印象要修正:R38 是 Thor 那条线的 7.0/7.1;Orin 系列的 JetPack 7.2 其实是 R39.2,7.2.1 是 R39.2.1。
1. Arch 主机的依赖
Ubuntu 主机上 NVIDIA 提供 tools/l4t_flash_prerequisites.sh 一键装依赖(apt)。Arch 上没有对应物,需要手动装。这是按官方脚本和社区文档映射出来的一份清单:
官方仓库:
sudo pacman -S --needed libxml2 lz4 sshpass nfs-utils dosfstools \
e2fsprogs cpio dtc binutils rsync usbutils iproute2 python-yaml vim
对应关系几个容易搞混的:
| Ubuntu 包 | Arch 对应 | 用途 |
|---|---|---|
| qemu-user-static | AUR: qemu-user-static + qemu-user-static-binfmt |
chroot 里跑 aarch64 二进制 |
| binfmt-support | systemd 自带的 systemd-binfmt |
注册 binfmt handler |
| libxml2-utils | libxml2(提供 xmllint) |
解析 flash 配置 |
| nfs-kernel-server | nfs-utils |
initrd flash 的 NFS 传输 |
| abootimg | AUR: abootimg |
boot.img 相关处理 |
| sshpass | sshpass |
刷写时通过 usb0 登录设备 |
| device-tree-compiler | dtc |
设备树 |
| xxd | vim(自带 /usr/bin/xxd) |
tegraflash 内部工具 |
AUR 部分(用 yay 或 paru):
yay -S qemu-user-static qemu-user-static-binfmt abootimg
装完验证 binfmt 已经注册,否则 apply_binaries.sh 会报 Exec format error:
ls /proc/sys/fs/binfmt_misc/ | grep aarch64
# 应能看到 qemu-aarch64(-static) 字样
nfs-utils 保险起见把服务带起来(initrd flash 全程要用 NFS):
sudo systemctl enable --now nfs-server
缺命令时怎么查包:
pacman -F 缺的命令名
2. 下载与解压
从 Jetson Linux 归档页 或 JetPack 下载页取两个文件:BSP 和 Sample Root Filesystem。以 6.2 为例:
Jetson_Linux_R36.4.3_aarch64.tbz2Tegra_Linux_Sample-Root-Filesystem_R36.4.3_aarch64.tbz2
7.2.1 对应 R39.2.1,文件名规律相同。BSP 和 rootfs 的版本号必须一致。
tar xf Jetson_Linux_R36.4.3_aarch64.tbz2
sudo tar xpf Tegra_Linux_Sample-Root-Filesystem_R36.4.3_aarch64.tbz2 -C Linux_for_Tegra/rootfs
cd Linux_for_Tegra
sudo ./apply_binaries.sh
rootfs 必须用 sudo 解压并保留权限(-p),否则后面刷出来的系统文件属主是错的。apply_binaries.sh 把 NVIDIA 驱动和用户态组件铺进 rootfs,之后再动手定制。
3. 预置账号与 rootfs 定制
这一步决定了"无人值守"能不能成立,官方说法是:系统启动时如果没有默认用户就会跑 oem-config 向导;预置了默认用户就跳过。所以全部要在生成镜像包之前完成。
3.1 创建默认用户
sudo ./tools/l4t_create_default_user.sh -u dev -p '你的密码' -n dev-01 --accept-license
参数含义(官方文档 ):
-u用户名,默认nvidia-p密码,不给则随机生成-n主机名,默认tegra-ubuntu-a自动登录桌面(想要开机免登录再加)--accept-license接受 EULA,无人值守必加
批量场景里主机名反正会被 first-boot 服务改掉(见 3.3),这里随便给个占位即可。
3.2 chroot 定制(装包、塞文件)
需要往 rootfs 里装 apt 包或放自己的二进制(比如 Rust 写的 setup wizard、systemd 服务文件)时,用 qemu 进 chroot 操作。核心步骤:
cd Linux_for_Tegra
# 1. 把静态 qemu 放进 rootfs,binfmt 已注册就能跑 aarch64
sudo cp /usr/bin/qemu-aarch64-static rootfs/usr/bin/
# 2. 挂载必要文件系统
sudo mount -t proc proc rootfs/proc
sudo mount -t sysfs sys rootfs/sys
sudo mount --bind /dev rootfs/dev
sudo mount --bind /dev/pts rootfs/dev/pts
# 3. 让 chroot 里有 DNS(先备份原来的)
sudo mv rootfs/etc/resolv.conf rootfs/etc/resolv.conf.bak
sudo cp /etc/resolv.conf rootfs/etc/resolv.conf
# 4. 装包
sudo chroot rootfs /bin/bash -c \
"export DEBIAN_FRONTEND=noninteractive; apt-get update && apt-get install -y --no-install-recommends curl htop && apt-get clean && rm -rf /var/lib/apt/lists/*"
# 5. 卸载、清理、恢复
sudo umount rootfs/dev/pts rootfs/dev rootfs/sys rootfs/proc
sudo rm rootfs/usr/bin/qemu-aarch64-static
sudo mv rootfs/etc/resolv.conf.bak rootfs/etc/resolv.conf
注意事项:
- 动作都在
apply_binaries.sh之后做,定制完不要再跑apply_binaries.sh,它会重新覆盖一批文件。 - 挂载和清理最好用
trap ... EXIT包一下,中途报错也能自动卸载,不然残留挂载点会坑到下一步的打包。 - rootfs 里想放 overlay 文件(服务单元、二进制)直接
cp -a进去就行,但 systemd 服务不会自动启用,要在 overlay 里自己放好multi-user.target.wants下的软链接。
3.3 批量设备的三件"去重"
同一个 rootfs 刷出来的设备,默认是"三胞胎":SSH 主机密钥相同、machine-id 相同、主机名相同。前两个是安全问题,第三个是运维问题。刷机前处理:
# 删掉 SSH 主机密钥,首启时重新生成
sudo rm -f rootfs/etc/ssh/ssh_host_*
# 清空 machine-id(首启时 systemd 会生成新的)
sudo truncate -s 0 rootfs/etc/machine-id
# 可选:dbus 的 machine-id 做成链接
sudo ln -sf /etc/machine-id rootfs/var/lib/dbus/machine-id
SSH 公钥直接写文件,用数字 uid/gid 设属主,不用进 chroot:
uid=$(awk -F: -v u=dev '$1==u{print $3}' rootfs/etc/passwd)
gid=$(awk -F: -v u=dev '$1==u{print $4}' rootfs/etc/passwd)
sudo install -d -m 700 rootfs/home/dev/.ssh
sudo tee -a rootfs/home/dev/.ssh/authorized_keys < ~/.ssh/id_ed25519.pub
sudo chmod 600 rootfs/home/dev/.ssh/authorized_keys
sudo chown -R "$uid:$gid" rootfs/home/dev/.ssh
主机名唯一化交给一个 first-boot 服务:首次开机时生成 SSH 主机密钥、按序列号后 6 位设置主机名(读不到序列号退到网卡 MAC,再不行用随机数)。服务单元骨架:
[Unit]
Description=Jetson first boot provisioning (host keys, hostname)
DefaultDependencies=no
After=local-fs.target
Before=sockets.target ssh.socket ssh.service network-pre.target
ConditionPathExists=!/var/lib/jetson-firstboot.done
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/jetson-firstboot.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
两个细节:
DefaultDependencies=no是为了让服务在早期就跑,避免和ssh.socket形成依赖环,不要去掉。- Ubuntu 从 22.10 起 SSH 默认 socket 激活,JetPack 7(Ubuntu 24.04)用的是
ssh.socket,JetPack 6.2(22.04)还是传统的ssh.service。Before=里两个都写上,systemd 对不存在的单元会静默忽略,不用为版本切换改配置。
脚本里记得 ssh-keygen -A 补主机密钥,flag 文件(/var/lib/jetson-firstboot.done)在结束时 touch,保证只跑一次。
4. 进入 Recovery 模式
设备没系统或系统已坏时,这步只能手工(除非有产线夹具):
- 跳线法:断电,用跳线短接板上的 FC REC 与 GND 针脚,再上电。
- 按钮法:按住 RECOVERY 按钮不放,点一下 RESET(或重新上电)。
如果设备系统还能启动、SSH 可达,可以省掉手工操作,直接从主机让它重启进 Recovery:
ssh <user>@<设备地址> sudo reboot forced-recovery
设备会直接以 Recovery 模式重新枚举(0955:7523),接着跑刷机命令即可。这个方式适合重刷和自动化场景;全新设备或系统已损坏的,只能老实用上面的手工方式。
主机上确认设备出现:
lsusb | grep -i nvidia
# Bus 001 Device 011: ID 0955:7523 NVIDIA Corp. APX
Orin Nano 的 Recovery 模式 ID 是 0955:7523(T23x 家族的规律是 0955:7X23,AGX Orin 是 7023)。注意刷写过程中设备会以 0955:7035(initrd 模式)重新枚举,这是正常的,等待循环判断"设备进入 Recovery"时匹配 7523 即可。
5. 单台刷机
sudo ./tools/kernel_flash/l4t_initrd_flash.sh --external-device nvme0n1p1 \
-c tools/kernel_flash/flash_l4t_t234_nvme.xml \
-p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
--showlogs --network usb0 jetson-orin-nano-devkit-super internal
这条命令同时刷两处:QSPI 里的 bootloader(-p 传的 qspi 配置)和 NVMe 上的系统(--external-device nvme0n1p1 + nvme 分区表)。JetPack 6.2 与 7.2 的参数一致,只有目录里的版本号不同。
几个提醒:
- 报
Invalid target board就是板型名不对,ls *.conf看 BSP 里实际有哪些(jetson-orin-nano-devkit、-super、以及较新版本里的-nvme变体)。老 BSP 没有-super配置,升级 BSP。 - 官方特别强调用高质量的 USB-C 数据线,劣质线是刷机失败的一大来源。尽量直插主机 USB 口,别过便宜 hub。
- 耗时不短,QSPI + NVMe 全套十几分钟正常,
--showlogs拉出来看进度。
6. 批量刷机:massflash
同一批设备刷同一份镜像,走 massflash 流程:先在开发机上生成一次 mfi 包,之后在任何一台 Linux 电脑上用包里的内容反复刷。
6.1 生成镜像包(只做一次)
sudo ./tools/kernel_flash/l4t_initrd_flash.sh --no-flash --network usb0 --massflash 5 \
--external-device nvme0n1p1 \
-c tools/kernel_flash/flash_l4t_t234_nvme.xml \
-p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
jetson-orin-nano-devkit-super internal
--no-flash只生成不刷机,生成阶段可以不接设备;手头有设备的话,接一台进 Recovery 在线生成也行,工具会自己读 EEPROM。--massflash 5指这个包最多同时刷 5 台,按产线并行数取。N 只是上限,后面实际刷 1 台还是 5 台都行。- 产物是
mfi_jetson-orin-nano-devkit-super.tar.gz,第 3 步预置的账号、定制全都在里面。 - 完全离线生成(不接设备)时,官方示例里通过
BOARDIDFABBOARDSKUBOARDREV环境变量提供板卡信息,替代 EEPROM 读取。
6.2 在任意刷机机上使用
刷机机不需要完整 BSP,也不用再解压 rootfs、跑 apply_binaries.sh,解压 mfi 包就够:
tar xpf mfi_jetson-orin-nano-devkit-super.tar.gz
cd mfi_jetson-orin-nano-devkit-super
sudo ./tools/kernel_flash/l4t_initrd_flash.sh --flash-only --network usb0 --massflash 5 --showlogs
把所有待刷设备进 Recovery、连好 USB,命令一跑就开始并行刷。几个来自官方文档的实用建议:
--keep保留刷机环境,后续再刷时用--reuse复用,能明显加速。- 用
ionice把刷机进程的 I/O 优先级提到最高:sudo ionice -c 1 -n 0 ./tools/...。 - 所有待刷设备的硬件版本要一致,这是官方要求。
- 并行刷写变慢或失败,通常是 hub 供电或带宽不够,减少同时数量或换好点的 hub。
- 解压出来的目录里通常自带说明,R39 文档里命令形式略有差别(
./l4t_initrd_flash.sh --flash-only --massflash 5 <target-board>),以包内说明为准。
7. 常见问题
卡在 Waiting for target to boot-up:多数是 usb0 网络没起来。检查主机防火墙(initrd flash 要用 NFS + SSH 走 IPv6 fc00:1:1::/48)、NetworkManager 是否接管了 usb0、USB 线/口是否可靠。重刷前把设备重新进一次 Recovery。
板型配置缺失:ls *.conf,找不到 -super 说明 BSP 版本旧,需要 6.2 及以后的 BSP。
刷完不是 MAXN SUPER 模式:确认用了 jetson-orin-nano-devkit-super 板型;另外电源模式要在设备上手动切到 MAXN SUPER(用 super 板型刷完后默认可能是 25W)。
密码含特殊字符:命令行里用单引号包起来。
重刷设备:NVMe 上旧分区会被覆盖,先备份数据。
Arch 特有:apply_binaries.sh 报 Exec format error,是 binfmt 没注册或 qemu-user-static 没装;报 command not found 就用 pacman -F 查缺哪个包。
8. 跨平台 GUI 能不能做?
结论先说:GUI 可以跨平台,刷机引擎不能。底层工具链是一堆 x86_64 Linux 二进制加 bash 脚本,全程依赖 Linux 内核的 USB 行为和时序,这不是 UI 层能封装掉的。评估三条路:
- Windows:官方 SDK Manager 支持 Windows,但本质是自动配好 WSL2 + usbipd-win 转发 USB。官方把"不支持刷外置存储"列为已知限制,社区反馈也两极,不太稳。
- macOS:无官方路径。有人的实测是 Apple Silicon 上跑 x86_64 虚拟机 + USB 直通失败(USB 超时),最终靠把整块 USB 控制器 PCI 直通给一台 Linux 虚拟机才成功,前提是你得有一台 x86_64 Linux 物理机。
- Docker 也绕不过:官方有
jetson-linux-flash-x86刷机容器,但宿主必须是 Linux,容器要--privileged+/dev/bus/usb+ 网络直通;Docker Desktop 在 Mac 上连 USB 直通都不完整。
所以想跨平台,现实架构是把"界面"和"刷机"拆开:
方案 A:跨平台 GUI 客户端 + Linux 刷机后端。 后端跑在连着 Jetson 的 Linux 机器上(可以是那台开发机),提供本地 HTTP/WebSocket 服务;GUI 负责命令编排、流式日志、进度展示。USB 检测发生在后端。适合产线上"一台刷机机 + 多台监听工位"的布局。
方案 B:GUI 只做 Linux 版。 直接在刷机机上跑,解锁后调脚本。Tauri、Electron、Qt 都能做,Electron 系可以参考官方 SDK Manager(Electron 技术栈)和 balena 的 jetson-flash(要求 x86 Linux 宿主)。
技术选型对比(针对"调外部命令 + 长日志流 + USB 感知 + 提权"这个场景):
| 框架 | 体积 | 调进程/日志流 | USB 生态 | 备注 |
|---|---|---|---|---|
| Electron | 大(~85MB 起) | child_process 成熟 | node-usb 成熟 | 有先例(SDK Manager、balenaEtcher) |
| Tauri | 小(~3MB 起) | 官方 shell 插件 + sidecar | 需自己用 Rust nusb/rusb |
体积优势大,USB 要自己啃 |
| Qt | 中 | QProcess 成熟 | libusb 绑定 | 桌面老牌,Linux 原生体验好 |
| Flutter | 中 | dart:io Process | libusb FFI 小众 | 可行但生态不是最优 |
| Avalonia | 中 | Process API 成熟 | LibUsbDotNet | .NET 技术栈可选 |
提权方面,Linux 桌面建议走 polkit/pkexec 而不是裸 sudo,刷机后端封装成 systemd 服务或独立的特权组件更干净。
另一个值得研究的成熟方案是 balena 的 jetson-flash :把工具链装进 Docker 容器(宿主只需 Docker 和 USB 直通),CLI 用"设备字典"组织设备差异(每台设备带 BSP 直链、分区映射、刷写介质),目前已支持到 L4T 39.2.0 的 Orin Nano NVMe。它的配置驱动思路、工作目录管理和进度呈现,都值得自研工具时参考。
顺带一提,NVIDIA 官方在 NVIDIA-AI-IOT/jetson-bsp-skills 仓库里放了一套给 AI agent 用的刷机技能(含 recovery 检测、EEPROM 校验、默认用户预置的完整流程),如果真要做 GUI 或自动化平台,那套流程设计值得抄。
小结
- 单台/批量、预置账号、跳过向导,整套流程在 Arch 上完全可行,依赖映射见第 1 节。
- 关键修正:
--accept-license必须加、mfi 包是.tar.gz、rootdev 用internal、N 是并发上限。 - GUI 若要做跨平台,定位成"远程控制台":界面随便跨,刷机引擎乖乖放在 Linux x86_64 物理机上。
参考
- NVIDIA Jetson Linux Developer Guide: Flashing Support (R36.5.2) (含 Skipping oem-config、massflash 章节)
- Jetson Orin Nano Developer Kit Quick Start (JetPack 7.2.1)
- NVIDIA 论坛:rootdev 参数 internal/external 的区别
- Flashing Jetson Orin Nano in Virtualized Environments
- balena-os/jetson-flash (Docker 化刷机 CLI,已支持 L4T 39.2.0 的 Orin Nano NVMe)
- NVIDIA-AI-IOT/jetson-bsp-skills (官方 agent 刷机技能集)
本文流程整理自 NVIDIA 官方文档与社区实践,相关脚本只在 Arch 上做过语法级检查;第一次上机建议先单台验证参数,尤其是串行号读取和 first-boot 服务在不同 JetPack 版本上的行为。
后续:这套流程我在用 Go 桌面框架 MyGo 工具化,项目叫 nvflasher(批量刷写、rootfs 预置、代理下载这些都做进去),等能跑通了单独写一篇。