📺 B站:博主个人介绍
📘 博主书籍-京东购买链接*:Yocto项目实战教程
📘 加博主微信,进技术交流群: jerrydev
Jetson A/B 分区与回滚机制:官方视角的总结与实战记录(L4T 36.x / Orin / UEFI)
写作定位:总结 / 记录(像项目笔记一样可直接落地),重点把“分区—元数据—判定—回滚—验证闭环”讲清楚。
适用范围:JetPack 6(L4T 36.x),Orin(t234),UEFI(官方文档称
cpu-bootloader)。
0. 先把结论放在最前面(避免绕)
- Jetson 的 A/B 不是一个开关,而是两层机制:
- Bootloader A/B:保护启动链固件分区,失败回退发生在 Linux 之前。
- Rootfs A/B:保护根文件系统
APP/APP_b,失败计数常见为最多 3 次(由刷机参数决定),计数与切换在cpu-bootloader阶段完成。
-
nvbootctrl是用户态入口,可以查看/设置槽位;但“到底从哪个槽启动”由 bootloader 链路按同一套元数据执行。 -
“最终使用的分区表”不是你看到的某个模板 XML,而是刷机时选中的 XML 经变量替换后生成的最终展开版(刷机过程中会生成最终 XML,写入 GPT 的分区名与大小以它为准)。
官方文档对应章节(建议你按这个顺序阅读):
- Partition Configuration(分区配置 / XML 模板与最终展开)
- Update and Redundancy(Bootloader A/B)
- Root File System Redundancy(Rootfs A/B:
APP/APP_b、3 次机制、verify 闭环)
在这里插入图片描述
1. 术语与对象:你要保护的到底是什么
1.1 Bootloader A/B(启动链固件冗余)
- 对象:启动链关键分区(典型命名
A_cpu-bootloader/B_cpu-bootloader,以及其他 A_/B_ 配对分区集合,取决于你的分区表)。 - 阶段:BootROM / MB1 / MB2 / UEFI(
cpu-bootloader)这条链路内。 - 目标:即使一套链路坏了,也能从另一套把系统拉起来。
- 行为:槽位会维护一套状态字段(active/priority/tries/success/bootable 等);失败会导致当前槽被标记不可用并回退到另一槽。
1.2 Rootfs A/B(根文件系统冗余)
-
对象:根文件系统分区
APP和APP_b。 -
阶段:主要在
cpu-bootloader尝试启动 Linux 的流程中。 -
目标:当前 rootfs 启动失败时自动切到备用 rootfs。
-
关键机制:
- 最大重试次数常见默认 3(由
ROOTFS_RETRY_COUNT_MAX配置,范围 0~3)。 - retry count 存在 scratch register(跨 warm reset 保留、掉电清空)。
- 成功标记闭环:系统启动成功后,Linux 侧服务会执行
nvbootctrl verify来完成“成功确认/清理计数/更新状态”等动作。
- 最大重试次数常见默认 3(由
1.3 重要提醒:两层机制不是“同一个计数”
- Bootloader A/B:维护的是启动链槽位状态机字段。
- Rootfs A/B:维护的是 rootfs 尝试次数(retry count)与当前 rootfs 槽位。
它们可能联动(尤其是 A↔B 配对设计),但不是同一套计数。
2. 分区:先确认你是否真的启用了 Rootfs A/B
2.1 目标机上最权威的确认:看 GPT 分区标签
在目标机上执行:
lsblk -o NAME,SIZE,PARTLABEL,PARTUUID,MOUNTPOINTS
# (可选)更直观打印 GPT 表
sudo sgdisk -p /dev/nvme0n1 2>/dev/null | head -n 120 || true
sudo sgdisk -p /dev/mmcblk0 2>/dev/null | head -n 120 || true
你要得到一个非常明确的结论:
-
是否存在
APP_b?- 有:Rootfs A/B 启用 → 才存在“3 次失败回退”这套机制。
- 没有:Rootfs A/B 未启用 → 只讨论 Bootloader A/B(以及你的内核/根分区本身是否冗余由你自己方案决定)。
2.2 主机侧:到底是哪份 XML 定义了分区
你在 Linux_for_Tegra/tools/kernel_flash/ 看到的例如:
flash_l4t_t234_nvme.xmlflash_l4t_t234_nvme_rootfs_ab.xmlflash_l4t_t234_nvme_rootfs_enc.xmlflash_l4t_t234_nvme_rootfs_ab_enc.xml
它们是模板。真正落地:
- 由
<board>.conf(内部存储)与l4t_initrd_flash.sh -c(外部存储)共同决定。 - 刷机时会把模板 XML 中的关键字替换成实际值,生成最终展开版 XML;GPT 按最终展开版写入。
实战记录建议:
- 你每次量产/版本发布,都把“刷机命令 + 最终展开 XML + 目标机 GPT 分区列表”存档,这能极大降低后续排查成本。
3. 元数据:slot/tries/success 到底存在哪里
这一节只保留“对排查最有用”的结论。
3.1 Rootfs retry count:在 scratch register(易失)
-
scratch register 的特点:
- warm reset 保留(所以连续失败可以计数)
- 掉电清空(冷启动通常会重新初始化)
-
所以你会看到典型现象:
- 连续 warm reboot 会消耗 tries
- 彻底断电再上电,tries 可能重置
3.2 Bootloader A/B 的槽位选择:冷启动与 warm 启动读取路径不同
- 冷启动:BootROM 从非易失来源(例如 BR-BCT/相关结构)获取当前槽位。
- warm 启动:BootROM 会使用 scratch register 缓存的槽位信息继续启动。
这一点决定了你在排查“为什么掉电后槽位又变了/为什么 warm reboot 一直回退”的现象时,必须把“冷启动 vs warm 启动”区分开。
3.3 Linux 侧闭环:nvbootctrl verify 与系统服务
Rootfs A/B 不是“bootloader 计数完就永远正确”,它需要 Linux 侧在启动成功后做确认/清理。官方流程中会有相关服务(常见为 nv-l4tbootloader-config.service)去调用 nvbootctrl verify。
实战技巧:如果你开机后立刻
reboot,可能导致 verify 服务没跑完,从而出现“明明能进系统,但 tries 继续被消耗”的错觉。
4. 判定与回滚:官方逻辑用“时间线”理解最清晰
4.1 Bootloader A/B:更早阶段的失败回退
时间线(只保留关键点):
- BootROM 选择 boot slot(冷启动/温启动路径不同)。
- MB1/MB2/UEFI 按该 slot 加载启动链组件。
- 如果该 slot 的链路失败(不可加载/校验失败/不可启动等),状态机将其标记为不可用并回退到另一 slot。
你在用户态最容易看到的证据:
sudo nvbootctrl dump-slots-info
你要重点关注的是:
- current / active slot
- priority
- tries_remaining
- successful / bootable
4.2 Rootfs A/B:3 次机制发生在 cpu-bootloader 尝试启动 Linux 时
时间线:
cpu-bootloader根据当前 rootfs slot 组织启动参数。- 尝试启动 Linux(加载 kernel/dtb/initrd,挂载 rootfs)。
- 若失败 → retry count 递减。
- 达到阈值(常见 3 次耗尽)→ 切到另一 rootfs slot 再试。
- 两边都失败 → 进入 recovery 路径(具体取决于配置)。
- 成功进入 Linux 后 → Linux 服务执行 verify,清理/确认状态,形成闭环。
你在用户态的证据:
sudo nvbootctrl -t rootfs dump-slots-info
systemctl status nv-l4tbootloader-config --no-pager || true
4.3 “触发 rootfs 回滚时,是只切 rootfs 还是整套 A/B 都切?”
官方概念上:rootfs fail-over 的动作是“切 rootfs slot”。
工程上常见观测:如果你的平台配置是 Bootloader A ↔ Rootfs A、Bootloader B ↔ Rootfs B 的配对关系,那么 rootfs 切换往往表现为“整套 A↔B 都跟着变”。
结论写法建议(更不容易被挑错):
- 触发原因:rootfs 失败计数到阈值。
- 切换对象:rootfs slot。
- 外观表现:在配对配置下,bootloader slot 可能同步变化(以
nvbootctrl两个视角的输出为准)。
5. 配置点:在哪里设置“3 次”以及如何验证
5.1 3 次阈值怎么设置
在刷机阶段设置(不是运行时改 /etc/*.conf):
ROOTFS_AB=1:启用 rootfs A/BROOTFS_RETRY_COUNT_MAX=3:设置最大重试次数(0~3)
你使用
flash.sh或l4t_initrd_flash.sh,本质都是在刷机流程里把这一组配置落进启动链路可读的状态。
5.2 验证:把“分区 + 两个视角 + cmdline”一次性对齐
# 分区是否存在 APP_b
lsblk -o NAME,SIZE,PARTLABEL,MOUNTPOINTS
# bootloader 视角
sudo nvbootctrl dump-slots-info
# rootfs 视角
sudo nvbootctrl -t rootfs dump-slots-info || true
# root= 到底指向哪个分区
cat /proc/cmdline | tr ' ' '
' | grep -E '^root='
你要在笔记里写清楚三行:
- GPT:APP / APP_b 是否存在
- bootloader current/active
- rootfs current/active/tries
6. 代码与落地:给一套“判断成功”的可用实现
你要求“给出具体判断代码”,这里提供两类:
- Linux 侧(推荐量产):健康检查 +
nvbootctrl verify闭环 - Bootloader 侧(说明逻辑骨架):状态机伪代码(用于你理解/审查/自研时对齐)
6.1 Linux 侧:健康检查脚本(建议用于产品)
核心思想:不要把“能进 userspace”当成功;让关键服务 + 业务自检通过后再执行 verify。
/usr/local/sbin/ab-healthcheck.sh:
#!/bin/bash
set -euo pipefail
LOG=/var/log/ab-healthcheck.log
log() {
echo "[$(date -Iseconds)] $*" | tee -a "$LOG"
}
# 1) rootfs 必须 rw
if ! mount | grep -E ' on / .*\(rw,' >/dev/null; then
log "rootfs is not rw => FAIL"
exit 1
fi
# 2) 关键服务(按你的产品修改)
REQUIRED_SERVICES=(
"network-online.target"
)
for s in "${REQUIRED_SERVICES[@]}"; do
if ! systemctl is-active --quiet "$s"; then
log "service $s not active => FAIL"
exit 1
fi
done
# 3) 业务自检(可选)
if command -v curl >/dev/null 2>&1; then
if curl -fsS --max-time 2 http://127.0.0.1:18080/health >/dev/null 2>&1; then
log "app health OK"
else
log "app health FAIL"
exit 1
fi
else
log "curl not found => skip app endpoint check"
fi
# 4) 成功闭环:verify
if command -v nvbootctrl >/dev/null 2>&1; then
log "nvbootctrl verify"
nvbootctrl verify || true
log "PASS"
exit 0
fi
log "nvbootctrl not found => FAIL"
exit 1
对应 systemd:/etc/systemd/system/ab-healthcheck.service
[Unit]
Description=A/B healthcheck then verify
After=multi-user.target network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/ab-healthcheck.sh
[Install]
WantedBy=multi-user.target
启用:
sudo chmod +x /usr/local/sbin/ab-healthcheck.sh
sudo systemctl daemon-reload
sudo systemctl enable --now ab-healthcheck.service
记录型建议:
- 在量产镜像里把这个服务作为“业务启动完成后的最后一道门”。
- 把日志落到
/var/log/ab-healthcheck.log,方便复盘为何某次被判失败。
6.2 Bootloader 侧:最小状态机伪代码(用于理解与对齐)
注意:以下为逻辑骨架,不是 NVIDIA 源码拷贝。你做 bootloader 内强制切换时,必须读写与
nvbootctrl同一套元数据,否则必然状态不一致。
#### 6.2.1 槽位元数据模型(抽象)
```c
typedef struct {
uint8_t priority;
uint8_t tries_remaining;
bool successful;
bool bootable;
} Slot;
typedef struct {
Slot slot[2];
uint8_t active; // 0/1
} AbMeta;
6.2.2 选择槽位(抽象)
int pick_slot(AbMeta *m)
{
// 1) active 不可启动则切换
if (!m->slot[m->active].bootable) {
m->active ^= 1;
}
// 2) priority 高者优先
int a = 0, b = 1;
int pick = (m->slot[a].priority >= m->slot[b].priority) ? a : b;
if (!m->slot[pick].bootable)
pick ^= 1;
// 3) tries 用尽且未 successful => 标不可启动并回退
if (!m->slot[pick].successful && m->slot[pick].tries_remaining == 0) {
m->slot[pick].bootable = false;
pick ^= 1;
}
return pick;
}
6.2.3 Rootfs retry(抽象:max=3)
typedef struct {
uint8_t a_tries; // 0..3
uint8_t b_tries; // 0..3
uint8_t current; // 0/1
uint16_t magic; // 0xFACE
} RootfsRetry;
RootfsRetry read_retry_from_scratch(void);
void write_retry_to_scratch(RootfsRetry r);
int rootfs_pick_with_retry(int current_slot, int max_retry)
{
RootfsRetry r = read_retry_from_scratch();
if (r.magic != 0xFACE) {
r.magic = 0xFACE;
r.a_tries = max_retry;
r.b_tries = max_retry;
r.current = current_slot;
write_retry_to_scratch(r);
}
if (r.current == 0) {
if (r.a_tries > 0) r.a_tries--;
if (r.a_tries == 0) r.current = 1;
} else {
if (r.b_tries > 0) r.b_tries--;
if (r.b_tries == 0) r.current = 0;
}
write_retry_to_scratch(r);
return r.current;
}
你做 bootloader 内“强制切换”时,至少要保证:
- 切换的是同一套 active/current
- tries/priority/success/bootable 同步维护
- Linux 启动成功后仍能通过 verify 闭环把状态收敛
7. 排查套路(记录式清单)
7.1 三步定位你遇到的是哪一层回滚
-
看分区:是否存在
APP_b。 -
看两份输出:
nvbootctrl dump-slots-info(bootloader)nvbootctrl -t rootfs dump-slots-info(rootfs)
-
看服务闭环:
systemctl status nv-l4tbootloader-configjournalctl -b -u nv-l4tbootloader-config
7.2 常见坑(按官方机制解释)
- 开机立刻 reboot:verify 没完成 → tries 被误消耗。
- 只改 active 不维护状态字段:看似切槽,实则下一轮被状态机回写覆盖。
- 只看模板 XML 不看最终分区/GPT:以为启用 rootfs_ab,实际没
APP_b。
8. 你可以直接用于项目评审的 Checklist
- GPT 分区中存在
APP_b(若需要 rootfs A/B)。 -
nvbootctrl dump-slots-info与nvbootctrl -t rootfs dump-slots-info输出可解释且一致。 - 能手动切 bootloader slot 并验证生效。
- 能模拟 rootfs 启动失败并观察 tries 递减与切换。
-
nv-l4tbootloader-config.service正常完成;业务健康检查后执行 verify。 - 断电冷启动与 warm reboot 的行为差异已记录并可解释。
9. 结尾:建议你贴这 3 段输出,我可以按“官方模型”逐行解读
sudo nvbootctrl dump-slots-info
sudo nvbootctrl -t rootfs dump-slots-info
lsblk -o NAME,SIZE,PARTLABEL,MOUNTPOINTS
我会直接给你一个“结论表”:
- 你当前启用了哪几层 A/B
- 当前槽/下次槽分别是什么
- tries/success 字段是否处于健康状态
- 若要做产品化健康检查,应该把成功判定挂在哪个服务之后
📺 B站:博主个人介绍
📘 *博主书籍-京东购买链接**:[Yocto项目实战教程](https://item.jd.com/15020438.html)
📘 **加博主微信,进技术交流群**: **jerrydev**
---
340

被折叠的 条评论
为什么被折叠?



