The robot's computer · Chapter 05 · Time: 3 hours (path A); unknown for path B · Level: Advanced · Status: Partly test-built
The staged Jetson Linux from chapter 4 on the robot's NVMe and booting - installed in place next to the factory system (path A, done on this robot), or flashed onto a blank drive with NVIDIA's initrd flash (path B, prepared but never run here).
This chapter puts the staged rootfs from chapter 4 onto the robot's 512 GB NVMe and makes the robot boot from it.
There are two ways. Path A is what was done on this robot on 2026-09-26: a new partition next to the factory
install, filled from bigbuddy over the network, then made the boot partition by renaming it. Every step of path A
is in the command log with its output. Path B is what you need if the NVMe is blank or replaced: NVIDIA's
initrd flash over USB with the robot in recovery mode. It was prepared on 2026-09-26 and never run.
Why the in-place install
The normal way to install Jetson Linux is to put the module into recovery mode and flash it from a PC. On this
robot the software way into recovery mode (sudo reboot --force forced-recovery) failed twice on 2026-09-26,
and the hardware way needs two pins on the carrier board that are only reachable after taking the robot apart.
The owner chose to install in place instead (docs/decisions.md2026-09-26). It keeps the factory install as
a fallback partition,APP_old, which chapter 15 later needs for the camera driver.
New idea: how a Jetson finds its operating system
The first-stage bootloader and the UEFI firmware live in a small flash chip (QSPI) on the module itself, not on
the NVMe. UEFI's boot order on this robot lists the NVMe first. On the NVMe it starts NVIDIA's L4TLauncher, which
looks for the GPT partition whose name is exactlyAPP, readsboot/extlinux/extlinux.conffrom it, and
loads the kernel and initrd named there. TheAPPENDline in that file becomes the kernel command line; its
root=PARTUUID=...tells the kernel which partition to mount as/. (Source: NVIDIA edk2-nvidia
L4TLauncher.h:36,L4TLauncher.c, as read fordocs/hardware.md.)So two things decide what boots: which partition is named
APP, and which partition theroot=line points
at. Path A uses exactly these two levers.
New idea: the four IDs of a partition
A GPT partition table can hold 128 entries. Each entry has a number (p16 is/dev/nvme0n1p16), a name
(APP_new, shown bylsblkas PARTLABEL), and a random unique GUID (PARTUUID, e.g.
ef01d088-5975-4263-b620-2d0f5137dfe1). The ext4 filesystem inside the partition has its own UUID and
label. L4TLauncher selects by name; the kernel'sroot=selects by PARTUUID. The table also records the
last usable sector: nothing can be created past it, even if the disk is bigger.
The factory flash left fourteen small partitions and one large one on the 512 GB Micron 2400. Names and order (the
robot today, after path A; p16 did not exist at the factory):
| Partition | Name | Size | What |
|---|---|---|---|
| p1 | APP (now APP_old) |
117.8G | the factory root filesystem |
| p2-p7 | A_kernel, A_kernel-dtb, A_reserved_on_user, B_kernel, B_kernel-dtb, B_reserved_on_user |
128M, 768K, 31.6M, 128M, 768K, 31.6M | kernel copies |
| p8, p9, p11, p12 | recovery, recovery-dtb, recovery_alt, recovery-dtb_alt |
80M, 512K, 80M, 512K | recovery kernel |
| p10, p13 | esp, esp_alt |
64M each | EFI system partition (mounted at /boot/efi) |
| p14, p15 | UDA, reserved |
400M, 479.5M | NVIDIA data / reserved |
| p16 | APP (path A) |
357.7G | the new root filesystem |
The names and order are the same as NVIDIA's layout file tools/kernel_flash/flash_l4t_external.xml in the BSP,
where APP has id="1" and sits after the small partitions. The factory GPT, however, only covered the first
128 GB of the disk.
Before you start
- This changes the partition table of the robot's only disk. On 2026-09-26 the owner had a full clone of the
disk first (how it was made is not recorded). Do not start without a backup you can restore.- The factory system must be running and you need a shell on it as
ubuntu. The factory image givesubuntu
passwordless sudo, which is why the commands usesudo -n(fail instead of asking).- Plug in the Ethernet cable. The new system has no Wi-Fi driver until chapter 7.
- Chapter 4 must be finished: the staged rootfs on bigbuddy, counted.
How the commands were run: from the laptop, each @robot block below was sent as
ssh -o BatchMode=yes rosorin 'bash -s' <<'EOF', the block, EOF. At that time the laptop's alias rosorin
pointed at the factory system: HostName 192.168.1.108, User ubuntu (the alias was changed at 18:52, chapter 6).
You can also type the blocks in an SSH session on the robot.
On the robot:
command -v sgdisk; sudo -n sgdisk -p /dev/nvme0n1 2>&1 | head -8
Check
From the log (17:14:03):/usr/sbin/sgdisk Disk /dev/nvme0n1: 1000215216 sectors, 476.9 GiB Model: Micron_2400_MTFDKBK512QFM Sector size (logical/physical): 512/512 bytes Disk identifier (GUID): 6A99051F-7A83-4C99-ABE0-615F90392068 Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 40, last usable sector is 250069646250,069,646 sectors of 512 bytes is 128 GB. The disk has 1,000,215,216 sectors. Three quarters of it is outside
the partition table.
If it fails: Unable to satisfy all constraints
Creating the partition straight away fails, because 128 GB-256 GB lies past the last usable sector (log 17:13:48):sudo -n parted -s -a optimal /dev/nvme0n1 mkpart APP_new ext4 128GB 256GB Warning: Not all of the space available to /dev/nvme0n1 appears to be used, you can fix the GPT to use all of the space (an extra 750145536 blocks) or continue with the current setting? Error: Unable to satisfy all constraints on the partition.The fix is
sgdisk -efirst (docs/lessons.md"Rebuild").
sgdisk -e moves the backup copy of the partition table to the real end of the disk, which also moves the last
usable sector there. It does not change any partition entry; the block below proves that by saving the entry list
before and after and comparing them. Then parted creates partition 16 from 128 GB to 256 GB and names it
APP_new. The word ext4 is only a type hint for parted; it formats nothing. -a optimal aligns the start for the
drive. partprobe tells the kernel to re-read the table.
On the robot:
set -e
sudo -n sgdisk -p /dev/nvme0n1 | sed -n "/^Number/,\$p" > /tmp/parts_before.txt
sudo -n sgdisk -e /dev/nvme0n1
sudo -n partprobe /dev/nvme0n1; sleep 1
sudo -n sgdisk -p /dev/nvme0n1 | grep "last usable"
sudo -n sgdisk -p /dev/nvme0n1 | sed -n "/^Number/,\$p" > /tmp/parts_after.txt
diff /tmp/parts_before.txt /tmp/parts_after.txt && echo "existing partition entries unchanged"
sudo -n parted -s -a optimal /dev/nvme0n1 mkpart APP_new ext4 128GB 256GB
sudo -n partprobe /dev/nvme0n1; sleep 2
lsblk -o NAME,SIZE,PARTLABEL,PARTUUID /dev/nvme0n1 | grep -E "NAME|APP"
Check
From the log (17:14:21):Warning: The kernel is still using the old partition table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8) The operation has completed successfully. Warning: Not all of the space available to /dev/nvme0n1 appears to be used, you can fix the GPT to use all of the space (an extra 6 blocks) or continue with the current setting? First usable sector is 40, last usable sector is 1000215176 existing partition entries unchangedThe
lsblklines that followed were cut off in the log; the next step's test proves p16 exists with the name
APP_new. The remaining "extra 6 blocks" warning from parted did not stop it.
The step refuses to run unless p16 is really APP_new and not mounted, then formats it ext4 with the label
APP_new.
On the robot:
set -e
test "$(lsblk -no PARTLABEL /dev/nvme0n1p16)" = "APP_new"
findmnt /dev/nvme0n1p16 && { echo "mounted, abort"; exit 1; } || true
sudo -n mkfs.ext4 -q -L APP_new /dev/nvme0n1p16
lsblk -f /dev/nvme0n1p16
On the robot:
sudo -n blkid /dev/nvme0n1p16; sudo -n dumpe2fs -h /dev/nvme0n1p16 2>/dev/null | grep -E "volume name|Block count|Filesystem state"
Check
Right aftermkfs,lsblk -fprinted the partition with empty columns (it had not caught up).blkidreads
the disk itself (log 17:14:44):/dev/nvme0n1p16: LABEL="APP_new" UUID="8ef4f905-4ef0-4749-b39e-438ac7b4993d" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="APP_new" PARTUUID="ef01d088-5975-4263-b620-2d0f5137dfe1" Filesystem volume name: APP_new Filesystem state: clean Block count: 31241216Write down your PARTUUID. It is random. Everywhere this guide shows
ef01d088-5975-4263-b620-2d0f5137dfe1,
your robot needs your value.
Format with e2fsprogs 1.46, never on Fedora
Runmkfs.ext4on the robot (Ubuntu 22.04,mke2fs 1.46.5) or in thejetson-flash:r36.4.3container, never
directly on bigbuddy. Fedora'smke2fs 1.47.3turns on the ext4 featureorphan_file, which the robot's 5.15
kernel does not support (it is missing from/sys/fs/ext4/features), and the root filesystem would not mount
(docs/restore.md"Rules", checked 2026-09-26 21:01).
The copy is one pipe through your laptop: bigbuddy packs the staged rootfs into a tar stream inside the container
(the files are root-owned there), the stream crosses the laptop, and the robot unpacks it as root into the new
partition. Nothing is written to disk in between.
On your laptop:
ssh -o BatchMode=yes rosorin 'sudo -n mkdir -p /mnt/app_new && sudo -n mount /dev/nvme0n1p16 /mnt/app_new && findmnt /mnt/app_new && sudo -n find /mnt/app_new -mindepth 1 -maxdepth 1 | grep -v lost+found | wc -l' && ssh -o BatchMode=yes bigbuddy 'docker run --rm -v /home/burgerbarn/jetson-flash/work:/work:z jetson-flash:r36.4.3 tar -C /work/Linux_for_Tegra/rootfs --numeric-owner --xattrs --acls -cpf - .' | ssh -o BatchMode=yes rosorin 'sudo -n tar -C /mnt/app_new --numeric-owner --xattrs --xattrs-include="*" --acls -xpf - && echo COPY_DONE'
Read it from left to right:
/mnt/app_new, mount p16 there, show the mount, and count what is already in it. The count0: a fresh filesystem holds only lost+found.tar -c writes the whole tree to standard output (-f -). -C changes into the rootfs first so./.tar -x reads the stream and unpacks it into /mnt/app_new.The flags carry everything a root filesystem needs besides file contents:
--numeric-owner: keep owner and group numbers exactly as they are in the staged rootfs. bigbuddy's ownburgerbarn is uid 1001, the new system's is 1000, and the factory system that unpacks the stream has its own-p: keep permission bits exactly (also setuid bits, which sudo needs).--xattrs --acls: keep extended attributes (for example file capabilities) and access control lists.--xattrs-include="*" on the unpacking side restores every attribute namespace, not only the default ones.The log ran the pipe in the background. When it was checked at 17:30, under 14 minutes after the start, it had
ended with exit code 0.
Check
Before the stream starts, the first part prints the mount and the count of existing entries (log 17:16:24):TARGET SOURCE FSTYPE OPTIONS /mnt/app_new /dev/nvme0n1p16 ext4 rw,relatime 0At the end the robot prints
COPY_DONE.
Compare file and folder counts with the numbers from chapter 4:
On your laptop:
ssh -o BatchMode=yes rosorin 'sudo -n bash -c "cd /mnt/app_new && echo files \$(find . -xdev \( -type f -o -type l \) -not -path ./lost+found | wc -l) dirs \$(find . -xdev -type d -not -path ./lost+found | wc -l)"'
and the bytes in regular files:
On your laptop:
ssh -o BatchMode=yes rosorin 'sudo -n find /mnt/app_new -xdev -type f -not -path "/mnt/app_new/lost+found/*" -printf "%s\n" | awk "{s+=\$1} END {printf \"%.0f\n\", s}"'
(The first command is the log's 17:30:03 command without its byte part, which overflowed; the second is the
18:36:31 command.)
Check
Both sides equal (log 17:30:03 and 18:36:31):files 188110 dirs 18751 7689137900
If it fails
bytes 2147483647. That isawkprinting with%dand running out of range, not a short copy. Use
printf "%.0f"as above.dudisagrees.du -sb --apparent-sizegave 6573389093 on bigbuddy and 6646333809 on the robot (log
18:36:07): about 73 MB apart. That is folder metadata, which is sized differently on bigbuddy's btrfs and the
robot's ext4, not file content. Compare regular-file bytes instead.- The count is not 0 before the copy. Something is already on p16; stop and find out what before unpacking
over it.
The staged rootfs has NVIDIA's template boot/extlinux/extlinux.conf, whose APPEND line is only
${cbootargs} (the arguments the bootloader passes in). When NVIDIA's flash tool installs a system, it adds the
root partition and NVIDIA's standard arguments. Here you add them yourself.
NVIDIA's standard arguments are CMDLINE_ADD in the BSP's board configuration. Read them on bigbuddy:
On bigbuddy:
docker run --rm -v /home/burgerbarn/jetson-flash/work:/work:z jetson-flash:r36.4.3 bash -c "du -sb --apparent-size /work/Linux_for_Tegra/rootfs | cut -f1; cd /work/Linux_for_Tegra; grep -h CMDLINE_ADD p3767.conf.common p3768-0000-p3767-0000-a0.conf jetson-orin-nano-devkit-super.conf 2>/dev/null"
Check
From the log (18:36:07); the onlyCMDLINE_ADDcomes fromp3767.conf.common:CMDLINE_ADD="mminit_loglevel=4 console=ttyTCU0,115200 firmware_class.path=/etc/firmware fbcon=map:0 nospectre_bhb video=efifb:off console=tty0"Compare the factory line from chapter 4: it is the same plus
net.ifnames=0, which is not in NVIDIA's files.
What each part of the new line does:
root=PARTUUID=<yours>: mount this partition as /.rw rootwait rootfstype=ext4: mount it writable, wait until the NVMe has appeared, it is ext4.console=ttyTCU0,115200 and console=tty0: kernel messages to the serial debug port and to the screen.firmware_class.path=/etc/firmware: where drivers look for firmware files.mminit_loglevel=4, fbcon=map:0, nospectre_bhb, video=efifb:off) are NVIDIA's defaults for thisnet.ifnames=0 was left out on purpose. Without it the wired network card gets a name from its PCI position,
enP8p1s0, instead of eth0. NetworkManager uses DHCP on it either way (docs/lessons.md).
The block keeps NVIDIA's template as extlinux.conf.nvidia (only once: test -e ... ||), then replaces the first
APPEND line of the LABEL primary entry with a short Python script, and shows the result and the difference.
Put your own PARTUUID into the NEW= line.
On the robot:
set -e
M=/mnt/app_new
grep -vE "^\s*(#|$)" $M/etc/network/interfaces 2>/dev/null | grep -v "^source" || echo "interfaces: nothing defined"
test -e $M/boot/extlinux/extlinux.conf.nvidia || sudo -n cp -p $M/boot/extlinux/extlinux.conf $M/boot/extlinux/extlinux.conf.nvidia
NEW=" APPEND \${cbootargs} root=PARTUUID=ef01d088-5975-4263-b620-2d0f5137dfe1 rw rootwait rootfstype=ext4 mminit_loglevel=4 console=ttyTCU0,115200 firmware_class.path=/etc/firmware fbcon=map:0 nospectre_bhb video=efifb:off console=tty0"
sudo -n python3 - "$NEW" <<'PY'
import sys
p="/mnt/app_new/boot/extlinux/extlinux.conf"
lines=open(p).read().split("\n")
out=[];in_primary=False;done=False
for l in lines:
s=l.strip()
if s.startswith("LABEL "): in_primary=(s=="LABEL primary")
if in_primary and s.startswith("APPEND") and not done:
out.append(sys.argv[1]); done=True; continue
out.append(l)
assert done
open(p,"w").write("\n".join(out))
PY
echo "== result"; grep -vE "^\s*#|^\s*$" $M/boot/extlinux/extlinux.conf
echo "== diff vs template"; diff $M/boot/extlinux/extlinux.conf.nvidia $M/boot/extlinux/extlinux.conf || true
ls -la $M/boot/Image $M/boot/initrd
sudo -n blkid -s PARTUUID -o value /dev/nvme0n1p16
The first grep confirms the new system has no old-style /etc/network/interfaces setup that could fight with
NetworkManager; it printed interfaces: nothing defined. \${cbootargs} is escaped so the shell writes the literal text ${cbootargs} into the file.
The last line prints p16's PARTUUID again so you can compare it with the line you just wrote.
Check
The file without comments (log 18:37:06):TIMEOUT 30 DEFAULT primary MENU TITLE L4T boot options LABEL primary MENU LABEL primary kernel LINUX /boot/Image INITRD /boot/initrd APPEND ${cbootargs} root=PARTUUID=ef01d088-5975-4263-b620-2d0f5137dfe1 rw rootwait rootfstype=ext4 mminit_loglevel=4 console=ttyTCU0,115200 firmware_class.path=/etc/firmware fbcon=map:0 nospectre_bhb video=efifb:off console=tty0The diff shows one changed line (line 10,
APPEND ${cbootargs}before), andblkidprints the same PARTUUID
as theroot=value.
Now give the new partition the name APP and the factory one APP_old. The block checks the current names first,
flushes and unmounts p16, and renames both in one sgdisk call. The partition GUIDs do not change.
Exactly one partition named APP
L4TLauncher boots the partition named exactlyAPP. Never leave two partitions with that name, and never none
(docs/restore.md"Rules"). If the new system does not come up, the plan recorded at the time was: recovery
pins (disassembly) or another boot medium, then rename back. It was not needed.
On the robot:
set -e
test "$(sudo -n sgdisk -i 1 /dev/nvme0n1 | grep "Partition name" | cut -d"'" -f2)" = "APP"
test "$(sudo -n sgdisk -i 16 /dev/nvme0n1 | grep "Partition name" | cut -d"'" -f2)" = "APP_new"
sync; sudo -n umount /mnt/app_new
sudo -n sgdisk -c 1:APP_old -c 16:APP /dev/nvme0n1
sudo -n partprobe /dev/nvme0n1 2>/dev/null || true
for i in 1 16; do echo "p$i: $(sudo -n sgdisk -i $i /dev/nvme0n1 | grep -E "Partition name|unique GUID" | tr -s " " | tr "\n" " ")"; done
lsblk -o NAME,PARTLABEL,PARTUUID /dev/nvme0n1 | grep -E "APP"
Check
From the log (18:44:39):p1: Partition unique GUID: 59FCA1C4-9E3E-406D-8532-CAD56C869912 Partition name: 'APP_old' p16: Partition unique GUID: EF01D088-5975-4263-B620-2D0F5137DFE1 Partition name: 'APP' ├─nvme0n1p1 APP_old 59fca1c4-9e3e-406d-8532-cad56c869912 └─nvme0n1p16 APP ef01d088-5975-4263-b620-2d0f5137dfe1
Reboot. The SSH session dies when the robot goes down; the two ServerAlive options make your laptop give up after
about 6 seconds instead of hanging. (macOS has no timeout command: command not found: timeout, log 16:49:19.)
On your laptop:
ssh -o BatchMode=yes -o ServerAliveInterval=3 -o ServerAliveCountMax=2 rosorin 'sync; sudo -n systemctl reboot'
To go back to the factory system later, swap the names the other way from the new system:
ssh -t rosorin 'sudo sgdisk -c 16:APP_new -c 1:APP /dev/nvme0n1 && sudo systemctl reboot' (docs/restore.md,
procedure A).
The new system has a new user, new host keys, and on this network got the address 192.168.1.22 by DHCP. Log in as
burgerbarn with your key. accept-new stores the new host key on first contact:
On your laptop:
ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 burgerbarn@192.168.1.22 'hostname; whoami; findmnt -no SOURCE,FSTYPE /; cat /proc/cmdline'
This is a shortened form of the log's 18:46:24 check; chapter 6 runs the full version.
Check
The matching lines of the 18:46:24 output:Warning: Permanently added '192.168.1.22' (ED25519) to the list of known hosts. rosorin burgerbarn /dev/nvme0n1p16 ext4 root=PARTUUID=ef01d088-5975-4263-b620-2d0f5137dfe1 rw rootwait rootfstype=ext4 mminit_loglevel=4 console=ttyTCU0,115200 firmware_class.path=/etc/firmware fbcon=map:0 nospectre_bhb video=efifb:off console=tty0 bl_prof_dataptr=2031616@0x471E...The bootloader adds its own arguments after yours (
bl_prof_dataptr=...). That is how it looked on the working
system.
One NVIDIA service fails on this first boot:
On your laptop:
ssh -o BatchMode=yes burgerbarn@192.168.1.22 'journalctl -b -u nv-l4t-bootloader-config.service --no-pager | tail -25'
If it fails: nv-l4t-bootloader-config.service
From the log (18:46:36):nv-l4t-bootloader-config.sh[1456]: Error: Cannot open /etc/nv_boot_control.conf for reading. nv-l4t-bootloader-config.sh[1456]: Cannot install package. Exiting... systemd[1]: nv-l4t-bootloader-config.service: Failed with result 'exit-code'.
/etc/nv_boot_control.confdescribes the board (theTNSPEClines you read in chapter 4). NVIDIA's flash tool
writes it during a normal flash; an in-place install has none (docs/lessons.md). The factory system has the
right file for this exact board, so copy it fromAPP_old.
The owner did this by hand at 18:52 (sudo still asks for your password at this point, so use an interactive
session). The four commands, from the robot's sudo log:
On the robot:
sudo mount -o ro /dev/nvme0n1p1 /mnt
sudo cp /mnt/etc/nv_boot_control.conf /etc/
sudo umount /mnt
sudo systemctl restart nv-l4t-bootloader-config.service
-o ro mounts the factory partition read-only, so nothing on it can change.
On your laptop:
ssh -o BatchMode=yes burgerbarn@192.168.1.22 'systemctl show nv-l4t-bootloader-config.service -p Type -p Result -p ExecMainStatus -p RemainAfterExit; journalctl -b -u nv-l4t-bootloader-config.service --no-pager | tail -14; systemctl --failed --no-legend | wc -l'
Check
From the log (18:52:10):Type=oneshot RemainAfterExit=no Result=success ExecMainStatus=0 ... nv-l4t-bootloader-config.sh[2829]: COMPATIBLE_SPEC 3767-000-0000--1--jetson-orin-nano-devkit-super- ... nv-l4t-bootloader-config.sh[2829]: TEGRA_BOOT_STORAGE nvme0n1 ... nv-l4t-bootloader-config.sh[2829]: TEGRA_CHIPID 0x23and 0 failed units.
Why this does not touch the bootloader
The service can update the QSPI bootloader. Its automatic update branch only acts when the board is an Orin
Nano devkit configuration and the module SKU is0005; this module is SKU0000(TNSPEC3767-303-0000-...).
Read on the robot in/opt/nvidia/l4t-bootloader-config/nv-l4t-bootloader-config.sh, function
auto_update_qspi(log 18:47:34). Chapter 9 holds thenvidia-l4t-*packages for a related reason.
Path A is done. Continue with chapter 6.
Not test-built
Nobody has run this on this robot. On 2026-09-26 the BSP and container were prepared for it and the command was
chosen from NVIDIA's README, then the plan switched to path A. Unknown: whether the robot enters recovery mode
with the pins below; how to run NVIDIA's flash tool from the container on Fedora (USB access, the USB network
link, NFS, IPv6, SELinux, firewall); whether the result boots. Treat every step here as a plan.
Use path B only if the NVMe is blank or replaced. It overwrites the whole NVMe, and (without --external-only) the
module's QSPI bootloader too.
Safety
The robot must be opened to reach the recovery pins. Switch it off and unplug the charger before you open it.
Never put the 12.6 V charger into the Jetson's DC jack.
Linux_for_Tegra/rootfs that path A copies, so the user,docs/hardware.md). This link wasID 0955:7020 NVIDIA Corp. L4T (Linux for Tegra) running on Tegra.From docs/hardware.md (NVIDIA carrier specification SP-11324-001, Table 3-4): J14 is the 1x12 right-angle button
header on the edge opposite the I/O ports. Pin 9 is GND, pin 10 is FORCE_RECOVERY*. Short pins 9 and 10 while
power is applied. Which end is pin 1 is not verified (the diagram arrow points at the right end with the ports edge
toward you; its meaning is unconfirmed). NVIDIA's R36.4.3 Quick Start describes the same thing for its devkit:
power off, jumper the REC and GND pins of the 12-pin button header, power on.
Then on bigbuddy:
On bigbuddy:
lsusb | grep -i 0955 || echo "no NVIDIA USB device"
Check (from NVIDIA's documentation, never seen here)
NVIDIA's R36.4.3 Quick Start lists0955:7323for the Orin NX 16 GB (P3767-0000) in recovery mode.
0955:7020means the robot booted normally instead.
If it fails: the software way
sudo reboot --force forced-recoveryon the running robot failed both times it was tried (log 16:30:39 and
16:50:49). The first time the robot simply rebooted. The second time it printed
Rebooting with argument 'forced-recovery'., then its USB link stopped answering on bigbuddy
(usb 1-1: device descriptor read/64, error -110,usb 1-1: device not accepting address 7, error -71) and the
robot was off the network until it was power-cycled. Do not rely on it.
NVIDIA's tools/kernel_flash/README_initrd_flash.txt, Workflow 4, covers modules like the Orin NX that boot from an
internal QSPI and an external NVMe. Its command for the first NVMe, with <board> replaced by the configuration the
factory used (jetson-orin-nano-devkit-super, from nv_boot_control.conf in chapter 4), run as root from the
Linux_for_Tegra folder:
On bigbuddy:
sudo ./tools/kernel_flash/l4t_initrd_flash.sh --external-device nvme0n1p1 \
-c tools/kernel_flash/flash_l4t_external.xml \
-p "-c bootloader/generic/cfg/flash_t234_qspi.xml --no-systemimg" --network usb0 \
jetson-orin-nano-devkit-super external
What the options mean, per the same README:
--external-device nvme0n1p1: the target drive as the Jetson sees it.-c tools/kernel_flash/flash_l4t_external.xml: the partition layout for that drive (the layout the factory disk-p "...": options for the internal QSPI part of the flash, with NVIDIA's QSPI layout.--network usb0: transfer over an Ethernet link carried on the USB cable instead of plain USB.external as the root device: the tool writes root=PARTUUID=... into the boot entry.--external-only the tool flashes both the internal QSPI and the external drive.The docs differ
NVIDIA's R36.4.3 Quick Start gives a different NVMe command for its devkit (layoutflash_l4t_t234_nvme.xml,
boardjetson-orin-nano-devkit, root deviceinternal, plus--showlogs). The record chose the README's
Workflow 4 above. Neither was run on this robot.
Unknown: the container invocation
The README's prerequisites script is for Debian-based hosts, and bigbuddy is Fedora; that is why the
jetson-flash:r36.4.3container exists. But nodocker runline for flashing was ever written or tested.
Known facts that such a line has to deal with:
- The README requires SSH to the target and NFS requests from the target to the host, over IPv6.
- NVIDIA's host prerequisites (
tools/l4t_flash_prerequisites.sh) are not in the saved image: chapter 4 ran
them in a--rmcontainer. They would have to run again in the flashing container.- The container would need the USB device, the host's network (for
usb0) and an NFS server, on a Fedora host
with SELinux enforcing and its own firewall.- The BSP ships host udev rules in
tools/kernel_flash/host_udev/(10-l4t-usb-msd.rules,
99-l4t-host.rules). One of them keeps NetworkManager off the flashing network device0955:7035. Whether
they are needed on bigbuddy is not known.
/etc/nv_boot_control.conf is written by the flash tool (docs/lessons.md), so path A's copy step is notnv-l4t-bootloader-config.service reports Result=success.APP partition of NVIDIA's layout (id="1"), not p16, with a new PARTUUID. TheEF01D088-...; they do not apply unchanged.APP_old. Chapter 15 copies the camera driver from the factory partition; with a blank drive you needetc/nv/nvautoconfig is in the staged rootfs, so the first boot still runs NVIDIA's resize and swap setup.Then continue with chapter 6.
Another way onto a blank drive
Chapter 8 describes restoring a golden backup onto an NVMe attached to bigbuddy in a USB enclosure
(docs/restore.md, procedure C). It leaves the QSPI alone. It has also never been tested on bare metal.
lsblk -o NAME,PARTLABEL,PARTUUID /dev/nvme0n1 shows exactly one APP (p16) and the factory APP_old (p1).ssh burgerbarn@<robot address> logs you in with your key, and hostname prints rosorin.findmnt -no SOURCE,FSTYPE / prints /dev/nvme0n1p16 ext4, and /proc/cmdline contains your PARTUUID.systemctl show nv-l4t-bootloader-config.service -p Result prints Result=success.Where this comes from
Command log 2026-09-26, robot (factory OS, userubuntu): 17:13:48 (failed mkpart), 17:14:03 (sgdisk -p),
17:14:21 (sgdisk -e, mkpart), 17:14:37 / 17:14:44 (mkfs, blkid), 18:37:06 (extlinux.conf), 18:44:39 (GPT
rename), 18:45:04 (reboot), 16:30:39 / 16:50:49 / 16:52:49 (failed forced recovery), 18:47:34
(auto_update_qspi); new system: 18:46:24, 18:46:36, 18:52:10; laptop: 17:16:07 (copy), 17:30:03 and 18:36:31
(counts, bytes), 18:36:07 (CMDLINE_ADD,du), 16:49:19 (notimeout); bigbuddy: 16:19:13 and 17:00:22
(README workflows), 16:48:24 (host udev rules). Robot/var/log/auth.logsudo lines 2026-09-26 18:52:02
(nv_boot_control.confcopy and service restart). Read-only lookups 2026-10-07: robotlsblkpartition names,
BSPflash_l4t_external.xmland README_initrd_flash.txt lines 84-120 and 170-190. Repo:docs/decisions.md
2026-09-26,docs/hardware.md"Boot" and "Recovery mode",docs/lessons.md"Rebuild (2026-09-26)",
docs/restore.md"Rules" and procedure A,docs/status.md"Done (verified)". NVIDIA Jetson Linux R36.4.3
Quick Start (docs.nvidia.com), recovery-mode USB IDs and devkit flash command.
← Prepare the build host · Contents · First boot and base settings →