Production Workflow#

This section provides the tools, templates, and step-by-step procedures for fTPM production when a single entity plays both the ODM and OEM roles. It covers everything from BSP installation through factory flashing.

For the conceptual design and the architectural tolerance that allows ODM and OEM to be two separate entities, refer to fTPM Production Flow.

Jetson BSP Installation#

  1. Download the latest BSP from Jetson Linux Archive.

    • The Jetson BSP: Jetson_Linux_${rel_ver}_aarch64.tbz2

    • The sample root filesystem: Tegra_Linux_Sample-Root-Filesystem_${rel_ver}_aarch64.tbz2

    • The public source tarball: public_sources.tbz2

    • The FSKP tools package: host_overlay_fskp_tools_${rel_ver}_aarch64.tbz2

    • The toolchain (optional): x-tools.tbz2

  2. Create a ${BSP_TOP} folder.

    mkdir ${BSP_TOP}
    cd ${BSP_TOP}
    
  3. Install the BSP.

    1. Untar the BSP package.

      tar jvxf ~/Downloads/Jetson_Linux_${rel_ver}_aarch64.tbz2
      
    2. Untar the BSP source package.

      tar jvxf ~/Downloads/public_sources.tbz2
      
    3. Untar the sample RootFS.

      cd Linux_for_Tegra/rootfs
      sudo tar jvxpf ~/Downloads/Tegra_Linux_Sample-Root-Filesystem_${rel_ver}_aarch64.tbz2
      
    4. Apply the BSP packages.

      cd ${BSP_TOP}/Linux_for_Tegra
      sudo ./apply_binaries.sh
      
    5. Install the FSKP packages.

      cd ${BSP_TOP}
      tar jvxf ~/Downloads/host_overlay_fskp_tools_${rel_ver}_aarch64.tbz2
      
    6. Untar the ATF and OP-TEE source tarball.

      cd Linux_for_Tegra/source/
      tar jvxf atf_src.tbz2
      tar jvxf nvidia-jetson-optee-source.tbz2
      cd ../
      

Server Setup for fTPM Production Scripts#

Install the required Python modules:

sudo apt-get update
sudo apt-get install -y git python3-pip python3-venv
python3 -m venv ${BSP_TOP}/ftpm-venv
source ${BSP_TOP}/ftpm-venv/bin/activate
${BSP_TOP}/ftpm-venv/bin/python -m pip install --upgrade pip
${BSP_TOP}/ftpm-venv/bin/python -m pip install git+https://github.com/wbond/asn1crypto.git@master
${BSP_TOP}/ftpm-venv/bin/python -m pip install git+https://github.com/wbond/oscrypto.git@master
${BSP_TOP}/ftpm-venv/bin/python -m pip install cryptography ecdsa numpy pyaes pycryptodomex pyyaml

This virtual environment is for the host that runs the fTPM production scripts. Activate it on the host before running the Python-based tools in this workflow.

Prerequisites for Enabling fTPM#

The KDK0 is the root key. The combined KDK0 and Device_SN values must be unique to each device, and the KDK0 itself is a 256-bit secret value. The FUSE_BOOT_SECURITY_INFO must be burned with the following bits.

FUSE_BOOT_SECURITY_INFO for Jetson Thor Series

Bits

Description

[3]

Secure boot encryption. Must be 1 to enable SBK.

[5]

Revocation policy. Must be 1 to enable the revocation policy.

[9]

OEM key valid. Must be 1.

[13]

fTPM enable. Must be 1 to enable fTPM.

FUSE_BOOT_SECURITY_INFO for Jetson Orin Series

Bits

Description

[2:0]

Authentication scheme field. Cannot be zero or greater than 011b. Must be a valid PKC authentication scheme.

[3]

Secure boot encryption. Must be 1 to enable SBK.

[9]

OEM key valid. Must be 1.

[11]

OEM key function. Must be 1 to enable KDF of the OEM fuse key.

[13]

OEM key function. Must be 1 to enable KDF of the Silicon ID generation.

KDK Database and Fuseblob Generation#

Generating kdk-db and Silicon ID Public Keys#

From ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool, run ./kdk_gen.py to generate the KDK database and corresponding Silicon ID public keys.

cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool

# Command line interface
./kdk_gen.py [-h] [--chip ${CHIP}] [--oem_id ${OEM_ID}] [--sn ${SN}] [--num_devices ${NUM_OF_DEVICES}] [--preset ${FILE} | --fixed --preset ${FILE}]

Example of five KDK entries with random KDK values:

cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool

# Jetson Thor series
./kdk_gen.py --chip t264 --oem_id 0x102 --sn 0x100000002 --num_devices 5

# Jetson Orin series
./kdk_gen.py --chip t234 --oem_id 0x102 --sn 0x100000002 --num_devices 5

Output files:

  • ftpm_kdk/kdk_db_0102-0000000100000002-5.csv

  • ftpm_kdk/pubkey_db_0102-0000000100000002-5.csv

KDK database contents (random KDK values):

cat ftpm_kdk/kdk_db_0102-0000000100000002-5.csv
0102 0000000100000002 681a8d62bffb803e9b1068eb4e14af3e440b5a73d0e81d161d2817c24dac41a6
0102 0000000100000003 bfec6db0ddab599b2a34d01ab9e641e3b735d7ead9649579689ed535c73d2053
0102 0000000100000004 801575a8ecd51955f8efcc840af0b6f94a4a0d1e2745f97122d46873c83bd751
0102 0000000100000005 a8d49348cbbf34814b387c7189187a1c98fe03a254503e879232765651dcf0ee
0102 0000000100000006 a8e20c2e0309a6564d0c694c75fcce9b21b846c54dc3b6343a8c276bbbd2dca5

The first two columns are oem-id and SN; the last column is the randomly generated KDK value.

Example of three KDK entries with a fixed KDK value:

cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool

# Store the fixed KDK value in a file
echo "681a8d62bffb803e9b1068eb4e14af3e440b5a73d0e81d161d2817c24dac41a6" > preset_kdk.txt

# Jetson Thor series
./kdk_gen.py --chip t264 --oem_id 0x102 --sn 0x100000002 --num_devices 3 --fixed --preset preset_kdk.txt

# Jetson Orin series
./kdk_gen.py --chip t234 --oem_id 0x102 --sn 0x100000002 --num_devices 3 --fixed --preset preset_kdk.txt

Note

The KDK database is used to generate fuseblobs.

Important

The Silicon ID public key database can be used by the fTPM manufacturer CA to verify device identity during provisioning.

cat ftpm_kdk/pubkey_db_0102-0000000100000002-5.csv
0102 0000000100000002 435ec556a1e23e9a676b8471ff2b2c25ca262bbc7f581abd69de025198cb56d157882fc257c4f544b206796dadf8257d5c4638e70bf8a2f13d9a2b7a30b57c0a
0102 0000000100000003 c25aef9b683cc4a2579bf634e35d42ee40b5e31dccef5dad7a16ab8b860dc2ec25a20ed6391edd8aa871cfa999acf57af1f81f1226e74569b2f3fff549bc0809
# The first two columns are oem-id and SN; the last column is the silicon-id public key.

Fuse Configuration Templates#

For the template fuse-configuration XML file, refer to Fuse Configuration File.

Create a template fuse-configuration XML file named fuse_temp.xml. The ODM and OEM use different template files. The following examples use sample values for the shared fuse contents; only KDK0, OEM_ID, and SN remain symbolic because they vary per device.

Fuse Configuration for the Jetson Thor Series#

ODM Fuse Configuration

Fuse Name

Value

PscOemKdk0

KDK0

OdmInfo

OEM_ID

OdmId

SN

<genericfuse MagicId="0x45535546" version="1.0.0">
   <fuse name="PublicKeyHash" size="64" value="0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"/>
   <fuse name="OptInEnable" size="4" value="0x1"/>
   <fuse name="PscOemKdk0" size="32" value="0x<32-byte-value-PscOemKdk0>"/>
   <fuse name="OespOemKdk0" size="32" value="0x111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000"/>
   <fuse name="SbOemKdk0" size="32" value="0x0000ffffeeeeddddccccbbbbaaaa999988887777666655554444333322221111"/>
   <fuse name="OdmInfo" size="4" value="0x<4-byte-value-OdmInfo>"/>
   <fuse name="OdmId" size="8" value="0x<8-byte-value-OdmId>"/>
   <fuse name="PscSecureBootKey" size="32" value="0x1234567890abcdeffedcba09876543211234567890abcdeffedcba0987654321"/>
   <fuse name="OespSecureBootKey" size="32" value="0x1234567890abcdeffedcba09876543211234567890abcdeffedcba0987654321"/>
   <fuse name="SbSecureBootKey" size="32" value="0x1234567890abcdeffedcba09876543211234567890abcdeffedcba0987654321"/>
   <fuse name="ArmJtagDisable" size="4" value="0x1"/>
   <fuse name="BootSecurityInfo" size="4" value="0x00002228"/>
   <fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>

OEM Fuse Configuration

Fuse Name

Value

PscOemKdk1

The root key of the EKB encryption and authentication key.

<genericfuse MagicId="0x45535546" version="1.0.0">
   <fuse name="PscOemKdk1" size="32" value="0x0f1e2d3c4b5a69780f1e2d3c4b5a69780f1e2d3c4b5a69780f1e2d3c4b5a6978"/>
   <fuse name="OespOemKdk1" size="32" value="0x89abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567"/>
   <fuse name="SbOemKdk1" size="32" value="0x76543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba98"/>
</genericfuse>

Fuse Configuration for the Jetson Orin Series#

ODM Fuse Configuration

Fuse Name

Value

Kdk0

KDK0

OdmInfo

OEM_ID

OdmId

SN

<genericfuse MagicId="0x45535546" version="1.0.0">
   <fuse name="OdmInfo" size="4" value="0x<4-byte-value-OdmInfo>"/>
   <fuse name="OdmId" size="8" value="0x<8-byte-value-OdmId>"/>
   <fuse name="Kdk0" size="32" value="0x<32-byte-value-Kdk0>"/>
   <fuse name="PublicKeyHash" size="64" value="0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"/>
   <fuse name="PkcPubKeyHash1" size="64" value="0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"/>
   <fuse name="PkcPubKeyHash2" size="64" value="0xffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100"/>
   <fuse name="OptInEnable" size="4" value="0x1"/>
   <fuse name="SecureBootKey" size="32" value="0x1234567890abcdeffedcba09876543211234567890abcdeffedcba0987654321"/>
   <fuse name="ArmJtagDisable" size="4" value="0x1"/>
   <fuse name="BootSecurityInfo" size="4" value="0x00002a09"/>
   <fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>

OEM Fuse Configuration

Fuse Name

Value

OemK1

The root key of the EKB encryption and authentication key.

OemK2

The root key of the UEFI PV key.

<genericfuse MagicId="0x45535546" version="1.0.0">
   <fuse name="OemK1" size="32" value="0x13579bdf2468ace013579bdf2468ace013579bdf2468ace013579bdf2468ace0"/>
   <fuse name="OemK2" size="32" value="0x02468ace13579bdf02468ace13579bdf02468ace13579bdf02468ace13579bdf"/>
   <fuse name="PscOdmStatic" size="4" value="0x00000060"/>
</genericfuse>

Note

Replace the sample hash, key, and board-specific fuse values with the values for your platform before running fskp_fuseburn.py. Replace KDK0, OEM_ID, and SN with the per-device data from your KDK database.

Generating Fuseblobs#

Run fskp_fuseburn.py to generate fuseblobs.

ODM example—five fuseblobs using the kdk-db:

# Jetson Thor series
sudo ./fskp_fuseburn.py -f fuse_temp_odm.xml \
                        --test \
                        --skipfskpkey \
                        --multi-blob ftpm_kdk/kdk_db_0102-0000000100000002-5.csv \
                        -g odm_out/ \
                        -c 0x26 \
                        --board-spec thor-agx-board-spec.txt \
                        -B ../../../../jetson-agx-thor-devkit.conf

# Jetson Orin series
sudo ./fskp_fuseburn.py -f fuse_temp_odm.xml \
                        --test \
                        --skipfskpkey \
                        --multi-blob ftpm_kdk/kdk_db_0102-0000000100000002-5.csv \
                        -g odm_out/ \
                        -c 0x23 \
                        --board-spec orin-agx-board-spec.txt \
                        -B ../../../../jetson-agx-orin-devkit.conf

# Package for delivery
tar cf odm_out_0102-0000000100000002-5.tar odm_out/

OEM example:

# Jetson Thor series
sudo ./fskp_fuseburn.py -f fuse_temp_oem.xml \
                        --test \
                        --skipfskpkey \
                        -g oem_out/ \
                        -c 0x26 \
                        --board-spec thor-agx-board-spec.txt \
                        -B ../../../../jetson-agx-thor-devkit.conf

# Jetson Orin series
sudo ./fskp_fuseburn.py -f fuse_temp_oem.xml \
                        --test \
                        --skipfskpkey \
                        -g oem_out/ \
                        -c 0x23 \
                        --board-spec orin-agx-board-spec.txt \
                        -B ../../../../jetson-agx-orin-devkit.conf

# Package for delivery
tar cf oem_out.tar oem_out/

Command-line options:

  • -f <fuse_temp.xml>: Fuse template file. A list of corresponding fuse configurations is generated in the ftpm_kdk directory (for example, fusexml_0102-0000000100000002.xml through fusexml_0102-0000000100000006.xml).

  • --test: Generate fuseblobs for dry run only. To burn fuses, replace with -b.

  • --skipfskpkey: No FSKP key used (fuseblobs are unencrypted). For production, replace --skipfskpkey:

    • For Jetson Thor series: -i <fskp_select> --key <fskp_key.bin>. The value for -i comes from fskp_select.txt.

    • For Jetson Orin series: --key-exp <fskp_ak.bin> <fskp_ek.bin> --fskpcfg <fskp_conf.txt>

  • --multi-blob <kdk_db>: Use the pre-generated kdk-db to fill in the template fuse configuration.

  • -g <out_dir>/<fuse_blob_prefix>: Output folder for generated fuseblobs. For example, -g fuse/out produces fuse/out_0102-0000000100000002.

  • -c <chip ID>: NVIDIA Tegra SoC chip ID.

  • --board-spec <board_spec>: Board specification (such as BOARDID or SKU).

  • -B <board_conf>: Board configuration file for low-level boot components.

EKB Generation#

ODM EKB Generation#

The ODM EKB Gen tool uses the following library modules for EK CSR generation and certificate signing:

  • lib/ek_csr_gen.py: Generates RSA and EC EK CSRs from the device EPS.

  • lib/ca_signing.py: Provides the CAInterface abstract class and the built-in SimulatorCA implementation. To integrate your production CA or HSM-backed CA server, subclass CAInterface.

# Command-line interface
odm_ekb_gen.py [-h] [--chip <CHIP>] [--kdk_db ${KDK_DB}]

# Jetson Thor series
odm_ekb_gen.py --chip t26x --kdk_db ftpm_kdk/kdk_db_0102-0000000100000002-5.csv

# Jetson Orin series
odm_ekb_gen.py --chip t234 --kdk_db ftpm_kdk/kdk_db_0102-0000000100000002-5.csv

The output ODM EKB DB is stored in the odm_out folder. Package and deliver to the OEM.

OEM EKB Generation#

The OEM unpacks the ODM EKB DB and generates the final EKB DB—per-device EKS images authenticated and encrypted by OEM fuse derivation keys.

The OEM EKB Gen tool is a wrapper of the Gen EKB script (gen_ekb.py) from the hwkey-agent sample application.

  1. Move the Gen EKB script to the same folder as the OEM EKB Gen tool.

  2. Run oem_ekb_gen.py:

    # Command-line interface
    oem_ekb_gen.py [-h] [-chip <CHIP>] [-oem_k1_key <oem_k1.key>] [-oem_k2_key <oem_k2.key>] [-oem_kdk1_key <oem_kdk1.key>] [-in_sym_key <sym.key>] [-in_sym_key2 <sym2.key>] [-in_auth_key <auth.key>] [-in_ftpm_odm_ekb <IN_FTPM_ODM_EKB>]
    
    # Jetson Thor series
    oem_ekb_gen.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 \
                   -in_ftpm_odm_ekb odm_out
    
    # Jetson Orin series
    oem_ekb_gen.py -chip t234 \
                   -oem_k1_key oem_k1.key \
                   -in_sym_key sym_t234.key \
                   -in_sym_key2 sym2_t234.key \
                   -in_auth_key auth_t234.key \
                   -in_ftpm_odm_ekb odm_out
    

Command-line options:

  • -chip <CHIP>: Jetson SoC chip name (t234 for Orin, t264 for Thor).

  • -oem_k1_key <oem_k1.key>: Root key of EKB encryption and authentication (Orin series).

  • -oem_kdk1_key <oem_kdk1.key>: Root key of EKB encryption and authentication (Thor series).

  • -in_sym_key <sym.key>: User-defined key for UEFI payloads encryption.

  • -in_sym_key2 <sym2.key>: User-defined key for disk encryption support.

  • -in_auth_key <auth.key>: User-defined key for UEFI variable authentication.

  • -in_ftpm_odm_ekb <IN_FTPM_ODM_EKB>: Path to the EKB_ftpm_db.

The generated per-device EKS images are stored in oem_out. Package and deliver to the ODM.

Image Generation#

Signing and Encrypting the EKB Final DB#

  1. Run l4t_sign_image.sh. For detailed usage, refer to the help option.

  2. Unpack the EKB_final_db to the oem_out folder.

  3. The encrypted and signed files are in oem_out/signed.

cd Linux_for_Tegra
./l4t_sign_image.sh --chip <CHIP_ID> \
                    --key <PKC_KEY> \
                    --encrypt_key <SBK_KEY> \
                    --mass-ekb oem_out \
                    --type <"data" | "eks"> \
                    --split False

# Package the signed EKBs
tar cf eks_<OEM_ID>-<SN>-<num_of_devices>.tar --directory=oem_out/signed/ .

Generating the ODM QSPI Image for Lower Boot Components (LBC)#

Run l4t_initrd_flash.sh with --odm-image-gen, using PKC and SBK as signing/encryption keys to generate lbc_odm.tar.gz.

Refer to ${BSP_TOP}/Linux_for_Tegra/tools/kernel_flash/README_initrd_flash.txt for detailed usage.

cd Linux_for_Tegra
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
                      --no-flash \
                      -u <PKC_KEY> \
                      -v <SBK_KEY> \
                      <BOARD_NAME> \
                      internal

Generating the OEM User Partition Image#

Generate the user partition image (UPI). To generate the UEFI key configuration file, refer to ${BSP_TOP}/Linux_for_Tegra/tools/README_uefi_secureboot.txt. For the Jetson Orin series, also provide the UEFI payload encryption key.

cd Linux_for_Tegra
sudo ./tools/gen_uefi_keys_dts.sh <UEFI_KEYS_CONF_FILE>

# Jetson Thor series:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
                      --no-flash \
                      --uefi-keys <UEFI_KEYS_CONF_FILE> \
                      <BOARD_NAME> \
                      internal

# Jetson Orin series:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
                      --no-flash \
                      --uefi-keys <UEFI_KEYS_CONF_FILE> \
                      --uefi-enc <UEFI_PAYLOAD_ENCRYPTION_KEY> \
                      <BOARD_NAME> \
                      internal

Factory Package Assembly#

cd Linux_for_Tegra

# Untar the LBC and UPI tarballs
sudo tar xvzf lbc_odm.tar.gz
sudo tar xvzf upi_oem.tar.gz

cd tools/kernel_flash/images/internal/

# Untar EKB_final_db received from ODM
sudo mkdir ekb_db
cd ekb_db
sudo tar xf ${ODM}/eks_<OEM_ID>-<SN>-<num_of_devices>.tar

# Package the factory tarball
cd ${BSP_TOP}/Linux_for_Tegra
sudo tar zvcf factory.tar.gz \
              tools/kernel_flash/initrdflashparam.txt \
              tools/kernel_flash/initrdflashimgmap.txt \
              tools/kernel_flash/images/

Factory Operations#

Fuse Burning#

  1. Install L4T BSP and FSKP package on the fuse-burning host at the factory.

  2. Untar the fuseblob.

  3. Burn the OEM fuseblob first; then burn the ODM fuseblob.

Single device:

# Burn OEM fuseblob:
# Jetson Thor series:
sudo ./fskp_fuseburn.py -P ./oem_out \
                        -c 0x26 \
                        --board-spec thor-agx-board-spec.txt \
                        -B ../../../../jetson-agx-thor-devkit.conf

# Jetson Orin series:
sudo ./fskp_fuseburn.py -P ./oem_out \
                        -c 0x23 \
                        --board-spec orin-agx-board-spec.txt \
                        -B ../../../../jetson-agx-orin-devkit.conf

# Burn ODM fuseblob:
# Jetson Thor series:
sudo ./fskp_fuseburn.py -P ./odm_out \
                        -c 0x26 \
                        --board-spec thor-agx-board-spec.txt \
                        -B ../../../../jetson-agx-thor-devkit.conf

# Jetson Orin series:
sudo ./fskp_fuseburn.py -P ./odm_out \
                        -c 0x23 \
                        --board-spec orin-agx-board-spec.txt \
                        -B ../../../../jetson-agx-orin-devkit.conf

Important

The ODM fuseblob must be burned after the OEM fuseblob because the ODM fuseblob contains the Security Mode fuse bit. Once burned, no other fuses can be burned.

Multiple devices:

Note

All devices must be connected to the same host and in recovery mode.

# Burn OEM mass fuseblobs:
# Jetson Thor series:
sudo ./fskp_massfuseburn.py --skipconfirmation \
                            --burnfuse \
                            -P ./oem_out \
                            -c 0x26 \
                            --board-spec thor-agx-board-spec.txt \
                            -B ../../../../jetson-agx-thor-devkit.conf

# Jetson Orin series:
sudo ./fskp_massfuseburn.py --skipconfirmation \
                            --burnfuse \
                            -P ./oem_out \
                            -c 0x23 \
                            --board-spec orin-agx-board-spec.txt \
                            -B ../../../../jetson-agx-orin-devkit.conf

# Burn ODM mass fuseblobs:
# Jetson Thor series:
sudo ./fskp_multiblobfuse.py --out ./odm_out \
                             --oem_id 0x0102 \
                             --sn 0x0000000100000002 \
                             --num_devices 5 \
                             --board-spec thor-agx-board-spec.txt \
                             -B ../../../../jetson-agx-thor-devkit.conf

# Jetson Orin series:
sudo ./fskp_multiblobfuse.py --out ./odm_out \
                             --oem_id 0x0102 \
                             --sn 0x0000000100000002 \
                             --num_devices 5 \
                             --board-spec orin-agx-board-spec.txt \
                             -B ../../../../jetson-agx-orin-devkit.conf

Flashing Devices#

  1. Install L4T BSP on the host.

  2. Untar the factory package.

  3. Flash the devices.

    cd ${BSP_TOP}/Linux_for_Tegra
    sudo tar zvxf factory.tar.gz
    cp tools/kernel_flash/images/rcmboot/* bootloader/
    
    # Flash:
    sudo ./l4t_initrd_flash.sh --flash-only \
                               --ekb-pair \
                               <BOARD_NAME> \
                               internal
    

Board-specific flash commands:

  • AGX Thor with UFS: jetson-agx-thor-devkit internal

  • AGX Orin with eMMC: jetson-agx-orin-devkit internal

  • AGX Orin with NVMe: jetson-agx-orin-devkit nvme0n1p1

  • Orin Nano with NVMe: jetson-orin-nano-devkit nvme0n1p1

End-to-End Walkthrough#

This walkthrough shows the end-to-end commands for a single-entity fTPM production flow on Jetson AGX Thor and Jetson AGX Orin platforms. The UEFI PV key feature is not used; the PKC key signs both low-level firmware and the UEFI image.

Preparation#

  1. Complete the Jetson BSP Installation.

  2. Collect the following keys and files:

    • ODM keys:

      # Jetson Thor series:
      ~/odm_keys/t264_pkc_key_list.xml
      ~/odm_keys/sbk-32.key
      
      # Jetson Orin series:
      ~/odm_keys/rsa3k.pem
      ~/odm_keys/sbk-32.key
      
    • ODM FSKP fusing files:

      # Jetson Thor series:
      ~/odm_fskp/odm_fuse_template.xml
      ~/odm_fskp/fskp_select.txt
      ~/odm_fskp/fskp_key.bin
      
      # Jetson Orin series:
      ~/odm_fskp/odm_fuse_template.xml
      ~/odm_fskp/fskp_ak.bin
      ~/odm_fskp/fskp_ek.bin
      ~/odm_fskp/fskp_conf.txt
      
    • OEM user-defined keys (for EKB generation):

      # Jetson Thor series:
      ~/oem_keys/oem_kdk1.key
      ~/oem_keys/sym_t264.key
      ~/oem_keys/sym2_t264.key
      ~/oem_keys/auth_t264.key
      
      # Jetson Orin series:
      ~/oem_keys/oem_k1.key
      ~/oem_keys/sym_t234.key
      ~/oem_keys/sym2_t234.key
      ~/oem_keys/auth_t234.key
      
    • OEM UEFI keys and, for Jetson Orin series, the UEFI payload encryption key:

      # Jetson Thor series:
      ~/oem_keys/uefi_keys/uefi_keys.conf
      
      # Jetson Orin series:
      ~/oem_keys/uefi_keys/uefi_keys.conf
      ~/oem_keys/uefi_keys/uefi_enc.key
      
    • OEM fuse template:

      ~/oem_keys/oem_fuse_template.xml
      

EKB Generation#

  1. Generate the EKB DB with KDK-based EK certificates:

    cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool
    echo "681a8d62bffb803e9b1068eb4e14af3e440b5a73d0e81d161d2817c24dac41a6" > preset_kdk.txt
    
    # Jetson Thor series:
    ./kdk_gen.py --chip t264 --oem_id 0x21 --sn 0x1000218000 --num_devices 2 --fixed --preset preset_kdk.txt
    ./odm_ekb_gen.py --chip t264 --kdk_db ftpm_kdk/kdk_db_0021-0000001000218000-2.csv
    
    # Jetson Orin series:
    ./kdk_gen.py --chip t234 --oem_id 0x21 --sn 0x1000218000 --num_devices 2 --fixed --preset preset_kdk.txt
    ./odm_ekb_gen.py --chip t234 --kdk_db ftpm_kdk/kdk_db_0021-0000001000218000-2.csv
    
  2. Add user keys to EKB:

    cp ../../../hwkey-agent/host/tool/gen_ekb/gen_ekb.py ./
    
    # Jetson Thor series:
    ./oem_ekb_gen.py -chip t264 \
                     -oem_kdk1_key ~/oem_keys/oem_kdk1.key \
                     -in_sym_key ~/oem_keys/sym_t264.key \
                     -in_sym_key2 ~/oem_keys/sym2_t264.key \
                     -in_auth_key ~/oem_keys/auth_t264.key \
                     -in_ftpm_odm_ekb odm_out
    
    # Jetson Orin series:
    ./oem_ekb_gen.py -chip t234 \
                     -oem_k1_key ~/oem_keys/oem_k1.key \
                     -in_sym_key ~/oem_keys/sym_t234.key \
                     -in_sym_key2 ~/oem_keys/sym2_t234.key \
                     -in_auth_key ~/oem_keys/auth_t234.key \
                     -in_ftpm_odm_ekb odm_out
    
    cp -r oem_out/ ${BSP_TOP}
    
  3. Sign and generate the final ekb_db:

    cd ${BSP_TOP}/Linux_for_Tegra
    
    # Jetson Orin series:
    ./l4t_sign_image.sh --chip 0x23 \
                        --key ~/odm_keys/rsa3k.pem \
                        --encrypt_key ~/odm_keys/sbk-32.key \
                        --mass-ekb ${BSP_TOP}/oem_out \
                        --type data \
                        --split False
    
    # Jetson Thor series:
    ./l4t_sign_image.sh --chip 0x26 \
                        --key ~/odm_keys/t264_pkc_key_list.xml \
                        --encrypt_key ~/odm_keys/sbk-32.key \
                        --mass-ekb ${BSP_TOP}/oem_out \
                        --type eks \
                        --split False
    

The encrypted and signed final ekb_db images are stored in ${BSP_TOP}/oem_out/signed.

Fuseblob Generation#

Create a merged fuse template (odm_fuse_template.xml) by combining the ODM and OEM fuse templates. Place the OEM content before the ODM content so that SecurityMode remains the last entry.

Jetson Thor series merged template:

<genericfuse MagicId="0x45535546" version="1.0.0">
   <fuse name="PublicKeyHash" size="64" value="0x<64-byte-value-PubKeyHash>"/>
   <fuse name="OptInEnable" size="4" value="0x1"/>
   <fuse name="PscOemKdk0" size="32" value="0x<32-byte-value-PscOemKdk0>"/>
   <fuse name="PscOemKdk1" size="32" value="0x<32-byte-value-PscOemKdk0>"/>
   <fuse name="OespOemKdk0" size="32" value="0x<32-byte-value-OespOemKdk0>"/>
   <fuse name="OespOemKdk1" size="32" value="0x<32-byte-value-OespOemKdk1>"/>
   <fuse name="SbOemKdk0" size="32" value="0x<32-byte-value-SbOemKdk0>"/>
   <fuse name="SbOemKdk1" size="32" value="0x<32-byte-value-SbOemKdk1>"/>
   <fuse name="OdmInfo" size="4" value="0x<4-byte-value-OdmInfo>"/>
   <fuse name="OdmId" size="8" value="0x<8-byte-value-OdmId>"/>
   <fuse name="PscSecureBootKey" size="32" value="0x<32-byte-value-SBK>"/>
   <fuse name="OespSecureBootKey" size="32" value="0x<32-byte-value-SBK>"/>
   <fuse name="SbSecureBootKey" size="32" value="0x<32-byte-value-SBK>"/>
   <fuse name="ArmJtagDisable" size="4" value="0x1"/>
   <fuse name="BootSecurityInfo" size="4" value="0x<4-byte-value-SECURITY_INFO>"/>
   <fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>

Jetson Orin series merged template:

<genericfuse MagicId="0x45535546" version="1.0.0">
   <fuse name="OdmInfo" size="4" value="0x<4-byte-value-OdmInfo>"/>
   <fuse name="OdmId" size="8" value="0x<8-byte-value-OdmId>"/>
   <fuse name="Kdk0" size="32" value="0x<32-byte-value-Kdk0>"/>
   <fuse name="PublicKeyhash" size="64" value="0x<64-byte-value-PubKeyHash>"/>
   <fuse name="PkcPubKeyHash1" size="64" value="0x<64-byte_value-PubKeyHash1>"/>
   <fuse name="PkcPubKeyHash2" size="64" value="0x<64-byte_value-PubKeyHash2>"/>
   <fuse name="OptInEnable" size="4" value="0x1"/>
   <fuse name="SecureBootKey" size="32" value="0x<32-byte-value-SBK>"/>
   <fuse name="ArmJtagDisable" size="4" value="0x1"/>
   <fuse name="OemK1" size="32" value="0x<32-byte-value-key>"/>
   <fuse name="OemK2" size="32" value="0x<32-byte-value-key>"/>
   <fuse name="PscOdmStatic" size="4" value="0x00000060"/>
   <fuse name="BootSecurityInfo" size="4" value="0x<4-byte_value-SECURITY_INFO>"/>
   <fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>
cd ${BSP_TOP}/Linux_for_Tegra
cd l4t/tools/flashtools/fuseburn/

# Jetson Thor series:
sudo ./fskp_fuseburn.py -f ~/odm_fskp/odm_fuse_template.xml \
                        --multi-blob ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool/ftpm_kdk/kdk_db_0021-0000001000218000-2.csv \
                        --test \
                        -i <fskp_select> \
                        --key ~/odm_fskp/fskp_key.bin \
                        -g fuseblob/odm_out \
                        -c 0x26 \
                        --board-spec thor-agx-board-spec.txt \
                        -B ../../../../jetson-agx-thor-devkit.conf

# Jetson Orin series:
sudo ./fskp_fuseburn.py -f ~/odm_fskp/odm_fuse_template.xml \
                        --multi-blob ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool/ftpm_kdk/kdk_db_0021-0000001000218000-2.csv \
                        --test \
                        -i 63 \
                        --key-exp ~/odm_fskp/fskp_ak.bin ~/odm_fskp/fskp_ek.bin \
                        --fskpcfg ~/odm_fskp/fskp_conf.txt \
                        -g fuseblob/odm_out \
                        -c 0x23 \
                        --board-spec orin-agx-board-spec.txt \
                        -B ../../../../jetson-agx-orin-devkit.conf

tar cf fuseblob_odm_out_0021-0000001000218000-2.tar fuseblob/

Note

The parameters in the preceding example are for reference only. Replace them with your FSKP keys and board-specific values.

The --test option generates fuseblobs for dry run only. To burn fuses, replace --test with --burnfuse. Follow the Fuse Burning procedure.

Flash Image Generation#

The following are example commands for various target configurations. Note the following about flash image generation:

  • The UEFI image generated by the LBC command (with --odm-image-gen) is the final UEFI image. The --uefi-keys option must be provided so that all UEFI keys are built in.

  • The UEFI image is encrypted and signed by the SBK key (-v) and PKC key (-u).

  • In the Jetson Orin series UPI commands, the --uefi-enc key is the sym_t234.key used in EKB generation.

  • To enable UEFI secure boot, run the following commands:

    cd ${BSP_TOP}/Linux_for_Tegra
    sudo ./tools/gen_uefi_keys_dts.sh ~/oem_keys/uefi_keys/uefi_keys.conf
    

AGX Thor with UFS:

cd ${BSP_TOP}/Linux_for_Tegra

# QSPI image (LBC):
sudo rm -rf tools/kernel_flash/images/
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
                      --no-flash \
                      -u ~/odm_keys/t264_pkc_key_list.xml \
                      -v ~/odm_keys/sbk-32.key \
                      --uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
                      jetson-agx-thor-devkit \
                      internal

# UPI:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
                      --no-flash \
                      --uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
                      jetson-agx-thor-devkit \
                      internal

AGX Orin with eMMC:

cd ${BSP_TOP}/Linux_for_Tegra

# QSPI image (LBC):
sudo rm -rf tools/kernel_flash/images/
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
                      --no-flash \
                      -u ~/odm_keys/rsa3k.pem \
                      -v ~/odm_keys/sbk-32.key \
                      --uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
                      -p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
                      jetson-agx-orin-devkit \
                      internal

# UPI:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
                      --no-flash \
                      --uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
                      --uefi-enc ~/oem_keys/uefi_keys/uefi_enc.key \
                      jetson-agx-orin-devkit \
                      internal

The output files from the preceding commands are lbc_odm.tar.gz and upi_oem.tar.gz.

AGX Orin with NVMe:

cd ${BSP_TOP}/Linux_for_Tegra

# QSPI image (LBC):
sudo rm -rf tools/kernel_flash/images/
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
                      --no-flash \
                      -u ~/odm_keys/rsa3k.pem \
                      -v ~/odm_keys/sbk-32.key \
                      --uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
                      -p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
                      jetson-agx-orin-devkit \
                      internal

# UPI:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
                      --no-flash \
                      --external-device nvme0n1p1 \
                      --external-only \
                      -c ./tools/kernel_flash/flash_l4t_t234_nvme.xml \
                      --uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
                      --uefi-enc ~/oem_keys/uefi_keys/uefi_enc.key \
                      jetson-agx-orin-devkit \
                      nvme0n1p1

The output files from the preceding commands are lbc_odm.tar.gz and upi_oem.tar.gz.

Orin Nano with NVMe:

cd ${BSP_TOP}/Linux_for_Tegra

# QSPI image (LBC):
sudo rm -rf tools/kernel_flash/images/
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --odm-image-gen \
                      --no-flash \
                      -u ~/odm_keys/rsa3k.pem \
                      -v ~/odm_keys/sbk-32.key \
                      --uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
                      -p "-c bootloader/generic/cfg/flash_t234_qspi.xml" \
                      jetson-orin-nano-devkit \
                      internal

# UPI:
sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
./l4t_initrd_flash.sh --mass-storage-only \
                      --no-flash \
                      --external-device nvme0n1p1 \
                      --external-only \
                      -c ./tools/kernel_flash/flash_l4t_t234_nvme.xml \
                      --uefi-keys ~/oem_keys/uefi_keys/uefi_keys.conf \
                      --uefi-enc ~/oem_keys/uefi_keys/uefi_enc.key \
                      jetson-orin-nano-devkit \
                      nvme0n1p1

The output files from the preceding commands are lbc_odm.tar.gz and upi_oem.tar.gz.

Factory Tarball Generation#

cd ${BSP_TOP}/Linux_for_Tegra

sudo tar zvxf lbc_odm.tar.gz
sudo tar zvxf upi_oem.tar.gz

sudo mkdir -p tools/kernel_flash/images/internal/ekb_db
sudo cp ${BSP_TOP}/oem_out/signed/* tools/kernel_flash/images/internal/ekb_db

sudo tar zvcf factory.tar.gz \
               tools/kernel_flash/initrdflashparam.txt \
               tools/kernel_flash/initrdflashimgmap.txt \
               tools/kernel_flash/images/

Send factory.tar.gz to the factory. Follow the Flashing Devices procedure.