Preparing a Jetson to flash and boot an encrypted and non-encrypted image for an SSD

From RidgeRun Developer Wiki






Problems running the pipelines shown on this page? Please see our GStreamer Debugging guide for help.



Why is this useful?

There are moments when we need to flash an external partitions from the Jetson platform. Some scripts might need to be modified for that, there are flashing scripts tgat are useful for these cases. This wiki shows how to create an encrypted (or non encrypted) minimal JetPack environment for an external partition (SSD) that will be prepared and built from a local PC, and then flashed and decrypted from a Jetson platform. This document will guide you on:

  • How to create a minimal filesytem.
  • How to encrypt a Jetson Image.
  • How to flash the encrypted/non encrypted Image from a Jetson platform to an external partition (an SSD).
  • How to encrypt, decrypt, and boot the external partition.
  • How to format and arrange the memory blocks of the external partition.

Getting started

Get the JetPack sources

This section details how to generate the JetPack environment needed for flashing the SSD. The goal is to create a compressed directory with the JetPack environment that will be used on the flash process from the Jetson. This final JetPack environment weighs around ~4.2GB (using the minimal image packages that NVIDIA provides).

1.1 Get the sources:

cd /tmp
mkdir linux-tegra && cd "$_"
wget -N https://developer.nvidia.com/embedded/l4t/r35_release_v1.0/release/jetson_linux_r35.1.0_aarch64.tbz2
wget -N https://developer.nvidia.com/embedded/L4T/r35_Release_v1.0/Release/secureboot_R35.1.0_aarch64.tbz2

1.2 Build the environment:

sudo tar xpvf jetson_linux_r35.1.0_aarch64.tbz2
sudo tar xvjf secureboot_R35.1.0_aarch64.tbz2

Create a minimal filesystem

This method creates a minimal filesystem. It does not include a desktop GUI.

On the host computer:

1. Add the desired packages to the minimal image package list file (this file already comes with a set of packages for a minimal image). This file is located in:

<JetPack_root_directory>/Linux_for_Tegra/tools/samplefs/nvubuntu-focal-minimal-aarch64-packages

- The <JetPack_root_directory> might be in this case the linux-tegra directory already created.

  • It is recommended to at least add the network-manager package to be able to ssh into the unit later:
xterm
xtrans-dev
xxd
zlib1g
zlib1g-dev
+network-manager

2. Initialize a docker container to create the environment for filesystem creation:

sudo apt-get install docker.io
sudo docker run --privileged -it --rm -v <JetPack_root_directory>/Linux_for_Tegra:/l4t ubuntu:20.04

3. Once in the docker container, generate the filesystem:

apt-get update
apt install -y qemu-user-static wget sudo
cd /l4t/tools/samplefs
sudo ./nv_build_samplefs.sh --abi aarch64 --distro ubuntu --flavor minimal --version focal

4. Copy the generated filesystem tarball to the JetPack environment. In another terminal outside the container do:

docker cp <container id>:/l4t/tools/samplefs/sample_fs.tbz2 <JetPack_root_directory>/Linux_for_Tegra/tools/samplefs

5. Untar the filesystem tarball into the rootfs directory:

cd <JetPack_root_directory>/Linux_for_Tegra
sudo rm -rf rootfs
mkdir rootfs && cd "$_"
sudo tar xpvf ../tools/samplefs/sample_fs.tbz2
cd ..

5.5 [Optional] If you wish to generate a full filesystem instead of a minimal one, follow the next commands:

cd <JetPack_root_directory>
wget -N https://developer.nvidia.com/embedded/l4t/r35_release_v1.0/release/tegra_linux_sample-root-filesystem_r35.1.0_aarch64.tbz2
sudo rm -rf Linux_for_Tegra/rootfs
mkdir Linux_for_Tegra/rootfs && cd "$_"
sudo tar xpvf ../../tegra_linux_sample-root-filesystem_r35.1.0_aarch64.tbz2
cd ..

6. Apply the NVIDIA binaries:

sudo apt-get install qemu-user-static
sudo ./apply_binaries.sh --t

7. Install dependencies:

sudo apt-get install abootimg
sudo apt-get install libxml2-utils
sudo apt install uuid-runtime

8. Create an user:

  • If a user is not created at this stage, then a mouse, keyboard and monitor are going to be needed to complete the Ubuntu setup when booting for the first time.
  • Specifying a machine name is optional, if not specified, the machine name will be tegra-ubuntu.
sudo ./tools/l4t_create_default_user.sh -u <username> -p <password> -n <machine name>

Choose 'Accept' and press Enter.

Congratulations. The minimal filesystem has been created. Now the next step is to create the images and flash them to the Jetson platform.

Flash and create a Non Encrypted Image for the SSD

  • The following instructions assume that an SSD NVMe card is already installed on the AGX module.
  • The following instructions assume that the Jetson platform is already flashed. You can use any JetPack version, these instructions were tested on JetPack 5.0.2.
  • The following instructions do not cover image and kernel compilation, but any image can be compiled and used normally.

Create the JetPack environment for the SSD

This is the JetPack environment that is going to live inside the SSD NVMe. The main idea is to prepare images from the minimal filesystem that was built earlier, compress it, and then sending it to the Jetson platform.

2.1.1. Create the images:

sudo BOARDID=2888 BOARDSKU=0004 FAB=400 BOARDREV=K.0 ./tools/kernel_flash/l4t_initrd_flash.sh --no-flash -S 8GiB -c tools/kernel_flash/flash_l4t_external.xml --external-only --external-device nvme0n1p1 --direct nvme0n1 jetson-agx-xavier-devkit external
Environment variables value table:
#
#                                     BOARDID  BOARDSKU  FAB  BOARDREV
#    --------------------------------+--------+---------+----+---------
#    jetson-agx-xavier-industrial     2888     0008      600  A.0
#    clara-agx-xavier-devkit"         3900     0000      001  C.0
#    jetson-xavier-nx-devkit          3668     0000      100  N/A
#    jetson-xavier-nx-devkit-emmc     3668     0001      100  N/A
#    jetson-xavier-nx-devkit-emmc     3668     0003      N/A  N/A
#    jetson-agx-xavier-devkit (16GB)  2888     0001      400  H.0
#    jetson-agx-xavier-devkit (32GB)  2888     0004      400  K.0
#    jetson-agx-orin-devkit           3701     0001      TS1  C.2
#    jetson-agx-orin-devkit           3701     0000      TS4  A.0
#    jetson-agx-xavier-devkit (64GB)  2888     0005      402  B.0
#    holoscan-devkit                  3701     0002      TS1  A.0
#    jetson-agx-orin-devkit           3701     0004      TS4  A.0
#    --------------------------------+--------+---------+----+---------

2.1.2. Delete the rootfs and unnecessary raw image:

sudo rm -rf rootfs
sudo rm -f bootloader/system.img.raw

2.1.3. Create the tarball:

  • Note: This might take a while.
cd ..
tar cjf tegra_env.tbz2 Linux_for_Tegra/ --verbose

2.1.4. Store the JetPack environment.

Follow section Store the JetPack environment

2.2 Flash and format the SDD

  • The next steps assume the JetPack environment tarball is already available in the Jetson.
  • These steps are meant to be followed in the Jetson.
  • The Jetson can be accessed through ssh if connected to a network (applies if the minimal FS was flashed).

2.2.1. Untar the JetPack environment tarball:

sudo tar xvjf tegra_env.tbz2 --verbose
  • Note: Remove the tar file if it is copied to the eMMC after extraction, in order to save space. Ideally, the tegra_env.tbz2 file would be inside an SD card

2.2.2. Flash the SSD:

cd Linux_for_Tegra
sudo BOARDID=2888 BOARDSKU=0004 FAB=400 BOARDREV=K.0 ./tools/kernel_flash/l4t_initrd_flash.sh --flash-only -S 8GiB -c tools/kernel_flash/flash_l4t_external.xml --external-only --external-device nvme0n1p1 --direct nvme0n1 jetson-agx-xavier-devkit external

2.3. Partition the SSD

2.3.1. Install dependencies:

sudo apt update
sudo apt install cloud-guest-utils
sudo apt -y install dosfstools

2.3.2. Start parted:

sudo parted /dev/nvme0n1

2.3.3. Create free space to later enlarge the OS partition:

  • In this case the starting point was calculated to be 65GB, refer to the next bullet point for details.
(parted) mkpart primary ext4 65GB 364GB
  • The starting point was calculated like:
OSExtensionStart = (FinalSize - CurrentAPPSize) + APPEnd = (64GB-8GB)+9.2GB} = 65GB
  • The APPEnd can be seen by doing:
(parted) print free

- In this case the output is the following, where the end of the APP partition is at 9.191 GB.

Number  Start   End     Size    File system  Name          Flags
        17.4kB  20.5kB  3072B   Free Space
 2      20.5kB  67.1MB  67.1MB               kernel        msftdata
 3      67.1MB  134MB   67.1MB               kernel_b      msftdata
 4      134MB   135MB   459kB                kernel-dtb    msftdata
 5      135MB   135MB   459kB                kernel-dtb_b  msftdata
 6      135MB   219MB   83.9MB               recovery      msftdata
 7      219MB   220MB   524kB                recovery-dtb  msftdata
 8      220MB   534MB   315MB                RECROOTFS     msftdata
 9      534MB   601MB   67.1MB  fat32        esp           boot, esp
10      601MB   601MB   18.4kB               UDA           msftdata
        601MB   601MB   2048B   Free Space
 1      601MB   9191MB  8590MB  ext4         APP           msftdata
        9191MB  1000GB  991GB   Free Space

2.3.4. Make a partition with the rest of the free space:

(parted) mkpart primary ext4 364GB 1000GB

2.3.5. Quit parted:

(parted) quit

2.3.6. Create the filesystems for the new partitions:

sudo mkfs -t ext4 /dev/nvme0n1p11
sudo mkfs -t vfat /dev/nvme0n1p12

2.3.7. Enlarge the OS partition and filesystem to fill the free space created on step 3:

sudo growpart /dev/nvme0n1 1
sudo e2fsck -f /dev/nvme0n1p1
sudo resize2fs /dev/nvme0n1p1

2.3.8. Verify the SSD is formatted correctly:

sudo parted -l

- An output similar to the following one should be seen:

Number  Start   End     Size    File system  Name          Flags
 2      20.5kB  67.1MB  67.1MB               kernel        msftdata
 3      67.1MB  134MB   67.1MB               kernel_b      msftdata
 4      134MB   135MB   459kB                kernel-dtb    msftdata
 5      135MB   135MB   459kB                kernel-dtb_b  msftdata
 6      135MB   219MB   83.9MB               recovery      msftdata
 7      219MB   220MB   524kB                recovery-dtb  msftdata
 8      220MB   534MB   315MB                RECROOTFS     msftdata
 9      534MB   601MB   67.1MB  fat32        esp           boot, esp
10      601MB   601MB   18.4kB               UDA           msftdata
 1      601MB   65.0GB  64.4GB  ext4         APP           msftdata
11      65.0GB  364GB   299GB   ext4         primary
12      364GB   1000GB  636GB   fat32        primary

2.3.9. Reboot.

  • By default, the Jetson should now boot from SSD.

Important: The data partition (partition 2 in the HPI project context which is meant to hold Docker data) can also be encrypted at this stage as past of the SSD formatting. For this, follow section Encrypt data partition before rebooting the AGX.

3. Flash and create an Encrypted Image for the SSD

  • The following instructions assume that an SSD NVMe card is already installed on the AGX module.
  • The following instructions do not cover image and kernel compilation, but any image can be compiled and used normally.
  • This section is covered on section ll

3.1 Create the JetPack environment

This is the JetPack environment that is going to live inside the eMMC.

3.1.1 Get the sources:

Follow section Get the JetPack sources

3.2 Configure the encypted partition

3.2.1. Modified the enc XML file to flash the encrypted APP partition. This XML file defines the partition table. You can do it by running:

$ sudo ./tools/kernel_flash/l4t_initrd_flash.sh --direct <extdev_on_host> \
      -c <external-partition-layout> \ 
      --external-device <extdev_on_target> \
      [ -p <options> ] \
      [ -S <rootfssize> ] \
      <boardname> external

Instructions can be found in Linux_for_Tegra/tools/kernel_flash/>README_initrd_flash.txt README file, follow the Workflow 11: Manually generate a bootable external storage device.

3.2.2 Encrypted workaround:

A way to recover the NVIDIA-generated key is by modifying the Python script that generates it. The script can be found here: tools/disk_encryption/gen_luks_passphrase.py. To obtain the generated passphrase, save the key to a file by adding the following lines to the script:

    # Generate passphrase
    if args.unique_pass:
        label_str = "luks-srv-passphrase-unique"
    else:
        label_str = "luks-srv-passphrase-generic"
    context_str = ''.join(str(c) for c in args.context_string)
    passphrase = nist_sp_800_108_with_CMAC(luks_key, context_str, label_str)
    print("%s" % passphrase.hex())

+    f = open("PASS.txt", "w")
+    f.write("%s" % passphrase.hex())
+    f.close()

if __name__ == "__main__":
    main()

This will generate a file called PASS.txt inside the bootloader directory. In this file lives the passphrase that is going to be needed later on to decrypt the filesystem. Please be aware that this PASS.txt should not be included in the final JetPack generated environment, it's just meant to be saved for later use.

/$HOME/Linux_for_Tegra/bootloader$  
✵ >>> ls | grep PASS
PASS.txt

3.2.3 Generate the encrypted key:

Go to the following location:

cd tools/disk_encryption/

Create the encrypted key that will be used in the next steps:

openssl rand -rand /dev/random -hex 16 > encrypted.key

Be aware of the path where this key is stored. DO NOT DELETE IT. It will be need it several times during this process.

3.2.4 Edit l4t_flash_from_kernel.sh: This script is located on /$HOME/Linux_for_Tegra/tools/kernel_flash. Add the following lines:

function get_dev_name_on_target
{
local disk_name=
local device_name="$1"
local part_num
if [[ "${host_mode}" = "0" ]]; then
    echo "${device_name}"
else
    disk_name=$(get_disk_name "$(basename "${device_name}")")
+    shopt -s extglob
    part_num=${device_name##${disk_name}}
+    part_num=${part_num##${part_num%%+([[:digit:]])}}
+    shopt -u extglob
    get_partition "${device_map["${disk_name}"]}" "${part_num}"
fi
}

3.2.5. Replace the nvsimg2img binary This file is located at /$HOME/Linux_for_Tegra/tools/kernel_flash/bin/. Download this file simg2img, and replace it for the old.

cd Downloads/
mv simg2img nvsimg2img
mv nvsimg2img /$HOME/Linux_for_Tegra/tools/kernel_flash/bin/

3.2.6. Put the AGX into recovery mode.

3.2.7. Create the images:

  • Note: The image size is specified with the -S flag. It can be smaller than 8GiB if a smaller filesystem is generated.
sudo ROOTFS_ENC=1 ./tools/kernel_flash/l4t_initrd_flash.sh --no-flash --external-device nvme0n1p1 -c ./tools/kernel_flash/flash_l4t_nvme_rootfs_enc.xml -p "-i encryption.key" --external-only -S 8GiB --direct nvme0n1 jetson-xavier external

Note: The flag "-i encryption.key" is the path to the recently created encrypted key, which should be: /$HOME/Linux_For_Tegra/tools/disk_ecnryption/encrypted.key

3.2.8. Delete the rootfs and unnecessary images:

sudo rm -rf rootfs
sudo rm -f bootloader/system_boot.*
sudo rm -f bootloader/system_root_encrypted.*

3.2.9. Create the tarball:

  • Note: This might take a while.
cd ..
tar cjf tegra_env.tbz2 Linux_for_Tegra/ --verbose

3.4. Flash and format the SDD

  • This section will explain how to flash the encrypted environment to the SSD from the Jetson platform.
  • The next steps assume the JetPack environment tarball is already available in the Jetson.
  • These steps are meant to be followed in the Jetson.
  • The Jetson can be accessed through ssh if connected to a network (applies if the minimal FS was flashed).

3.4.1. Untar the JetPack environment tarball:

sudo tar xvjf tegra_env.tbz2 --verbose
  • Note: Remove the tar file if it is copied to the eMMC after extraction to save space. Ideally, the tegra_env.tbz2 file would be inside an SD card.

3.4.2. Patch the XML partition table file with the right SSD disk dimensions:

cd Linux_for_Tegra
sed -i "s/num_sectors=\"122159104\"/"num_sectors="\"$(cat /sys/block/nvme0n1/size)\"/g" tools/kernel_flash/flash_l4t_nvme_rootfs_enc.xml
sed -i "s/sector_size=\"512\"/"sector_size="\"$(cat /sys/block/nvme0n1/queue/hw_sector_size)\"/g" tools/kernel_flash/flash_l4t_nvme_rootfs_enc.xml

3.4.3. Flash the SSD:

sudo ./tools/kernel_flash/l4t_initrd_flash.sh --flash-only --external-device nvme0n1p1 -c ./tools/kernel_flash/flash_l4t_nvme_rootfs_enc.xml --external-only -S 8GiB --direct nvme0n1 jetson-xavier external

3.5. Partition the SSD

3.5.1. Install dependencies:

sudo apt update
sudo apt install cloud-guest-utils
sudo apt -y install dosfstools

3.5.2. Start parted:

sudo parted /dev/nvme0n1

3.5.3. Create free space to later enlarge the OS partition:

  • In this case the starting point was calculated to be 65GB, refer to the next bullet point for details.
(parted) mkpart primary ext4 65GB 364GB
  • The starting point (65GB) was calculated like:
OSExtensionStart = (FinalSize - CurrentAPPSize) + APP_ENC_End = (64GB-8GB)+9.3GB = 65GB
  • The APP_ENC_End can be seen by doing:
(parted) print free

- In this case the output is the following, where the end of the APP partition is at 9.225 GB.

Number  Start   End     Size    File system  Name          Flags
        17,4kB  20,5kB  3072B   Free Space
 3      20,5kB  83,9MB  83,9MB               recovery      msftdata
 4      83,9MB  84,4MB  524kB                recovery-dtb  msftdata
 5      84,4MB  168MB   83,9MB               kernel        msftdata
 6      168MB   252MB   83,9MB               kernel_b      msftdata
 7      252MB   253MB   524kB                kernel-dtb    msftdata
 8      253MB   253MB   524kB                kernel-dtb_b  msftdata
 9      253MB   568MB   315MB                RECROOTFS     msftdata
10      568MB   635MB   67,1MB  fat32        esp           boot, esp
11      635MB   635MB   18,4kB               UDA           msftdata
        635MB   635MB   2048B   Free Space
 1      635MB   1054MB  419MB   ext4         APP           msftdata
 2      1054MB  9225MB  8171MB               APP_ENC       msftdata
        9225MB  1000GB  991GB   Free Space

3.5.4. Make a partition with the rest of the free space:

(parted) mkpart primary ext4 364GB 1000GB

3.5.5. Quit parted:

(parted) quit

3.5.6. Create the filesystems for the new partitions:

sudo mkfs -t ext4 /dev/nvme0n1p12
sudo mkfs -t vfat /dev/nvme0n1p13

3.5.7. Decrypt the APP_ENC partition, use the key generated in the flashing process:

sudo cryptsetup -v luksOpen /dev/nvme0n1p2 <target-name>
  • target-name can be any name.

- The output should be something like the following:

Enter passphrase for /dev/nvme0n1p2: 
Key slot 0 unlocked.
Command successful.

3.5.8. Extend the partition and the filesystem to fill the free space created on step 3:

sudo growpart /dev/nvme0n1 2
sudo cryptsetup resize <target-name>
sudo e2fsck -f /dev/mapper/<target-name>
sudo resize2fs /dev/mapper/<target-name>

Validate that the filesystem is really around 64GB by mounting it:

sudo mount /dev/mapper/<target-name> /mnt
df -h /mnt

- Then unmount:

sudo umount /mnt
  • Another way to do this is to run df -h /dev/mapper/<OS-given-target-name>. You can verify which is the target name by running lsblk.
  • These actions will ask again for the partition passphrase.

- Verify that the partition and the filesystem got extended by running lsblk, in this example the target-name is hpi-encrypt:

nvme0n1         259:0    0 931,5G  0 disk  
├─nvme0n1p1     259:24   0   400M  0 part  
├─nvme0n1p2     259:25   0  59,6G  0 part  
│ └─hpi-encrypt 252:0    0  59,6G  0 crypt 
├─nvme0n1p3     259:26   0    80M  0 part  
├─nvme0n1p4     259:27   0   512K  0 part  
├─nvme0n1p5     259:28   0    80M  0 part  
├─nvme0n1p6     259:29   0    80M  0 part  
├─nvme0n1p7     259:30   0   512K  0 part  
├─nvme0n1p8     259:31   0   512K  0 part  
├─nvme0n1p9     259:32   0   300M  0 part  
├─nvme0n1p10    259:33   0    64M  0 part  
├─nvme0n1p11    259:34   0    18K  0 part  
├─nvme0n1p12    259:35   0 278,5G  0 part  
└─nvme0n1p13    259:36   0 592,5G  0 part 

3.5.9. Close the encrypted partition:

sudo cryptsetup -v luksClose <target-name>

3.5.10. Verify the SSD is formatted correctly:

sudo parted -l

- An output similar to the following one should be seen:

Number  Start   End     Size    File system  Name          Flags
 3      20,5kB  83,9MB  83,9MB               recovery      msftdata
 4      83,9MB  84,4MB  524kB                recovery-dtb  msftdata
 5      84,4MB  168MB   83,9MB               kernel        msftdata
 6      168MB   252MB   83,9MB               kernel_b      msftdata
 7      252MB   253MB   524kB                kernel-dtb    msftdata
 8      253MB   253MB   524kB                kernel-dtb_b  msftdata
 9      253MB   568MB   315MB                RECROOTFS     msftdata
10      568MB   635MB   67,1MB  fat32        esp           boot, esp
11      635MB   635MB   18,4kB               UDA           msftdata
 1      635MB   1054MB  419MB   ext4         APP           msftdata
 2      1054MB  65,0GB  63,9GB               APP_ENC       msftdata
12      65,0GB  364GB   299GB   ext4         primary
13      364GB   1000GB  636GB   fat32        primary

Important: The data partition can also be encrypted at this stage as past of the SSD formatting. For this, follow section Encrypt data partition before rebooting the platform.

3.5.11. Reboot.

  • By default, the Jetson should now boot from SSD.

General procedures

This section aims to gather different procedures, with detailed steps, that were needed to the workflow of this wiki but maybe they were not wide explained.

1. Encrypt data partition

  • The partition to encrypt use in these instructions is /dev/nvme0n1p11 for example purposes.

1. Format the partition using cryptsetup, it will ask you to create a passcode for the partition:

Important: that this will overwrite the current data.

sudo cryptsetup -y -v luksFormat /dev/nvme0n1p11
  • Now, if you try to mount the partition it should fail:
sudo mount /dev/nvme0n1p11 /mnt

- The error log should look like:

mount: /mnt: unknown filesystem type 'crypto_LUKS'.

2. Configure a target to be able to access the partition:

  • The target can have any name.
sudo cryptsetup -v luksOpen /dev/nvme0n1p11 <target-name>
  • Now the partition is accessible through a sub partition. This can be validated by running:
lsblk

- The output should be something like the following (using hpidata as target name):

├─nvme0n1p9  259:9    0    64M  0 part  
├─nvme0n1p10 259:10   0    18K  0 part  
├─nvme0n1p11 259:11   0 278,5G  0 part  
│ └─hpdata   252:0    0 278,5G  0 crypt 
└─nvme0n1p12 259:12   0 592,5G  0 part 

3. Create a filesystem using the target:

  • The following command created an ext4 type filesystem.
sudo mkfs.ext4 /dev/mapper/<target-name>

4. Close the target:

sudo cryptsetup -v luksClose <target-name>
  • If you do lsblk again, the target (sub partition) will be gone.

2. Access the encrypted partition

1. Configure a target to be able to access the partition:

  • The target can have any name.
sudo cryptsetup -v luksOpen /dev/nvme0n1p11 <target-name>

2. Mount the target:

sudo mount -v /dev/mapper/<target-name> /mnt
  • From this point, the data on the partition can be accessed through the mount point.

3. Unmount the target and close the partition:

sudo umount /mnt
sudo cryptsetup -v luksClose <target-name>

3. Add a Key File to Decrypt LUKS Partition

Important: LUKS only allows having 8 different keys, including the passphrase.

1. Generate a key a store it in a file. For example:

echo "Q2fQamleTgKoRM9luHiSRoerTojy9w0a" > luks_key.txt

2. Add the key to the LUKS partition. In order to do this you need to enter the passphrase initially set.

sudo cryptsetup luksAddKey /dev/nvme0n1p11 luks_key.txt

Note: If you want to only use the key file instead of a passphrase use the following command when formatting the partition (see Step 1 of the Encrypt data partition section):

sudo cryptsetup -y -v luksFormat /dev/nvme0n1p11 --key-file=luks_key.txt

3 Try to access the partition to test the key.

sudo cryptsetup -v luksOpen /dev/nvme0n1p11 <target-name> --key-file=luks_key.txt

- The output should be something like the following:

# Output
Key slot 1 unlocked.
Command successful.

4. Close the partition:

sudo cryptsetup -v luksClose <target-name>

4. Store the JetPack environment

Since the standard Ubuntu filesystem that comes by default with JetPack takes too much space and the JetPack environment might not fit, one of the two options below is recommended to be followed.

4.1 Flash the AGX eMMC with a minimal filesystem

  • This method flashes a very minimal filesystem. It does not include a desktop GUI.

On the host computer used to flash the AGX's eMMC:

1. Follow subsection Create a minimal filesystem to create a minimal filesystem.

2. Flash the eMMC:

- Set the AGX to Force Recovery mode and connect it to the host computer, then execute the flash script.

sudo ./flash.sh jetson-xavier mmcblk0p1

3. Once the eMMC is flashed, connect it to a network using an ethernet cable and transfer the JetPack environment tarball:

rsync --progress tegra_env.tbz2 <username>@<Jetson-IP>:~

4.2 Use an SD card to hold the JetPack environment

  • This method can be used if reflashing the eMMC is not desired. Just save the JetPack environment tarball into a (preferrably 16GB or larger) micro SD card and then insert it into the Jetson.
  • On the Jetson, mount the SD card to get into its contents.

5. SSD partitioning

The following sections detail how to partition and delete partitions from the disk.

The following command can be run to have more information about the storage devices detected:

sudo parted -l

5.1 How to make partitions

1. Check de NVMe's device name:

lsblk -d -p | grep nvme | cut -d\  -f 1

- The output should something like the following:

/dev/nvme0n1

2. Start parted on the device disk:

sudo parted /dev/nvme0n1

3. Once on parted select the device by running select /dev/nvme0n1 + Enter:

- The output should be the following:

(parted) select /dev/nvme0n1                                              
Using /dev/nvme0n1

4. Make the disk label by running mklabel gpt.

5. Make the desired partitions, for example to make 3 partitions run the following:

mkpart primary ext4 1MB 64GB
mkpart primary ext4 64GB 364GB
mkpart primary ext4 364GB 1024GB

Note: To check the free space of the disk (helpful to know how much space is left after making each partition) run print free.

- The output should be something like the following:

Model: Samsung SSD 970 EVO Plus 1TB (nvme)
Disk /dev/nvme0n1: 1000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start   End     Size    File system  Name     Flags
        17.4kB  1049kB  1031kB  Free Space
 1      1049kB  64.0GB  64.0GB  ext4         primary
 2      64.0GB  364GB   300GB   ext4         primary
        364GB   1000GB  636GB   Free Space

6. Quit parted by running quit.

7. Build the filesystem for each partition, for example for the three partitions created:

sudo mkfs -t ext4 /dev/nvme0n1p1
sudo mkfs -t ext4 /dev/nvme0n1p2
sudo mkfs -t vfat /dev/nvme0n1p3

5.2 Mount a partition permanently

5.2.1 Encrypted LUKS partition

To always have a partition mounted on boot, follow the next instructions. In this section, the previously encrypted partition "nvme0n1p2" will help as an example.

First add the mapper information to the /etc/crypttab file.

sudo vim /etc/crypttab

Add the following information:

<target name> <source device> <key-file> <options>

Where:

  • target name: describes the mapped device name. For example, if your device mapping is /dev/mapper/hpi, then hpi is the required target.
  • source device: describes either the block special device or file that contains the encrypted data. This is specified using UUID=<uuid>, or LABEL=<label>, PARTUUID=<partuuid> or PARTLABEL=<partlabel>.

To find the UUID information, you can use:

lsblk -f /dev/nvme0n1p2

And the output should look like this one:

NAME      FSTYPE      LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
nvme0n1p2 crypto_LUKS       9c3b5881-a4f8-4a51-a785-b55597b458cb                
└─hpi     ext4              dac3843f-2556-41fe-90c6-7f7efc0c508f   53.2G     4% 
  • key-file: describes the file to use as a key for decrypting the data of the source device. Note that the passphrase must not be followed by a newline character. On this case, I used the LUK key previously generated before creating the encrypted images on the host computer.
  • options: describes the cryptsetup options associated with the encryption process. At minimum, the field should contain either the string luks respectively tcrypt or the cipher, hash and size options. Options are in the format: key=value [,key=value …].

Note: Consult man crypttab for more information.

The information that will be added should look like this:

hpi UUID="9c3b5881-a4f8-4a51-a785-b55597b458cb" /home/nvidia/Linux_for_Tegra/tools/disk_encryption/encrypted.key luks

Be aware that the UUID added above is the one that belongs to the nvme0n1p2 partition, NOT the one of the hpi target device.

Next, create a mount point.

sudo mkdir /mnt/capturemodes

Then, update the /etc/fstab file with device information in order to define how to mount the LUKS device. The entry in the /etc/fstab file should take the format;

<file system> <mount point>   <type>  <options>    <dump>  <pass> 

Following the example, it should look like this:

/dev/mapper/hpi /mnt/capturemodes ext4 defaults 0 0 

Verify using the mount command before rebooting the system. If all is working, you should see “successfully mounted” for your LUKS device.

$ mount -av
/                        : ignored
/mnt/capturemodes       : successfully mounted

You can now reboot your system to confirm the same.

systemctl reboot

Once the reboot is done, check the mounting:

$ lsblk
nvme0n1      259:0    0 931.5G  0 disk  
├─nvme0n1p1  259:1    0   400M  0 part  
├─nvme0n1p2  259:2    0  59.6G  0 part  
│ └─hpi      252:0    0  59.6G  0 crypt /mnt/capturemodes

Note: After turning on or rebooting the board, the serial console will ask for the passphrase:

Please enter passphrase for disk APP_ENC (hpi) on /mnt/capturemodes: 

5.2.2. Mount a normal partition

Add a rule to the /etc/fstab file. For example, to have the second partition made on these examples be always mounted on /mnt/capturemodes add the following line:

/dev/nvme0n1p2       /mnt/capturemodes     ext4           defaults                                     0 1

6. Delete partitions

1. Select the disk device using fdisk:

sudo fdisk /dev/nvme0n1

2. Make sure to have selected the right device by printing its information by pressing p + Enter.

- The output should show the disk information:

Command (m for help): p
Disk /dev/nvme0n1: 931.53 GiB, 1000204886016 bytes, 1953525168 sectors
Disk model: Samsung SSD 970 EVO Plus 1TB            
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 4B4A6BA5-6665-42A9-A0EB-DF76D474D831

Device             Start        End    Sectors  Size Type
/dev/nvme0n1p1        40   33554471   33554432   16G Microsoft basic data
/dev/nvme0n1p2  33554472   33685543     131072   64M Microsoft basic data
/dev/nvme0n1p3  33685544   33816615     131072   64M Microsoft basic data
/dev/nvme0n1p4  33816616   33817511        896  448K Microsoft basic data
/dev/nvme0n1p5  33817512   33818407        896  448K Microsoft basic data
/dev/nvme0n1p6  33818408   33982247     163840   80M Microsoft basic data
/dev/nvme0n1p7  33982248   33983271       1024  512K Microsoft basic data
/dev/nvme0n1p8  33983272   34597671     614400  300M Microsoft basic data
/dev/nvme0n1p9  34597672   34728743     131072   64M EFI System
/dev/nvme0n1p10 34728744 1953525134 1918796391  915G Microsoft basic data

3. To delete a partition, press d + Enter, then <partition number to delete> + Enter:

Command (m for help): d
Partition number (1-10, default 10): 1

Partition 1 has been deleted

Note: If there is just one partition, it will be deleted immediately after d + Enter.

4. Write the changes to the disk by pressing w + Enter:

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

7. Make Jetson boot from SSD - using JetPack tools and a host PC

Important: Make sure that an SSD is installed on the Jetson before going ahead with the next steps.

1. Make sure that the Jetson can boot from the eMMC. This is an obligatory requirement. The steps following this one assume the Jetson can boot from the eMMC and that a JetPack environment is already set up. To setup JetPack and flash the eMMC follow the instructions at Build and testing instructions.

2. Install the necessary dependencies on the host PC:

sudo apt install libxml2-utils simg2img network-manager abootimg sshpass device-tree-compiler

3. Stop the automount service (this command applies to Debian based systems):

systemctl stop udisks2.service

4. Move to the Linux_for_Tegra directory and set the Jetson to recovery mode.

cd <JetPack-directory>/Linux_for_Tegra

5. Flash the NVMe:

sudo ./tools/kernel_flash/l4t_initrd_flash.sh --external-device nvme0n1p1 -c ./tools/kernel_flash/flash_l4t_nvme.xml --external-only -S <APP-partition-size>GiB --showlogs  jetson-xavier nvme0n1p1
  • <APP-partition-size> corresponds to the size root (/) will take.

- For example for a 16GB root:

sudo ./tools/kernel_flash/l4t_initrd_flash.sh --external-device nvme0n1p1 -c ./tools/kernel_flash/flash_l4t_nvme.xml --external-only -S 16GiB  --showlogs  jetson-xavier nvme0n1p1

Note: The flashing process can take a long time (from ~30min to ~1h).

  • After the flashing is done reboot the Jetson and it should boot from the SSD. If it does not, refer to the Change boot order section.
  • To check where the system is booting from, the flag in the extlinux.conf file can be checked or by running lsblk and checking where / is mounted:

- Cat the extlinux.conf file:

cat /boot/extlinux/extlinux.conf

- Look for the root flag, in this case we are booting from eMMC.

.
.
.
APPEND ${cbootargs} root=/dev/mmcblk0p1

- Run lsblk:

lsblk

- We can see that / is mounted on mmcblk0p1 (eMMC):

nvidia@hpi-xavier:~$ lsblk
NAME         MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
mmcblk0      179:0    0  29.1G  0 disk 
├─mmcblk0p1  179:1    0    28G  0 part /

8. Change the boot order

The order in which the Jetson is going to try to boot from can be modified. By default, after installing an SSD, the boot order will be to try to boot from the SSD and then from eMMC. Modifying the boot order can be useful in cases where a boot from eMMC is needed to modify the SDD partitions, for example.

Important: A display, a mouse and keyboard are needed for this method.

1. Connect a display, mouse and keyboard to the Jetson and boot. When booting, press the F12 key repeatedly until a message saying "Press ESC for boot options" appears. When this appears, immediately press ESC.

2. On the UEFI GUI, select the following:

Boot Maintenance Manager  
   Boot Options 
       Change Boot Order

3. Select the booting options (should be highlighted in black when selected) and press Enter.

4. A menu like the following will appear. To move the selected option up or down, use the + and - keyboard keys. Then press Enter.



5. Hit ESC until the main menu is reached. Once here, select Continue and press Enter. The Jetson should boot from the selected device after this step.

After all these steps are done your Jetson should boot from the encrypted SSD