NVIDIA Jetson设备刷写利器:l4t_initrd_flash.sh参数解析

掌握嵌入式开发的关键工具,解锁高效设备部署新姿势

在NVIDIA Jetson嵌入式开发领域,l4t_initrd_flash.sh 脚本是开发者进行设备刷写的核心工具之一。
与传统flash.sh不同,它采用initrd(initial ramdisk)技术,通过USB引导设备进入RAM中的Linux环境,实现对eMMC或NVMe等存储器的直接写入操作。
本文将深入解析该脚本的各项参数及其应用场景。

一、脚本核心工作机制与工作流程

l4t_initrd_flash.sh属于L4T(Linux for Tegra)工具链的一部分,其工作流程分为两个关键阶段:

  1. 初始化阶段:通过--no-flash参数生成刷写包
  2. 刷写阶段:使用--flash-only将映像刷入设备

这种分离设计允许开发者预先生成刷写包,然后在多台设备上重复使用,显著提高批量生产效率。

1.1 与传统flash.sh的本质区别

# 传统刷写流程(依赖U-Boot)
sudo ./flash.sh jetson-agx-orin-devkit internal

# initrd刷写流程(RAM环境操作)
sudo ./tools/kernel_flash/l4t_initrd_flash.sh --external-device nvme0n1p1 -c flash_l4t_t234_nvme.xml jetson-orin-nano-devkit nvme0n1p1
  • 无Bootloader依赖:通过USB将设备引导至RAM中的Linux环境(initrd),直接在内存中操作存储设备
  • 双阶段分离:生成镜像包(--no-flash)与刷写设备(--flash-only)解耦,支持预编译和批量部署
  • 硬件识别机制:通过USB VendorID(0955)和ProductID精准识别Jetson设备

1.2 核心工作流程

单设备
多设备
设备进入Recovery模式
脚本初始化
参数解析
安装udev规则
扫描USB设备
检测设备数量
直接刷写
并行刷写
日志记录

二、参数详解与使用场景

设备与存储相关参数

参数缩写说明应用场景
--external-device指定外部存储设备标识符刷写NVMe SSD:--external-device nvme0n1p1
-c <config_file>-c外部存储设备的分区布局配置文件定义NVMe分区结构:-c flash_l4t_t234_nvme.xml
-S <size>-S设置APP分区大小(支持KiB/MiB/GiB)限制应用分区空间:-S 8GiB
-k <partition>-k仅刷写指定分区快速更新内核分区:-k kernel

2.1 设备控制参数

参数作用源码关键逻辑
--usb-instance指定物理设备标识(如1-2.4busnum-devpath组合标识设备
--direct直连模式(非USB恢复模式)跳过USB设备扫描,直接操作/dev/mmcblk0等设备节点
--massflash [N]批量刷写模式(默认上限10台)${target_board}.conf中的MAX_MASSFLASH定义上限
--device-instance设备实例编号(多设备时自动递增)instance=$((instance + 1))循环分配ID

2.2 存储配置参数

# 典型NVMe刷机命令
sudo ./l4t_initrd_flash.sh \
  --external-device nvme0n1p1 \          # 指定NVMe分区
  -c flash_l4t_t234_nvme.xml \           # 分区布局配置文件
  -S 32GiB \                             # 限制APP分区大小
  -k kernel \                            # 仅刷写内核分区
  jetson-agx-orin-devkit external
  • -c <config_file>
    外部存储分区配置文件(XML格式),定义rootfs/APP等分区布局
  • -S <size>
    设置APP分区容量(支持KiB/MiB/GiB单位),如-S 16GiB
  • -k <partition>
    选择性刷写指定分区(如-k kernel仅更新内核)

2.3 流程控制参数

参数作用组合使用场景
--no-flash仅生成刷写包不执行刷写预编译阶段:生成基础镜像包
--flash-only使用预生成镜像直接刷写生产环境:配合--no-flash生成的镜像包
--append追加外部存储配置到现有镜像包分步操作:先内部存储后追加NVMe配置
--reuse复用之前生成的工作目录快速迭代:避免重复解压基础文件
--keep刷写后保留临时文件调试:检查生成的initrd和flash布局

2.4 高级功能参数

网络刷写模式(适用于无物理接触场景)

# USB网络刷写(通过usb0虚拟网卡)
--network usb0

# 以太网刷写(指定IP和网关)
--network eth0:192.168.0.17/24:192.168.0.21
  • 原理:在主机上配置NFS服务,设备通过网络加载刷写文件

安全验证参数

  • -u <key_file>:PKC密钥文件(ODM融合设备)
  • -v <key_file>:SBK密钥(存储加密)
  • -i <key>:根文件系统加密密钥

调试辅助参数

--showlogs        # 实时显示刷写日志
--initrd          # 停留在initrd环境(不执行刷写)
  • 日志路径:Linux_for_Tegra/initrdlog/flash_${devpath}_${instance}_${ts}.log

三、关键位置参数解析

脚本需要两个必需的位置参数

  1. <board-name>:目标设备型号标识符

    • 例如:jetson-orin-nano-devkitjetson-xavier-nx-devkit-emmc
  2. <rootdev>:根文件系统目标设备

    • eMMC/SD卡:mmcblk0p1
    • NVMe SSD:nvme0n1p1
    • 外部存储:external

四、实用场景示例

场景1:刷写Jetson Orin Nano到NVMe SSD

sudo ./l4t_initrd_flash.sh --external-device nvme0n1p1 \
  -p "-c ./bootloader/t186ref/cfg/flash_t234_qspi.xml" \
  -c ./tools/kernel_flash/flash_l4t_t234_nvme.xml \
  --showlogs --network usb0 \
  jetson-orin-nano-devkit nvme0n1p1

场景2:修复文件系统挂载失败问题

当出现 “ERROR: mmcblk0p1 mount fail” 错误时:

  1. 在initrd中添加e2fsck工具
  2. 重建initrd映像
  3. 仅刷写更新后的initrd:
./tools/kernel_flash/l4t_initrd_flash.sh --flash-only \
  --initrd --showlogs \
  jetson-xavier-nx-devkit-emmc mmcblk0p1

场景3:批量生产环境刷写

# 生成刷写包
sudo ./l4t_initrd_flash.sh --no-flash --external-device nvme0n1p1 \
  -c flash_l4t_t234_nvme.xml jetson-agx-orin-devkit external

# 同时刷写多台设备
sudo ./l4t_initrd_flash.sh --massflash 5 --showlogs \
  --flash-only jetson-agx-orin-devkit external

五、注意事项与排错技巧

  1. 恢复模式准备:执行刷写前需将设备置于Force Recovery Mode

    • 长按恢复按钮 → 按下电源键 → 释放恢复按钮
  2. 文件系统兼容性

    # 刷写前检查文件系统
    e2fsck -f /dev/mmcblk0p1
    resize2fs /dev/mmcblk0p1
    
    
  3. 日志分析:刷写日志保存在Linux_for_Tegra/initrdlog/目录

    • 文件名格式:flash_<devpath>_<instance>_<timestamp>.log
  4. 设备识别失败处理:当脚本无法识别设备时

    • 检查udev规则:/etc/udev/rules.d/99-l4t-host.rules
    • 确认USB连接状态:lsusb | grep 0955
  5. 设备无法识别

# 检查udev规则
ls /etc/udev/rules.d/99-l4t-host.rules

# 验证设备进入恢复模式
lsusb | grep 0955:7[ef]23  # Orin系列设备ID
  1. 文件系统挂载失败
# 在initrd中添加e2fsck工具
cp $(which e2fsck) ${INITRD_DIR}/bin

# 重建initrd后单独刷写
./l4t_initrd_flash.sh --flash-only --initrd --showlogs \
  jetson-xavier-nx-devkit-emmc mmcblk0p1

六、与传统flash.sh的对比优势

特性l4t_initrd_flash.sh传统flash.sh
刷写机制通过initrd在RAM中操作直接写入存储设备
速度⭐⭐⭐⭐ (较快)⭐⭐⭐ (较慢)
外部存储支持✅ 原生支持NVMe/UFS/SSD⚠️ 有限支持
安全启动✅ SBK/PKC密钥注入⚠️ 基础支持
批量刷写✅ 支持多设备并行❌仅单设备
网络刷写✅ NFS/TFTP支持❌ 不支持
依赖项不依赖U-Boot需要bootloader
资源占用较高(需解压initrd)较低

结语

掌握l4t_initrd_flash.sh的深入用法,能够显著提升Jetson设备部署效率,特别是在大规模生产环境定制化存储方案中。该脚本的模块化设计(生成与刷写分离)和网络刷写支持,使其成为工业级应用的首选工具。建议结合具体硬件型号和JetPack版本,通过小规模测试验证后再部署到生产环境。

建议

  1. 环境隔离原则
    使用物理Ubuntu主机(非虚拟机)执行刷写,避免USB连接不稳定

  2. 版本匹配策略
    JetPack版本需与L4T版本严格对应(如JetPack 6.0对应L4T 36.3.x)

  3. 日志管理规范
    启用--showlogs并归档initrdlog/目录,建立设备刷写档案

  4. 安全操作警告
    慎用--erase-all:会完全擦除存储设备所有分区


附录:Linux_for_Tegra/tools/kernel_flash/l4t_initrd_flash.sh

#!/bin/bash

# SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: MIT
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

# Usage: ./l4t_initrd_flash.sh <options> [ --external-device <ext> -c <cfg> -S <SIZE> ] <target> <rootfs_dir>
# This script flashes the target using initrd

set -eo pipefail
trap cleanup EXIT

clean_up_network_flash()
{
	if [ -n "${network}" ]; then
		if [ -f /etc/exports ]; then
			sed -i -e '/^# Entry added by NVIDIA initrd flash tool/,+1d' /etc/exports
		fi
		if command -v exportfs &> /dev/null; then
			exportfs -ra
		fi
	fi
}

cleanup()
{
	remove_udev_rules
	clean_up_network_flash
}

install_udev_rules()
{
	ln -s "$(realpath "${UDEV_L4T_DIR}/99-l4t-host.rules")" /etc/udev/rules.d/
	ln -s "$(realpath "${UDEV_L4T_DIR}/10-l4t-usb-msd.rules")" /etc/udev/rules.d/
}

remove_udev_rules()
{
	rm -f /etc/udev/rules.d/99-l4t-host.rules
	rm -f /etc/udev/rules.d/10-l4t-usb-msd.rules
}

fill_devpaths()
{
	# Find devices to flash
	devpaths=($(find /sys/bus/usb/devices/usb*/ -name devnum -print0 | {
		found=()
		while read -r -d "" fn_devnum; do
			dir="$(dirname "${fn_devnum}")"
			vendor="$(cat "${dir}/idVendor")"
			if [ "${vendor}" != "0955" ]; then
				continue
			fi
			product="$(cat "${dir}/idProduct")"
			case "${product}" in
			"7018") ;; # TX2i
			"7418") ;; # TX2 4GB
			"7c18") ;; # TX2, TX2 NX
			"7019") ;; # AGX Xavier
			"7819") ;; # AGXi
			"7919") ;; # AGXi
			"7023") ;; # AGX Orin
			"7223") ;; # AGX Orin 32GB
			"7323") ;; # Orin NX 16GB (p3767-0000)
			"7423") ;; # Orin NX 8GB (p3767-0001)
			"7523") ;; # Orin Nano 8GB (p3767-0003)
			"7623") ;; # Orin Nano 4GB (p3767-0004)
			"7e19") ;; # NX
			*)
				continue
				;;
			esac
			fn_busnum="${dir}/busnum"
			if [ ! -f "${fn_busnum}" ]; then
				continue
			fi
			fn_devpath="${dir}/devpath"
			if [ ! -f "${fn_devpath}" ]; then
				continue
			fi
			# Only include devices for which the DEVNAME exists. In a container
			# environment, the DEVNAME for this device may not have been mapped
			# in, which is the case for when a device is in recovery mode, but
			# that device is not mapped into the current container.
			devname=$(udevadm info --query=property "$dir" | grep DEVNAME | cut -d= -f2)
			if [ ! -e "$devname" ]; then
				continue
			fi

			busnum="$(cat "${fn_busnum}")"
			devpath="$(cat "${fn_devpath}")"
			if [ -n "${usb_instance}" ] && [ "${usb_instance}" != "${busnum}-${devpath}" ]; then
				continue
			else
				found+=("${busnum}-${devpath}")
			fi
		done
		echo "${found[@]}"
	}))
	# Handle the "direct" device logically no difference than a device that is connected on Jetson.
	# So add the "direct" device to the Jetson device list.
	if [ -n "${direct}" ]; then
		devpaths+=("direct")
	fi
}

L4T_INITRD_FLASH_DIR="$(cd "$(dirname "${0}")" && pwd)"
L4T_TOOLS_DIR="${L4T_INITRD_FLASH_DIR%/*}"
LINUX_BASE_DIR="${L4T_TOOLS_DIR%/*}"
showlogs=0
flash_only="0"
initrd_only=""
reuse_package=""
external_only=""
no_flash="0"
target_partname=""
max_massflash=""
keep=""
network=""
direct=""
reuse=""
export initrd_flash_step=1
UDEV_L4T_DIR="${L4T_INITRD_FLASH_DIR}/host_udev"
flash_cmd="${L4T_INITRD_FLASH_DIR}/l4t_initrd_flash_internal.sh"
source "${L4T_INITRD_FLASH_DIR}"/l4t_initrd_flash.func
parse_param "$@"

remove_udev_rules

install_udev_rules

fill_devpaths

if [ "${flash_only}" = "0" ]; then
	# Generate flash images for both single flash and massflash
	echo "${flash_cmd} --no-flash $*"
	"${flash_cmd}" --no-flash "$@"
	echo "Finish generating flash package."
fi

if [ "${no_flash}" = "1" ]; then
	echo "Put device in recovery mode, run with option --flash-only to flash device."
	exit 0
fi

# Exit if no devices to flash
if [ "${#devpaths[@]}" -eq 0 ]; then
	echo "No devices to flash"
	exit 1
fi

# If we got here, that means user is doing flash in a single command. Therefore,
# check this condition.
if [ "${flash_only}" = "0" ]  && [ -z "${direct}" ]; then
   if [ ${#devpaths[@]} -gt "1" ]; then
	echo "Error, too many devices in RCM mode"
	echo "For signing and flashing, only one device is supported"
	exit 1
   fi
fi

flash_param="$(cat "${L4T_INITRD_FLASH_DIR}/${INITRD_FLASHPARAM}")"
target_board=$(awk -F" " '{print $(NF-1)}' "${L4T_INITRD_FLASH_DIR}/${INITRD_FLASHPARAM}")
CHIPID=$(LDK_DIR=${LINUX_BASE_DIR}; source "${LDK_DIR}/${target_board}.conf";echo "${CHIPID}")
ts=$(date +%Y%m%d-%H%M%S);
instance=0
get_max_flash

if [ ${#devpaths[@]} -gt "${max_massflash}" ]  && [ -z "${direct}" ]; then
	echo "Too many devices in RCM mode"
	exit 1
fi

mkdir -p "${LINUX_BASE_DIR}/initrdlog/"
for devpath in "${devpaths[@]}"; do
	fn_log="${LINUX_BASE_DIR}/initrdlog/flash_${devpath}_${instance}_${ts}.log"
	cmdarg=()
	[ -n "${keep}" ] && cmdarg+=("--keep")
	[ -n "${reuse}" ] && cmdarg+=("--reuse")
	[ -n "${initrd_only}" ] && cmdarg+=("--initrd")
	[ -n "${reuse_package}" ] && cmdarg+=("--use-backup-image")
	[ -n "${external_only}" ] && cmdarg+=("${external_only}")
	[ -n "${target_partname}" ] && cmdarg+=("-k" "${target_partname}")
	[ -n "${network}" ] && cmdarg+=("--network" "${network}")
	[ -n "${direct}" ] && cmdarg+=("--direct" "${direct}")
	[ -n "${EKB_PAIR}" ] && cmdarg+=("--ekb-pair")
	[ -n "${erase_all}" ] && cmdarg+=("--erase-all")
	if [[ "${CHIPID}" = "0x19" && "${flash_only}" = "0" && -n "${FAB}" ]]; then
		# For T194, if FAB has already been defined, T194 must not read
		# uid again otherwise, it will hang.
		cmdarg+=("--skipuid")
	fi
	cmd="${flash_cmd} ${cmdarg[*]} --usb-instance ${devpath} --device-instance ${instance} --flash-only ${flash_param}";
	echo "${cmd}"
	if [ "${max_massflash}" -eq 1 ] || [ -n "${direct}" ]; then
		eval "${cmd}" 2>&1 | tee "${fn_log}"
		echo "Log is saved to Linux_for_Tegra/initrdlog/flash_${devpath}_${instance}_${ts}.log "
		exit
	else
		eval "${cmd}" > "${fn_log}" 2>&1 &
	fi
	flash_pid="$!";
	flash_pids+=("${flash_pid}")
	echo "Start flashing device: ${devpath}, rcm instance: ${instance}, PID: ${flash_pid}";
	echo "Log will be saved to Linux_for_Tegra/initrdlog/flash_${devpath}_${instance}_${ts}.log "
	if [ "${showlogs}" -eq 1 ]; then
		gnome-terminal -- /bin/bash -c "tail -f ${fn_log}" -t "${fn_log}" > /dev/null 2>&1 &
	fi;
	if [ "${max_massflash}" -eq 1 ]; then
		break
	fi
	instance=$((instance + 1))
done

 # Wait until all flash processes done
failure=0
while true; do
	running=0
	if [ ${showlogs} -ne 1 ]; then
		echo -n "Ongoing processes:"
	fi;
	new_flash_pids=()
	for flash_pid in "${flash_pids[@]}"; do
		if [ -e "/proc/${flash_pid}" ]; then
			if [ ${showlogs} -ne 1 ]; then
				echo -n " ${flash_pid}"
			fi;
			running=$((running + 1))
			new_flash_pids+=("${flash_pid}")
		else
			wait "${flash_pid}" || failure=1
		fi
	done
	if [ "${showlogs}" -ne 1 ]; then
		echo
	fi;
	if [ "${running}" -eq 0 ]; then
		break
	fi
	flash_pids=("${new_flash_pids[@]}")
	sleep 5
done

if [ ${failure} -ne 0 ]; then
	echo "Flash complete (WITH FAILURES)";
	exit 1
fi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值