We attempted OTA with only disk encryption enabled. The OTA process took very little time, but the thor system became abnormal after reboot.
This file has sections on encrypted rootfs and OTA . Linux_for_Tegra/tools/ota_tools/version_upgrade/Image_based_OTA_Examples.txt
And you did the encrypt parts of :
sudo ./l4t_generate_ota_package.sh --help
Usage: sudo ./l4t_generate_ota_package.sh [options] <target board> <bsp version>
Where,
<target board>: target board. Supported boards: jetson-agx-orin-devkit, jetson-agx-orin-devkit-industrial, jetson-orin-nano-devkit, jetson-orin-nano-devkit-super, jetson-orin-nano-devkit-super-maxn, jetson-agx-thor-devkit.
<bsp version>: the version of the base BSP. Supported versions: R38-2, R38-3, R38-4.
options:
-u <PKC key file>: PKC key used for odm fused board
-v <SBK key file>: Secure Boot Key (SBK) key used for ODM fused board
-i <enc rfs key file>: Key for disk encryption support
-s Skip generating system image.
-b Update bootloader only. Only valid if <bsp version> is R38.
-r Update rootfs only. Only valid if <bsp version> is R38.
-o Specify the script to update rootfs partition.
-f Specify the rootfs image to be written to rootfs partition.
-p Specify the options directly passed into flash.sh when generating images.
--external-device <external device>: Specify the external device to be OTAed. Supported devices: nvme0n1.
This option is only valid for jetson-orin-nano-devkit, jetson-orin-nano-devkit-super, jetson-orin-nano-devkit-super-maxn, and jetson-agx-thor-devkit.
-S <size>: Specify the size of rootfs partition on external device. Only valid when --external-device option is set. KiB, MiB, GiB short hands are allowed
Ths size set through this option must be the same as the size of rootfs partition on the external device to be OTAed.
-E <esp image>: Specify the image to update ESP.
-T <ext num sectors>: Specify the number of the sectors of the external storage device.
--rootfs-uuid <rootfs UUID>: Specify UUID for rootfs tp be updated through OTA
--rootfs-b-uuid <rootfs B UUID>: Specify UUID for rootfs B on the device to be updated through OTA.
--uda-uuid <uda UUID>: Specify UUID for UDA on the device to be updated through OTA.
--uefi-keys <keys_conf>: Specify UEFI keys configuration file.
--uefi-enc <UEFI_ENC_key>: Key file (0x19: 16-byte; 0x23: 32-byte) to encrypt UEFI payloads.
Example:
1. Upgrade from R38.2 to R38 ToT on Jetson AGX Thor Devkit
sudo ./l4t_generate_ota_package.sh jetson-agx-thor-devkit R38-2
2. Upgrade from R38.2 to R38 ToT on Jetson AGX Thor Devkit that has 24GiB APP partition
sudo ./l4t_generate_ota_package.sh --external-device nvme0n1 -S 24GiB jetson-agx-thor-devkit R38-2
3. Upgrade from R38.2 to R38 ToT on Jetson AGX Thor Devkit that has 24GiB APP partition and with rootfs A/B enabled
sudo ROOTFS_AB=1 ./l4t_generate_ota_package.sh --external-device nvme0n1 -S 24GiB jetson-agx-thor-devkit R38-2
hello somnus2021,
you’ll need to create OTA payload with ROOTFS_ENC=1, please also have an updated EKS image applied, you should also include your disk encrypt key for generating OTA payload.
hello JerryChang,
First, create an encrypted rootfs and flash it to the devkit by following these steps:
cd optee/samples/hwkey-agent/host/tool/gen_ekb/
./example.sh
copy eks_t264.img to Linux_for_Tegra/bootloader
echo <disk_enc_key> > disk_enc.key //disk_enc_key is the same as sym2_t264.key in example.sh
sudo ROOTFS_ENC=1 ./tools/kernel_flash/l4t_initrd_flash.sh --external-device nvme0n1p1 \
-c ./tools/kernel_flash/flash_l4t_t264_nvme_rootfs_enc.xml \
-i disk_enc.key" --external-only \
jetson-agx-thor-devkit external
Then reboot; the lsblk output is as follows:
lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 16M 1 loop
nvme0n1 259:0 0 953.9G 0 disk
├─nvme0n1p1 259:1 0 400M 0 part /boot
├─nvme0n1p2 259:2 0 54.6G 0 part
│ └─crypt_root 253:0 0 54.6G 0 crypt /
├─nvme0n1p3 259:3 0 100M 0 part
├─nvme0n1p4 259:4 0 768K 0 part
├─nvme0n1p5 259:5 0 512M 0 part /boot/efi
├─nvme0n1p6 259:6 0 384.8M 0 part
├─nvme0n1p7 259:7 0 100M 0 part
├─nvme0n1p8 259:8 0 768K 0 part
├─nvme0n1p9 259:9 0 512M 0 part
├─nvme0n1p10 259:10 0 384.8M 0 part
├─nvme0n1p11 259:11 0 400M 0 part
│ └─crypt_UDA 253:1 0 384M 0 crypt /mnt/crypt_UDA
└─nvme0n1p12 259:12 0 335M 0 part
Generate the ota payload and ota
sudo -E ROOTFS_ENC=1 ./tools/ota_tools/version_upgrade/l4t_generate_ota_package.sh \
--external-device nvme0n1 -i disk_enc.key jetson-agx-thor-devkit R38-4
Finally reboot, the error log is as follows:
o 8.12-tegra/updates/drivers/usb/typec/fusb301.[ 5.894982] usbcore: registered new interface driver uas
[ 5.916247] Checking whether device /dev/sd?1 existexistxternal storage devices
[ 5.923228] Device /dev/sd?1 does not exist
[ 5.933285] Looking for OTA work directory on the device(s): /dev/nvme0n1p1
[ 5.937591] mount /dev/nvme0n1p1 /mnt
[ 5.946960] EXT4-fs (nvme0n1p1): recovery complete
[ 5.947125] EXT4-fs (nvme0n1p1): mounted filesystem 9d8554ab-317c-4385-af17-940443c0e321 r/w with ordered data mode. Quota mode: none.
[ 5.958387] is_boot_part_for_disk_enc /dev/nvme0n1p1 /mnt
[ 5.973173] The mounted /dev/nvme0n1p1 is boot partition, try locating rootfs partition and mount it...
/6.8.12-tegra/kernel/drivers/usb/gadget/udc/tegra-xudc.ko
insmod /lib/modules/6.8.12-tegra/kernel/drivers/usb/storage/uas.ko
insmod /lib/modules/6.8.12-tegra/kernel/drivers/usb/gadget/libcomposite.ko
[ 6.005868] umount /mntpted rootfs partition /dev/nvme0n1p2 through UUID(b48e5bbb-2a80-468f-a436-4d22773159d3)
[ 6.018534] unlock_encrypted_partition /dev/nvme0n1p2 dm_crypt_ota dm_cryptf17-940443c0e321.
/bin[ 6.028571] The encrypted partition /dev/nvme0n1p2 is not LUKS format
[ 6.034865] The encrypted partition /dev/nvme0n1p2 is not LUKS format
[ 6.041151] Failed to run "unlock_encrypted_partition /dev/nvme0n1p2 dm_crypt_ota dm_crypt"
[ 6.049567] Failed to run "moutn_rootfs_partition /dev/nvme0n1p1 /mnt"
[ 6.056026] Failed to run "mount_ota_work_partition /dev/nvme0n1p1 /mnt"
[ 6.062731] Internal storage device(/dev/mmcblk0p1) does not exist
[ 6.069085] OTA work directory is not found on internal and external storage devices
/nv_ota_disk_enc.func: line 46: /lib/cryptsetup/ld-linux-aarch64.so.1: No such file or directory
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.2# [ 16.001247] tegra-mc 8108020000.memory-controller: sync_state() pending due to 8181200000.host1x
[ 16.001282] tegra-mc 8108020000.memory-controller: sync_state() pending due to bus@0:aconnect@9000000
[ 16.005222] tegra186-emc 8108020000.memory-controller:external-memory-controller@8108800000: sync_state() pending due to 8181200000.host1x
[ 16.017782] tegra186-emc 8108020000.memory-controller:external-memory-controller@8108800000: sync_state() pending due to bus@0:aconnect@9000000
[ 16.030700] tegra186-emc 8108020000.memory-controller:external-memory-controller@8108800000: sync_state() pending due to 88090b0000.hda
[ 16.051351] tegra186-emc 8108020000.memory-controller:external-memory-controller@8108800000: sync_state() pending due to 81893d0000.rtcpu
[ 16.063877] tegra-mc 8108020000.memory-controller: sync_state() pending due to 81893d0000.rtcpu
[ 16.072611] tegra186-emc 8108020000.memory-controller:external-memory-controller@8108800000: sync_state() pending due to 8808c00000.display
[ 16.085181] tegra-mc 8108020000.memory-controller: sync_state() pending due to 8808c00000.display
[ 16.093928] tegra186-emc 8108020000.memory-controller:external-memory-controller@8108800000: sync_state() pending due to 0000:01:00.0
[ 16.106140] tegra-mc 8108020000.memory-controller: sync_state() pending due to 0000:01:00.0
bash-5.2#
bash-5.2#
Where did this process go wrong?
hello somnus2021,
may I double check which Jetpack release version you’re working with?
did you have OTA update from r38.4 to r38.4?
Hello JerryChang,
I’m using JetPack 7.1, and performed an OTA update from R38.4 to R38.4.
hello somnus2021,
it looks something wrong with your flash commands, the last column should be internal for Thor’s root device.
please refer to developer guide, Disk Encryption.
here’s the sample command to enable disk encryption,
$ sudo ROOTFS_ENC=1 ./l4t_initrd_flash.sh -i "./disk_enc.key" jetson-agx-thor-devkit internal
please give it a try to re-flash your target, and then generate the OTA payload for confirmation.
hello JerryChang,
I tried again following your suggestion, but got the same error. Would you mind confirming that once more?
hello somnus2021,
may I have your step in details for cross check.
BTW, please see-also Topic 358606, although it’s the topic we’ve verify OTA update with ROOTFS_AB=1 from r38.4 to r38.4 on Jetson AGX Thor developer kit.
Hello, JerryChang,
Here are the detailed steps:
cd optee/samples/hwkey-agent/host/tool/gen_ekb/
./example.sh
copy eks_t264.img to Linux_for_Tegra/bootloader
echo <disk_enc_key> > disk_enc.key //disk_enc_key is the same as sym2_t264.key in example.sh
sudo ROOTFS_ENC=1 ./tools/kernel_flash/l4t_initrd_flash.sh \
-i disk_enc.key jetson-agx-thor-devkit internal
The example.sh script is shown below:
#!/bin/bash
# SPDX-License-Identifier: BSD-2-Clause
# SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# [T264 example]
# Fill in your OEM_KDK1 fuse key value
echo "0000000000000000000000000000000000000000000000000000000000000000" > oem_kdk1.key
# Generate user-defined symmetric key files
# A random generate key is recommended for production, and a specified key is recommended for testing
# For each key, there are reference examples for generating random key and specifying keys.
# openssl rand -rand /dev/urandom -hex 32 > sym_t264.key # kernel/kernel-dtb encryption key
echo "0000000000000000000000000000000000000000000000000000000000000000" > sym_t264.key
# openssl rand -rand /dev/urandom -hex 16 > sym2_t264.key # disk encryption key
echo "00000000000000000000000000000000" > sym2_t264.key
# openssl rand -rand /dev/urandom -hex 16 > auth_t264.key # uefi variables authentication key
echo "00000000000000000000000000000000" > auth_t264.key
python3 gen_ekb.py \
-chip t264 \
-oem_kdk1_key oem_kdk1.key \
-in_sym_key sym_t264.key \
-in_sym_key2 sym2_t264.key \
-in_auth_key auth_t264.key \
-out eks_t264.img
Generate the ota payload and ota
sudo -E ROOTFS_ENC=1 ./tools/ota_tools/version_upgrade/l4t_generate_ota_package.sh \
--external-device nvme0n1 -i disk_enc.key jetson-agx-thor-devkit R38-4
After OTA and reboot, the above error happened.
Can you reproduce this issue?
Maybe I’m overcomplicating this but tools/ota_tools/version_upgrade/Image_based_OTA_Examples.txt is really worthwhile.
Search it for luks if your error keeps complaining about luks.
You have not included your steps to scp ota_tools__aarch64.tbz2 nvidia@192.168.55.1:~/ to your Thor{s} tar it and launch installation of it.
If you will be doing a number of Thor OTA upgrade you might wish to examine and modify these 2 files for Thor and your use case.
demo_host_ota_uefi_sb.sh
demo_target_ota_uefi_sb.sh
Case 20: Jetson Thor Devkit, UEFI payloads signed, disk encryption enabled,
and base version of R38.4.0:
$ sudo ROOTFS_ENC=1 -E ./tools/ota_tools/version_upgrade/l4t_generate_ota_package.sh\
--external-device nvme0n1 \
--uefi-keys ./uefi_keys/uefi_keys.conf \
--rootfs-uuid 11111111-1111-1111-111111111111 \
--uda-uuid 33333333-3333-3333-333333333333 \
jetson-agx-thor-devkit R38-4
OR
Case 24: an OTA update from R38.2.0 to the latest R38 version on the jetson-agx-thor-devkit with
disk encryption enabled.
$ sudo -E ROOTFS_ENC=1 ./tools/ota_tools/version_upgrade/l4t_generate_ota_package.sh \
--external-device nvme0n1 -i ekb.key jetson-agx-orin-devkit R38-2
The "ekb.key" is the key used for disk encryption. It must be the same as the one used to
flash R38.2.0 to the device.
THEN
rootfs_a_uuid="$(lsblk -P -n -o PARTLABEL,UUID /dev/nvme0n1 \
| grep APP | cut -d\ -f 2 | cut -d= -f 2 | sed 's/^\"\(.*\)\"$/\1/')"
$ echo "rootfs_a_uuid:${rootfs_a_uuid}" >"uuids.txt"
$ scp uuids.txt userid@x86_64_host:~/
c. The host machine gets UUIDs from the received "uuids.txt" and executes the script
"l4t_ota_sign_uefi_base.sh" to generate "uefi_secureboot_overlay_multi_specs.tar.gz"
with obtained UUIDs as input parameters. Afterward, the host machine sends the
"ota_tools_<version>_aarch64.tbz2", "ota_payload_package.tar.gz", and
"uefi_secureboot_overlay_multi_specs.tar.gz" archives to the target device.
$ rootfs_a_uuid="$(grep "rootfs_a_uuid" ~/uuids.txt | cut -d: -f 2 || true)"
$ sudo ./tools/ota_tools/version_upgrade/l4t_ota_sign_enc_uefi_base.sh \
--uefi-keys uefi_keys/uefi_keys.conf \
--uefi-enc ./uefi_enc.key \
--rootfs-uuid "${rootfs_a_uuid}"
$ scp ../ota_tools_<version>_aarch64.tbz2 nvidia@192.168.55.1:~/
$ scp ./bootloader/jetson-agx-orin-devkit/ota_payload_package.tar.gz \
nvidia@192.168.55.1:~/
$ scp ./bootloader/uefi_overlay/uefi_secureboot_overlay_multi_specs.tar.gz \
nvidia@192.168.55.1:~/
d. The target device receives all OTA packages, extracts "ota_tools_<version>_aarch64.tbz2",
and triggers the OTA update.
$ tar xjpf ota_tools_<version>_aarch64.tbz2
$ cd Linux_for_Tegra/tools/ota_tools/version_upgrade
$ sudo ./nv_ota_start.sh ~/ota_payload_package.tar.gz
Yes, I omitted this step; otherwise, I wouldn’t have the tool to perform the OTA upgrade either.
Thank you very much.
Your log shows it not finding uncompressed ota_payload_package.tar.gz is why I asked.
I overlooked this part of the log.
Command: ./nv_ota_start.sh /home/test/ota_payload_package.tar.gz
/usr/bin/efibootmgr
/usr/bin/efibootdump
/usr/sbin/nvme
Current rootfs is on /dev/nvme0n1
init_ota_log /ota_log
Creating log dir at /ota_log
Create log file at /ota_log/ota_20260416-060818.log
OTA_LOG_FILE=/ota_log/ota_20260416-060818.log
Extract /home/test/ota_payload_package.tar.gz
update_nv_boot_control_in_rootfs /ota_work
3834-000-0008--1--jetson-agx-thor-devkit-
TNSPEC 3834-400-0008-G.5-1-1-jetson-agx-thor-devkit-
COMPATIBLE_SPEC 3834-000-0008--1--jetson-agx-thor-devkit-
TEGRA_BOOT_STORAGE nvme0n1
TEGRA_CHIPID 0x26
TEGRA_OTA_BOOT_DEVICE /dev/mtdblock0
TEGRA_OTA_GPT_DEVICE /dev/mtdblock0
Info: Write TegraPlatformCompatSpec with 3834-000-0008--1--jetson-agx-thor-devkit-.
Info: The esp is already mounted to /boot/efi.
check_prerequisites
decompress_ota_package ota_package.tar /ota_work
decompress_ota_package: start at Thu Apr 16 06:08:44 AM UTC 2026
Sha1 checksum for /ota_work/ota_package.tar (8b476ee80f024000b3765c41df02b90c42e03cc1) matches
decompress_ota_package: end at Thu Apr 16 06:08:49 AM UTC 2026
nv_ota_update_implement.sh
Command: nv_ota_update_implement.sh
check_target_board /ota_work TARGET_BOARD
get_chip_id CHIP_ID
ota_choose_images /ota_work
COMPATIBLE_SPEC=3834-000-0008--1--jetson-agx-thor-devkit-
TEGRA_CHIPID=0x26
_BOARD_SPEC_NAME=3834-000-0008-
Copy files from ./images-R38-ToT/3834-000-0008-/ to ./images-R38-ToT/
is_rootfs_a_b_enabled ROOTFS_AB_ENABLED ROOTFS_CURRENT_SLOT
ROOTFS_AB_ENABLED=0
ROOTFS_CURRENT_SLOT=0
is_rootfs_encryption_enabled ROOTFS_ENC_ENABLED
ROOTFS_ENC_ENABLED=1
get_update_slot UPDATE_SLOT
UPDATE_SLOT=B
get_update_control /ota_work UPDATE_BOOTLOADER UPDATE_ROOTFS
UPDATE_BOOTLOADER=1, UPDATE_ROOTFS=1
check_bsp_version /ota_work BASE_VERSION
User release version in system: 0.0
User release version in OTA package: 0.0
User version is to be upgrade from 0.0 to 0.0
base_version=R38 ota_version=R38
update_misc_partitions /ota_work /ota_work/external_device/images-R38-ToT
Updating misc partitions without layout change
install_partition_with_alt /ota_work/external_device/images-R38-ToT recovery
prerequisite_check recovery
No image is specified for partition recovery
Skip updating recovery partition as no valid image is found
install_partition_with_alt /ota_work/external_device/images-R38-ToT recovery-dtb
prerequisite_check recovery-dtb
No image is specified for partition recovery-dtb
Skip updating recovery-dtb partition as no valid image is found
install_partition_with_alt /ota_work/external_device/images-R38-ToT esp
prerequisite_check esp
The /ota_work/external_device/images-R38-ToT/esp.img for partition esp is not found
Skip updating esp partition as no valid image is found
clean_up_boot_partition
update_rootfs /ota_work
update_rootfs_with_a_b_disabled /ota_work
update_rootfs_in_recovery /ota_work
force_booting_to_recovery
Force booting to recovery by writing \x07\x00\x00\x00\x03\x00\x00\x00 to UEFI variable L4TDefaultBootMode-781e084c-a330-417c-b678-38e696380cb9
dd if=/tmp/var_tmp.bin of=L4TDefaultBootMode-781e084c-a330-417c-b678-38e696380cb9 bs=8
1+0 records in
1+0 records out
8 bytes copied, 0.00246269 s, 3.2 kB/s
Rootfs is to be updated in recovery kernel once device is rebooted.
check_bootloader_version /ota_work
update_bootloader /ota_work
Bootloader on non-current slot(B) is to be updated once device is rebooted
clean_up_ota_files
Apologies for the side track. Is your / root actually 56GB?
nvme0n1 259:0 0 953.9G 0 disk
├─nvme0n1p2 259:2 0 54.6G 0 part
│ └─crypt_root 253:0 0 54.6G 0 crypt /
Prior to OTA 38.4 was your existing nvme 38.4 encrypted?
Yes, the NVMe was already encrypted prior to the OTA 38.4 update.
hello somnus2021,
are you working with Jetson AGX Thor developer kit?
we’ve tested locally to confirm OTA update with ROOTFS_ENC=1 from r38.4 to r38.4 on AGX-Thor.
Do you have the original keys with which you encrypted your Thor?
If not; do you have the eks_t264.img that flash.sh or l4t_initrd_flash.sh created at the time you initially flashed your Thor? It appears you could reuse it for OTA since it’s 38.4 to 38.4.
This line of thought is based on following
https://docs.nvidia.com/jetson/archives/r38.4/DeveloperGuide/SD/SoftwarePackagesAndTheUpdateMechanism.html#over-the-air-update
OTA Upgrades with Disk Encryption Enabled
Note:
Before generating a OTA payload package, ensure that ${TARGET_BSP}/bootloader/eks_<CHIP_ID>.img contains the same keys
as the existing ones in the EKS partition on the device to be updated.
Hello, JerryChang,
Could you provide the complete steps? I’ll try following them.
hello somnus2021,
FYI, here’s steps to generate OTA payload.
$ export TARGET_BSP=$OUT/JetPack/JetPack-7.1/Linux_for_Tegra
$ sudo -E ROOTFS_ENC=1 ./tools/ota_tools/version_upgrade/l4t_generate_ota_package.sh --external-device nvme0n1 -i ./sym2_t264.key jetson-agx-thor-devkit R38-4
$ scp bootloader/jetson-agx-thor-devkit/ota_payload_package.tar.gz <your target>