关于 jetson-validate-image
用于验证定制 Jetson BSP 镜像的占位 Skill,面向检查磁盘产物和已刷写设备的工程师。DUT 访问约定与 UART 辅助脚本已定义,大部分验证流程仍是框架。
主要功能
- 01静态 BSP 产物检查
- 02通过 SSH 和 UART 访问 DUT
- 03启动与模块一致性检查
- 04汇总测试结果和日志
适用场景
- 01刷写前检查 BSP 镜像
- 02验证刚刷写的 Jetson
- 03运行冒烟或回归检查
- 04在 CI 中设置定制批次门禁
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ nvidia/ skills --skill jetson-validate-image安装后在 Claude Code 中输入/jetson-validate-image确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
BENCHMARK.mdskill-card.mdskill.oms.sigevals/evals.jsonreferences/dut-access.mdscripts/uart_session.py详细介绍
验证 BSP 镜像
状态: DUT 访问约定已稳定;其余验证流程仍为骨架。
目的
确认刚完成定制的 BSP 已正确落地——既作为静态产物存在于磁盘上,也已在目标设备上作为运行系统启动——无需重新推广或重新刷写。本流程构成 Setup → Customize → Build → Deploy 流水线中的 Deploy 验证阶段(流水线视图请参见
../../context/bsp-customization-workflow.md),并且可以独立重复运行。
前提条件
- 已激活目标平台配置文件,并解析出
bsp_image:(先运行/jetson-init-image)。 - 对于仅静态验证范围:无需其他条件;此技能会直接读取
<bsp_image.root_path>。 - 对于目标设备验证范围:
/jetson-flash-image已将暂存的 BSP 推送到 DUT。- 已在激活的配置文件中编写
dut_access:块(或在运行时交互式填写——参见## DUT access)。 - 根据传输方式准备好主机工具:SSH 需要
ssh+sshpass;UART 需要 Python 3.6+ 和pyserial。 - 当
auth=password或sudo.method=password时,主机上必须能够解析所需的环境变量(password_env/sudo.password_env用于指定环境变量名称;切勿在 YAML 中内联机密)。
调用时机
- 在
jetson-flash-image已将 BSP 写入目标设备后。 - 用户明确要求在已刷写的设备上进行验证、测试或运行冒烟/回归检查时。
- 在宣布一批定制成果可交付前,将其用作 CI 门禁。
说明
以下流程为骨架。
- 读取活动目标,遵循前置条件约定。
- 选择验证范围——静态验证(针对磁盘上的
bsp_image,无需 DUT)和/或目标设备验证(DUT 必须从刚刷写的映像启动)。 - 静态检查(若在范围内):
- 必需的工件是否存在于
<bsp_image.root_path>/Linux_for_Tegra/下的预期路径。 - DTB/模块校验和或签名验证。
- 根据每块板的
.conf中的 XML 检查分区布局是否合理。 - 对照
bsp_image检查覆盖层暂存的输出(提升后两者应完全一致)。 - Initramfs ↔ 内核 + rootfs 模块一致性。 提取
<bsp_image>/Linux_for_Tegra/bootloader/l4t_initrd.img和<bsp_image>/Linux_for_Tegra/rootfs/boot/initrd;根据已提升的bsp_image状态验证以下三项不变量: (a)<bsp_image>/Linux_for_Tegra/kernel/Image与<bsp_image>/Linux_for_Tegra/rootfs/boot/Image必须逐字节一致——此处出现偏差意味着跳过了“将内核 Image 镜像到 rootfs”步骤,后续任何 initramfs 刷新都是基于先前的内核构建的。 (b) 对两个 initrd 映像中包含的每个模块路径,其字节内容/md5 必须与<bsp_image>/Linux_for_Tegra/rootfs/lib/modules/<ver>/下的文件匹配——任何偏差都意味着这些模块会在早期启动时被覆盖。 (c) 每个 initramfs*.ko上标记的 vermagic 必须与从<bsp_image>/Linux_for_Tegra/kernel/Image获取的UTS_RELEASE匹配(例如,使用strings解析Linux version …字符串)——vermagic 不匹配意味着内核Image已刷新,但未重新运行l4t_update_initrd.sh,模块将无法加载,并报错“disagrees about version of symbol …”。/jetson-promote-image对kernel/Image或rootfs/lib/modules/以及内核 Image 镜像步骤设置的门控会排除所有这三种故障模式;若此处发现偏差,请告知用户并引导其重新执行干净的提升流程。参见../jetson-promote-image/SKILL.md。
- 必需的工件是否存在于
- 目标设备检查(若在范围内):
- 按照下方的
## DUT access部分连接到 DUT——根据活动配置文件的dut_access:块确定传输方式(ssh/uart)、凭据和 sudo 方法(若字段缺失/标记为prompt,则交互式询问),然后运行连接探测;若失败则停止。 - 确认已启动并进入用户空间。
- 运行选定的测试套件(冒烟测试、回归测试、针对每项自定义配置的专项测试、临时测试)。
- 已加载模块的 srcversion 偏差。 对于已知经自定义配置重新构建的模块,将 DUT 上的
cat /sys/module/<name>/srcversion与modinfo /lib/modules/$(uname -r)/.../<name>.ko | awk '/srcversion/ {print $2}'的读取结果进行比较。不匹配意味着内核运行的是较旧副本,而 rootfs 中的是较新副本——这几乎总是因为 initramfs 过时(引导加载程序侧的 initrd 携带了自定义配置前的模块,该模块先被加载,而 rootfs 中的副本无法替换已运行的模块)。建议重新运行/jetson-promote-image并重新刷写。注意:有些模块不会提供srcversion;可改为将校验值与通过提取/sys/module/<name>/sections/下的区域而获得的内核已加载二进制进行核对,或通过行为进行比较(例如,新版本已知会提供的 printk、sysfs 节点或 DT 属性)。 - 运行中的内核与 rootfs 内核
Image。 将 DUT 上的cat /proc/version(或uname -v)与从/boot/Image提取的Linux version …字符串(strings /boot/Image | grep -m1 'Linux version')进行比较。不匹配——通常是构建时间戳/LOCALVERSION不同——意味着引导加载程序运行的内核Image比 rootfs 中的旧,这几乎可以确定是因为新Image已提升,但 initramfs/extlinux.conf/QSPI 启动分区未刷新。此时 rootfs 中的模块会具有不同的 vermagic,之后任何针对新内核构建的.ko执行modprobe都会失败。建议运行/jetson-promote-image并重新刷写。 - 用户空间中的 dmesg 可读性。 Ubuntu 22.04 将
kernel.dmesg_restrict=1设为默认值;非 root 用户读取dmesg会返回“Operation not permitted”,且命中数会被静默置零。所有基于dmesg的检查都必须使用sudo运行(或通过sudo sysctl kernel.dmesg_restrict=0临时降低限制)。在验证层指出这一点,可确保基于 printk 的自定义配置检查结果准确。 - 收集结果、日志和工件。
- 按照下方的
- 摘要:逐项检查的通过/失败情况、总体结论,以及日志和工件的存放位置。
DUT 访问
目标设备端需要一种方式来访问刚刷写好的 DUT。
目前支持两种地位相同的传输方式:ssh(首选)和
uart(适用于没有网络的 DUT)。
相关约定已确定,但为使 SKILL.md 保持在 Agent 路由预算内,
内容存放于
references/dut-access.md。该参考文档涵盖:
dut_access:配置文件架构(ssh、uart、sudo、workdir)。- 解析顺序(配置文件 → 环境变量 → 交互式提示),以及 完整的拒绝触发条件表。
- 强制连接探测(
uname -r+cat /etc/nv_tegra_release)及其输出验证规则。 - 各传输方式的文件传输(
scp与通过 tty 传输 base64,并对100 KB 的文件发出警告)。
- 按
sudo.method× 传输方式划分的 sudo 调用矩阵。 scripts/uart_session.py遵循的 UART 实现约定(登录 / exec / push / pull 状态机、退出码、健壮性说明)。- 安全说明(密码处理、主机密钥固定)。
可用脚本
| 脚本 | 用途 | 参数 |
|---|---|---|
scripts/uart_session.py | 目标设备端的 UART 传输黑盒:登录、命令执行(可选 sudo)以及通过 tty 传输 base64 文件。在 DUT 没有网络或 ssh 不可用时替代 ssh。 | --tty <dev> [--baud <n>] --user <name> --password-env <ENVVAR> [--sudo-password-env <ENVVAR>] [--shell-prompt <regex>] [--lock-strategy refuse|wait] <probe|exec|push|pull> [...] |
调用方式(该技能将脚本作为子进程调用——在 Agent 运行时术语中为 run_script()):
# run_script: probe the DUT over UART
DUT_UART_PASSWORD_ENV=DUT_UART_PWD \
DUT_UART_PWD="$(read -rs -p 'UART login pw: '; echo "$REPLY")" \
scripts/uart_session.py \
--tty /dev/ttyACM0 --baud 115200 \
--user ubuntu --password-env DUT_UART_PASSWORD_ENV \
probe
# run_script: exec a sudo command and capture exit code
scripts/uart_session.py --tty /dev/ttyACM0 --user ubuntu \
--password-env DUT_UART_PASSWORD_ENV \
--sudo-password-env DUT_SUDO_PWD \
exec --use-sudo 'dmesg | tail -200'
脚本的退出码即为约定内容——请参阅
UART implementation contract 中的退出码表。
示例
仅静态验证(无需 DUT):
/jetson-validate-image
> static checks only against the staged BSP
刚刷写完 DUT 后通过 SSH 进行目标设备端验证:
/jetson-flash-image
↓
/jetson-validate-image
> on-target checks via dut_access.ssh
通过 UART 进行目标设备端验证(DUT 无网络):
/jetson-validate-image
> use the uart transport at /dev/ttyACM0; the dut_access.uart block
in the profile already has the tty and login_password_env wired up
限制
- 占位技能——目前仅确定了 DUT 访问约定和
uart_session.py辅助脚本。静态检查列表、 测试套件选择、结果接收端布局和通过 / 失败策略 均在## Open items下跟踪,且可能变更。 - UART 文件传输通过逐字节发送 base64 实现,波特率为 115200 时速率约为 10 KB/s——源文件大于 100 KB 时会发出警告,但仍会继续传输。对于 大批量传输,请切换到 SSH 传输方式。
uart_session.py在每次子命令调用时都会打开并关闭 tty (每次登录约需 1–2 s)。验证过程若通过 UART 运行超过 10 条 命令,则耗时较长;建议使用 SSH。- SSH 使用
StrictHostKeyChecking=accept-new,并为每个配置文件使用单独的known_hosts文件。指纹变更时会拒绝连接——通常表示 DUT 已重新刷写(主机密钥已重新生成)或 IP 已重新分配。请手动移除对应配置文件中的条目, 不要自动接受。 - 密码不会直接写入配置文件 YAML——只会持久化
password_env(环境变量名)。auth=prompt/sudo.method=prompt会让密码出现在对话日志中, 用户需自行负责管理。 transport=uart搭配lock_strategy=steal尚未实现 (需要发送控制字符,可能会破坏占用者的状态)。
故障排除
| 错误 | 原因 | 解决方案 |
|---|---|---|
no DUT transport configured(拒绝) | 请求了目标设备范围,但在配置文件/环境变量/交互式提示中均未解析出 dut_access.transport | 在活动配置文件中编写 dut_access:,或重新运行并仅指定静态范围。 |
$PASSWORD_ENV is unset(拒绝) | auth=password(或 sudo.method=password)指定的环境变量未在主机上导出 | 调用此技能前运行 export <ENVVAR>=...;切勿将密码直接写入 YAML。 |
tty held by another process(拒绝,退出码 3) | lock_strategy=refuse,且 fuser 检测到另一个占用进程(minicom、picocom、getty) | 关闭占用进程(只有在确认进程内容后才运行 sudo fuser -k <tty>),或使用 lock_strategy=wait 重新运行。 |
pyserial import failed(退出码 4) | 已选择 UART 传输,但此技能的 Python 环境未安装 pyserial | 运行 apt install python3-serial 或 pip install pyserial。 |
| SSH 指纹被拒绝 | DUT 的主机密钥自按配置文件固定 known_hosts 后发生了变化(通常是重新刷写所致) | 从 <workspace>/target-platform/<profile-stem>.known_hosts 中删除匹配行并重新运行;首次连接时将接受新密钥。 |
login failed(退出码 128) | UART 探测在超时时间内未能匹配登录提示符或 shell 提示符 | 确认 DUT 已通电并启动至用户空间;如果 DUT 使用了不同寻常的 PS1,检查 --shell-prompt 覆盖项;确认 --user 对应真实账户。 |
DUT not booted from the just-flashed BSP(警告/拒绝) | DUT 上的 /etc/nv_tegra_release 与 bsp_image.version 不匹配 | 重新运行 /jetson-flash-image,并确认 DUT 确实已断电重启并进入新的 BSP,而不是之前的版本。 |
sudo prompt detected but no sudo password configured(退出码 130) | 将 --use-sudo 传给 uart_session.py exec,但未设置 --sudo-password-env,且 DUT 用户没有 NOPASSWD 权限 | 在配置文件中设置 sudo.method=password 和 sudo.password_env,或通过 DUT 上的 /etc/sudoers.d/ 授予 NOPASSWD 权限。 |
command timed out(退出码 129) | 长时间运行的 DUT 命令超出了脚本的超时时间 | 直接通过 ssh 传输方式运行命令,或将命令拆分为较短步骤。 |
参考资料
references/dut-access.md— 完整的 DUT 访问规范:配置文件架构、解析顺序、探测、文件传输、sudo、UART 实现和安全性。../../context/target-platform-contract.md— 目标平台规范。../../context/bsp-customization-workflow.md— 工作区编辑协议(此技能负责部署的最后阶段)。../jetson-build-source/SKILL.md— 构建器;生成此技能用于验证的构件。
评论
支持 Markdown