SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: LicenseRef-NvidiaProprietary

NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
property and proprietary rights in and to this material, related
documentation and any modifications thereto. Any use, reproduction,
disclosure or distribution of this material and related documentation
without an express license agreement from NVIDIA CORPORATION or
its affiliates is strictly prohibited.

# Overview of the Firmware TPM implementation on Jetson

__Applies to__ the Jetson AGX Orin, the Jetson Orin NX series, and the Jetson Orin Nano series.

Before you begin, please reference the TCG website for the TPM specification and knowledge.
- [TCG TPM2 Brief Overview](https://trustedcomputinggroup.org/wp-content/uploads/2019_TCG_TPM2_BriefOverview_DR02web.pdf)
- [TPM 2.0 Specification](https://trustedcomputinggroup.org/resource/tpm-library-specification/)
- [TCG Documentation Portal](https://trustedcomputinggroup.org/resources/?)

The Firmware TPM (__fTPM__) implementation is done by leveraging the official [TCG reference implementation of the TPM 2.0 specification](https://github.com/Microsoft/ms-tpm-20-ref). The reference implementation includes a sample fTPM Trusted Application (TA) designed to be executed with OP-TEE.

A fundamental difference exists between a discrete TPM (DTPM) and fTPM. A DTPM or a TPM chip has been provisioned by the TPM vendor or manufacturer with the Endorsement Key (EK) certificate, which is the TPM identification that can be used for TPM attestation. Without this certificate, the TPM is not useful, because trust can not be constructed between the TPM user and the services that rely on TPM.

To create a trustworthy fTPM entity on different devices, you need to provision it with a per-device unique ID, the EK certificate, and this needs to be done during the device manufacturing process.

The fTPM implementation on Jetson includes two sample applications:
- fTPM TA from [ms-tpm-20-ref](https://github.com/Microsoft/ms-tpm-20-ref).
- fTPM helper applications
    - The helper applications provide the functionalities for the provisioning process.

# The fTPM Provisioning

The fTPM provisioning refers to the process of configuring and initializing a firmware-based Trusted Platform Module (TPM) on a Jetson device.

fTPM provisioning involves several steps:
- Initialization: The fTPM TA is initialized by the Trusted OS (OP-TEE) during the early secure boot stage.
- Configuration: The fTPM is configured with the settings such as Endorsement Primary Seed (EPS), EK certificate, owner password, and other parameters.
- Key generation: The fTPM generates a unique EK and storage root key (SRK), which are used to secure sensitive data.

The benefits of fTPM provisioning includes:
- Improved security: By securing sensitive data and ensuring the integrity of the device's boot process, fTPM provisioning helps prevent unauthorized access or manipulation.
- Compliance: fTPM provisioning can help the device complies with the requirements from the remote service providers, such as TPM attestation.
- Trust: By providing a secure foundation for devices, fTPM provisioning helps establish trust between devices, networks, and users.

The fTPM provisioning process we designed to make the Jetson device deal with the fTPM manufacturer Certificate Authority (CA) server. There are two kinds of provisioning methods: offline provisioning and online provisioning.

- Offline provisioning method
    - The offline provisioning method needs the fTPM manufacturer to pre-generate the EK certificates and encode it into the Encrypted Keyblob (**EKB**). This process needs to be completed during the device manufacturing process. When the device boots up, You can use a provisioning tool to query the EK certificates from EKB and store them in the fTPM non-volatile (NV) memory.
- Online provisioning method (Not available yet in the implementation)
    - The online provisioning method depends on a three-way handshake protocol between the CA and the device to validate the device identity, sign, and issue the EK certificate.

## Preparation before the fTPM provisioning process (Offline method)

### The Prerequisites of the fTPM Owner
- Here is a list of the tasks that the fTPM owner can complete:
    - This role is identical to the DTPM vendor or manufacturer and is the Original Design Manufacturer (ODM) of the fTPM provisioning support.
    - It should be a third-party service provider with expertise in fTPM production.
    - It should be able to set up a secure environment to work with the fTPM provisioning process.
    - The CA server held by the fTPM owner should be acknowledged by the popular cloud service provider with which devices are designed to be enrolled.
    - It should meet the requirements for preparing and generating materials for fusing the devices and keeping them secure.
    - It should support multiple Original Equipment Manufacturers (OEMs) which means that the fTPM owner should work with multiple OEMs and maintain and isolate the materials for different OEMs.
- The requirements of the material preparation and the generation work.
    - The per-device basis KDK0, Device_SN, Silicon_ID, and Silicon_ID public key generation.
        - This can be done by running the kdk_gen.py KDK Gen tool, where:
            - KDK0 is a secret per-device unique 256-bit random value.
            - SN is a per-device unique serial number.
            - OEM_ID is an OEM_ID number for different OEMs.
            - Device_SN is the concatenation of OEM_ID and SN where OEM_ID and SN are in the big-endian form.
        - The public key of Silicon_ID
            - This public key can be used for the fTPM provisioning tool to verify the materials signed by the Silicon_ID private key.
            - The public key is generated by Silicon_ID where:
                - Silicon_ID is KDF(key=KDK0, Device_SN).
                - The asymmetric key pair of SiliconID is ECC_P256_key_gen(seed=Silicon_ID).
                - The private key should be dropped immediately.
        - ==Note==: The KDK0 should be discarded after the corresponding fuseblob, the EK, and the EK-associated certificate are generated.
    - The per-device basis fuseblob generation.
        - This task can be completed by running the FSKP tool (fskp_fuseburn.py).
        - Here is a list of the properties that are related to the fTPM:
            - Fuse_ODM_INFO is ${OEM_ID}.
            - Fuse_ODMID0/1 is ${SN}.
            - Fuse_KDK0 is ${KDK0}.
        - The KDK0 should be discarded after a corresponding fuseblob and EK are generated. The purpose of not keeping it in storage is to avoid potential leaks.
    - The per-device basis fTPM EKB generation.
        - This task can be completed by running the odm_ekb_gen.py ODM EKB Gen tool.
        - Here are the fTPM EKB properties:
            - EPS Seed is 256-bit random number that will be used for the EPS deriving process.
            - The EK Certificate Signing Request (CSR).
                - The EK CSR will be generated and sent to the CA for signing.
            - EK Certificate
                - The EK Certificate will be issued by the CA after signing the CSR.
        - EKB is per-device EKB image with the following fTPM properties:
            - Device_SN, EPS Seed
            - EK certificates (RSA and EC)
    - The database generated by the fTPM manufacturer:
        - ${Device_SN}
        - $(Silicon_ID_PUB_KEY)
        - ${EPS_Seed}
        - ${EK_Cert}

```mermaid
---
title: The preparation work of the fTPM owner.
---
graph TB
	subgraph Materials [Generate the materials]
		direction LR
		dev_sn1[[Device_SN]] ~~~ oem_kdk1[[KDK0]] ~~~ pub_key1[[Silicon_ID_PUB_KEY]] ~~~ eps_seed1[[EPS_Seed]]
		dev_sn2[[00100001]] ~~~ oem_kdk2[[0x11aa22...]] ~~~ pub_key2[[0x1133...]] ~~~ eps_seed2[[0x5a6b...]]
		dev_sn3[[00100002]] ~~~ oem_kdk3[[0x22bb33...]] ~~~ pub_key3[[0x2233...]] ~~~ eps_seed3[[0x5a6b...]]
		dev_sn4[[00200001]] ~~~ oem_kdk4[[0xaa11bb...]] ~~~ pub_key4[[0x55eeff...]] ~~~ eps_seed4[[0x5a6b...]]
		dev_sn5[[00200002]] ~~~ oem_kdk5[[0x99bbcc...]] ~~~ pub_key5[[0x33bbdd...]] ~~~ eps_seed5[[0x5a6b...]]
		dev_sn6[[00300001]] ~~~ oem_kdk6[[0x33ddee...]] ~~~ pub_key6[[0x9e2b8c...]] ~~~ eps_seed6[[0x5a6b...]]
		dev_sn7[[00300002]] ~~~ oem_kdk7[[0x22bbcc...]] ~~~ pub_key7[[0x1dfd32...]] ~~~ eps_seed7[[0x5a6b...]]
	end

	DataBase[("${Device_SN}-${Silicon_ID_PUB_KEY}-${EPS_Seed}<br>EKCert-${Device_SN}<br>Fuseblob-${Device_SN}")]
	FskpTool{{FSKP Tool}}
	Fuseblobs[["Fuseblob-${Device_SN}"]]
	fTPMGenEKCSRTool{{fTPM Gen EK CSR Tool}}
	fTPMManuCA{{fTPM Manufacturer CA}}
	EKCSRs[["EKCSR-${Device_SN}-${EPS_Seed}"]]
	EKCerts[["EKCert-${Device_SN}"]]

	Materials --"Store in the database"--> DataBase

	Materials --> fTPMGenEKCSRTool
	fTPMGenEKCSRTool --> EKCSRs
	EKCSRs --"Send to the CA"--> fTPMManuCA
	fTPMManuCA --> EKCerts
	EKCerts --> DataBase

	Materials --> FskpTool
	FskpTool --"Generate per-device fuseblobs"--> Fuseblobs
	Fuseblobs --> DataBase
```

### Signing the EK CSRs
After the per-device EK CSR has been generated, the EK CSR needs to be signed by the fTPM manufacturer CA, and the CA returns the EK certificate. The EK should support two algorithms RSA and EC. During the preparation period, the fTPM owner should prepare two EK CSRs for the two algorithms and store the two certificates in the storage.

This file name format is "ek_\${CERT_TYPE}\_\${KEY_TYPE}\-\${OEM_ID}\-\${SN}.der".
- CERT_TYPE: csr or cert.
- KEY_TYPE: rsa or ec.
- OEM_ID and SN: same as the definition above.
```mermaid
graph LR
	fTPMServer(fTPM Manufacturer Server)
	fTPMCA(fTPM Manufacturer CA)

	EKCSR[[EK CSR<br>ek_csr_rsa-0102-1122334455667788.der<br>ek_csr_ec-0102-1122334455667788.der]]
	EKCert[[EK Certificate<br>ek_cert_rsa-0102-1122334455667788.der<br>ek_cert_ec-01020-1122334455667788.der]]

	fTPMServer --> fTPMCA
	fTPMServer ~~~ EKCSR
	EKCSR ~~~ fTPMCA

	fTPMCA --> fTPMServer
	fTPMServer ~~~ EKCert
	EKCert ~~~ fTPMCA
```

### Generating per-device EKB
The offline provision method aims to create a per-device EKB image with the device's fTPM properties:

- Device_SN, EPS Seed
- Two certificates (RSA and EC).

The ODM EKB Gen Tool encodes the fTPM properties into the EKB image and the output of the per-device EKB images is saved as an unencrypted binary file with the OEM_ID and SN as the filename index.

```mermaid
graph LR
	subgraph HW [KDK DB]
		direction LR
		kdk_db(kdk_db)
	end
	subgraph TOOL [Tool]
		GenEkbTool(ODM EKB Gen Tool)
	end
	subgraph EKB [EKB]
		direction LR
		DevSN(Device_SN)
		EPSSeed(EPS Seed)
		EKCertRsa(EK Cert RSA)
		EKCertEc(EK Cert EC)
	end
	subgraph EKB_list [fTPM_EKB list]
		direction LR
		ekb1[["ftpm_ekb_${OEM_ID}-${SN}.bin"]]
		ekb2[[ftpm_ekb_0102-1122334455667788.bin]]
		ekb3[[ftpm_ekb_0102-1122334455667789.bin]]
		ekb4[[ftpm_ekb_0102-1122334455667790.bin]]
	end

	HW --> TOOL --> EKB ~~~ EKB_list
```

> [!Note] Here is some important information about the EKB contents:
> - EKB also contains device manufacturer (OEM) defined keys such as the UEFI payloads encryption key, the UEFI variable authentication key, and the disk encryption key (called user keys in general). After the per-device-based fTPM property EKB, which is called fTPM_EKB, is generated in plain binary form by the fTPM manufacturer (by running odm_ekb_gen.py), the ftpm_EKB is sent to the device manufacturer so that the user keys can be added by running oem_ekb_gen.py by the device manufacturer.
> - The final result EKB that contains the fTPM_EKB, and user keys is encrypted and signed by K1-derived keys. K1 is owned by the device manufacturer and is burned to OEM_K1 fuse through OEM_fuseblob.
> - [The fTPM Production Flow](#the-ftpm-production-flow) section provides more information about fuseblob generation and burning by using the FSKP tool.

## The Key Derivation Process to support fTPM

This section provides a list of the prerequisites to use Jetson Secure Bootloaders. The fTPM provisioning process relies on the secure boot support on the Jetson platform. The secure boot function should construct Hardware Root of Trust (HROT) and Root of Trust for Measurement (RTM) because they are needed for the device attestation in the fTPM provisioning process.

The Trusted Computing Base (TCB) of the Jetson platform consists of the security-relevant components that have been loaded at the stage of the secure boot chain.
```mermaid
graph LR
	subgraph HW [Hardware]
		fuse(FUSE)
	end
	subgraph ROM [ROM]
		direction LR
		PSCROM(PSCROM)
		BootROM(BootROM)
		PSCBL(PSCBL)
		MB1(MB1)
	end
	subgraph FMC [FMC]
		MB2(MB2)
	end
	subgraph FW [Firmware]
		TOS(TOS</br>fTPM TA)
	end

	HW ~~~ ROM ~~~ FMC ~~~ FW
```
- The role in the secure boot chain
    - The hardware and ROM are the HROT of secure boot and also the ROT and RTM of the secure boot on the device.
    - FUSE
        - KDK0: A secure fuse slot on Jetson Orin. It will be used to derive the Silicon ID.
    - ROM
        - This includes the ROM and ROM extension code.
            - PSCROM and BootROM are the static and fixed codes stored in the ROM.
            - PSCBL and MB1 are the ROM extension code to execute the bottom half of the initial boot.
    - First Mutable Code (FMC)
        - MB2 is the boot loader that handles the construction of the components for fTPM provisioning support.
    - Firmware
        - Trusted OS (TOS) is the trusted firmware that includes ARM Trusted Firmware (ATF), OP-TEE OS with the fTPM, and fTPM helper TAs.

### Silicon ID Provisioning Flow in Secure Boot

The Silicon ID Seed provisioning flow along the TCBs in the secure boot chain. There are three layers:
- Hardware and ROM (PSCROM, BootROM, PSCBL, and MB1).
- FMC (MB2).
- Firmware (TOS).

The purpose of this flow is to generate a Silicon ID and fTPM seed. The security mechanism of the key generation flow uses the TZ-SE, which is the hardware Security Engine in the TrustZone, but the keyslot is not accessible by the CPU. The fTPM Seed is passed to OP-TEE using encrypted TZ memory and is used to derive the EPS during the fTPM provisioning process.

The Silicon ID generation flow:
- In the PSCBL layer:
    - Silicon_ID is the KDF(key=KDK0, Device_SN)
        - The Device_SN is a unique number that comprises an OEM_ID and a unique SN.
- In the MB2 layer:
    - fTPM Seed
        - The fTPM Seed is KDF(key=Silicon_ID, constant str1).
    - Silicon ID Key Seed
        - The Silicon ID Key Seed is KDF(key=Silicon_ID, constant str2).
    - The Silicon_ID key pair
        - The Silicon_ID<sub>key pair</sub> is f(ECC, NIST P256)<sub>KEYGEN</sub>(seed=Silicon_ID_Key_Seed).

### EPS Derivation Flow in the OP-TEE OS Layer

The Endorsement Primary Seed (EPS) is the ROT of the fTPM entity and is tied with the EK, which can be used to attest the TPM identity and the keys generated by the TPM. The EPS is derived from the TOS layer of TCB in the Jetson device. The fTPM helper PTA in the OP-TEE OS implements the EPS derivation process by using the fTPM Seed from the bootloader and the fTPM property EPS_Seed in the EKB.

The EPS will be injected into fTPM TA during the TA's first start-up time and stored in NV memory.

- In the fTPM helper PTA layer:
    - Here is the EPS derivation flow:
        1. Device_SN is fuse_read(ODMID, ODM_INFO).
        2. fTPM Root Seed
            - The fTPM Root Seed is KDF(key=fTPM_Seed, constant str).
        3. EPS
            - The EPS is KDF(key=fTPM_Root_Seed, info=Device_SN, salt=EPS_Seed).


> [!Note]
> - The EPS Seed is a random number that is generated by odm_ekb_gen.py and stored in EKB.

## The fTPM Production Flow

The fTPM production flow is the design of provisioning an EK certificate by the fTPM manufacturer who owns the CA and fTPM manufacturing server and is qualified to issue the certificate. This should be done in a secure environment during the fTPM manufacturing process.

Here are the requirements of the fTPM Manufacturer Server:
- This server should be a secure environment and have Hardware Secure Module (HSM) support.
- This server is responsible for generating the materials for fTPM production.

Here are the requirements of the fTPM Manufacturer CA:
- A CA ==MUST== verify fTPM residency of a key before signing a certificate. This can be done by verifying the signature of the EK CSR.
- The CA ==SHOULD== support a standard certificate transport protocol that provides protection from replay attacks and provides confidentiality and integrity.

~~~mermaid
sequenceDiagram
	participant odm as fTPM Design Manufacturer<br>(ODM)<br>fTPM Manufacturer Server
	participant oem as OEM Manufacturer<br>OEM Manufacturer Server
	participant oem_factory as OEM Manufacturer Factory

	critical
		Note over odm: ODM Fuseblob Generation
		Note over odm: 1. Generating keys and Materials owned by ODM<br>(ODM_KDK0, PKC, SBK, OEM_ID, and SN, etc.)
		Note over odm: 2. Generating ODM fuseblob<br>The fuseblob is encrypted and signed by ODM owned fskp key (or fskp expansion key).
		Note over odm: 3. Storing fuseblobs in the database<br>(ODM_fuseblob_db)
	end

	critical
		Note over oem: OEM Fustblob Generation
		Note over oem: 1. Generating OEM K1 and K2 keys
		Note over oem: 2. Generating OEM fuseblob
	end

	critical
		Note over odm: ODM EKB Generation Phase 1
		Note over odm: 1. Generating EPS Seed and EK Certificates
		Note over odm: 2. Generating EKB_ftpm_db
	end

	odm->>oem: Send EKB_ftpm_db to OEM

	critical
		Note over oem: OEM EKB Generation
		Note over oem: 1. Generating the user-defined keys in EKB
		Note over oem: 2. Merging the EKB_ftpm_db from ODM and<br> the user-defined keys.
		Note over oem: 3. Encrypting the EKB image by OEM K1.
		Note over oem: 4. Output the EKB_final_db
	end

	oem->>odm: Send EKB_final_db to ODM

	critical
		Note over odm: ODM EKB Generation Phase 2
		Note over odm: 1. Storing the EKB_final_db
	end

	critical
		Note over odm: ODM packages Generation
		Note over odm: 1. Generating the QSPI Image
		Note over odm: 2. Signing and encrypting the EKB_final_db by using PKC and SBK keys and output EKB_db.signed
		Note over odm: 3. Generating per OEM basis package<br>ODM_fuseblob_db, QSPI image, and EKB_db.signed
	end

	odm->>oem: Send the above packages to OEM

	critical
		Note over oem: OEM packages Generation
		Note over oem: 1. Generating complete QSPI image package
		Note over oem: 2. Generating mass storage (eMMC or NVMe ) image package
	end

	oem->>oem_factory: Deliver the packages to the factory

	Note over oem_factory: Running FSKP tool<br>Burn the OEM owned fuse: OEM_fuseblob

	Note over oem_factory: Running FSKP tool<br>Burn the per device basis fuse: ODM_fuseblob_db

	Note over oem_factory: Running flashing tool<br>1. flashing images to QSPI and mass storage.<br>2. flashing the per device specific EKB from EKB_db.signed.
~~~

The diagram shows the fTPM production flow.

Here are the roles in the diagram:
- The fTPM Design Manufacturer (aka ODM):
    - Is the owner of the fTPM manufacturer server and the fTPM manufacturer CA.
    - Delivers the fTPM packages to OEM.
- The OEM Manufacturer:
    - Owns the OEM defined fuse keys.
    - Owns the user-defined keys in EKB.
    - Owns the OS bootloader e.g. UEFI and UEFI payloads such as L4TLauncher.
    - Owns Platform Vendor (PV) keys that encrypt and sign UEFI image.
    - Generates the packages for production.
- The OEM Manufacturer Factory assembles, fuses, and flashes the devices.

Here is the fTPM production flow:
- ODM Fuseblob Generation
    - The keys and materials, such as ODM_KDK0, SN, PKC, and SBK keys, which are owned by ODM are generated.
        - The SECURITY_INFO and SECURITY_MODE fuses are owned by ODM as well. Please reference [Generating the Corresponding fuseblobs](#generating-the-corresponding-fuseblobs) section.
    - The ODM fuseblob is generated.
        - Run the "KDK Gen Tool" (kdk_gen.py) to generate **ODM_KDK_db**. (db means database.)
        - Run the fuse burn tool (fskp_fuseburn.py) with ODM_KDK_db and PKC, SBK keys as input.
            - The ODM fskp key will be used as the signing and encryption key to generate the ODM signed **ODM_fuseblob_db**.
    - The fuseblobs are stored in the database.
        - The fuseblob database (ODM_fuseblob_db) is a list of individual files, such as odm_fuseblob-${Device_SN}.bin.

- OEM Fuseblob Generation
    - The fuse keys owned by OEM such as OEM_K1 and OEM_K2.
        - The PSC_ODM_STATIC fuse is owned by OEM as well.
        - The OEM fuseblob is generated.
            - Run the fuse burn tool (fskp_fuseburn.py) with the OEM fuse configuration XML file.
                - The field such as OEM_K1, OEM_K2, PSC_ODM_STATIC, etc, should be filled in the configuration file.
                - The OEM fskp key will be used as the signing and encryption key to generate the OEM signed **OEM_fuseblob**.

- The ODM EKB Generation Phase 1
    - The EPS Seed and EK Certificates are generated.
        - This will use the fTPM Gen CSR tool to generate the fTPM EK CSRs.
        - The EK CSRs should be delivered to the CA and signed by the CA.
        - The CA returns the EK certificates.
    - The **EKB_ftpm_db** is generated.
        - Run the ODM EKB Gen tool (odm_ekb_gen.py) with the ODM_KDK_db as the input.
    - Send the **EKB_ftpm_db** to OEM.

- OEM EKB Generation
    - The user-defined keys that will be encoded into EKB are generated.
    - Run the OEM EKB Gen tool (oem_ekb_gen.py) to merge the EKB_ftpm_db and user-defined keys.
        - Use the OEM_K1 key as the encryption key to generate OEM encrypted **EKB_final_db**.
    - Send the **EKB_final_db** to ODM.

- ODM EKB Generation Phase 2
    - Receiving and storing the EKB_final_db.

- ODM Packages Generation
    - Generating the QSPI image, for example, you can run the flashing tool with PKC and SBK keys to generate, sign, and encrypt images for all partitions on QSPI.
        - ==Note:== The generated UEFI and EKB are used only as placeholders. The final UEFI is generated and signed by OEM, and the final EKB is loaded from EKB_final_db.
    - Sign and encrypt the EKB_final_db by using PKC and SBK keys and output EKB_db.signed.
    - Generate the per-OEM packages.
        - ODM_fuseblob_db, QSPI image, and EKB_db.signed

         > [!Note] Here is some important information:
         > - ODM_fuseblob_db contains a list of encrypted and signed fuseblobs including odm_fuseblob-<OEM_ID>-<SN>, odm_fuseblob-<OEM_ID>-<SN+1>, and so on.
         > - EKB_db.signed contains a list of PKC and SBK signed and encrypted EKB such as ekb-<OEM_ID>-<SN>.signed, ekb-<OEM_ID>-<SN+1>.signed, and so on.
         > - It is a one-to-one mapping between odm_fuseblob-<OEM_ID>-<SN> and ekb-<OEM_ID>-<SN>, and the flashing tool ensures ekb-<OEM_ID>-<SN> is only flashed to the device with fuseblob-<OEM_ID>-<SN> burned.
    - Send the packages to OEM.

- OEM Packages Generation
    - Generate the complete QSPI image package.
    - Generate the mass storage (eMMC or NVMe) image package.
    - Deliver the package to the factory.

- OEM Factory
    - Burn the OEM fuseblob.
    - Burn the per-device basis ODM fuseblob.
    - Flash the device.
        - Flash the QSPI and the mass storage.
        - Flash the per-device basis EKB image.

### The fTPM Production Flow when ODM and OEM are in the same entity

The fTPM production flow involves collaboration between ODMs and OEMs to create and generate per-device basis fuse blobs and images. However, there is a case where the same entity is capable of playing both ODM and OEM roles, In other words, it can handle the fTPM functional design, EK certificate signing, device image signing, and device manufacturing. In such cases, the UEFI PV key feature can be skipped, simplifying the fTPM production flow. The same PKC key used to sign low-level firmwares is also used to sign the UEFI image. For detailed flow and command samples, please refer to Appendix B.

## The Software Architecture to Support fTPM Provisioning
~~~mermaid
graph LR
	subgraph optee [OP-TEE]
		direction TB
		subgraph sel0 [S-EL0]
			direction LR
			ftpm_helper_ta(fTPM Helper TA)
			ftpm_ta(fTPM TA)
			ftpm_helper_ta ~~~ ftpm_ta
		end
		subgraph sel1 [S-EL1]
			ftpm_helper_pta(fTPM Helper PTA)
		end
	end
	subgraph el0 [EL0]
		direction LR
		ftpm_prov_script(fTPM provisioning script)
		ftpm_helper_ca(fTPM Helper CA)
		ftpm_prov_script ~~~ ftpm_helper_ca
	end

	el0 ~~~ sel0
~~~

The SW Components
- fTPM provisioning script
    - This sample script handles the fTPM provisioning process on the Jetson device.
    - The provisioning process on the device:
        - Querying the EK certificates from the EKB.
        - Storing the EK certificate to the fTPM NV memory.
        - Taking ownership of the fTPM.
        - Creating EK accordingly with the default EK handles.
    - The provisioning process only needs to be activated once.
    - This script should be bundled with the fTPM support package provided by the fTPM design manufacturer (ODM).
- The fTPM helper TA/CA and PTA
    - They are the applications designed for fTPM provisioning support.
    - fTPM helper CA
        - The fTPM helper CA provides the command line interface (CLI) for the script to query the EK certificates from the EKB.
    - fTPM helper TA
        - The fTPM helper TA provides the interfaces to support the fTPM helper CA.
        - It helps to query SN and EK certificates.
    - fTPM helper PTA
        - The fTPM helper PTA helps to gather the fTPM properties from MB2 and EKB.
        - The fTPM helper PTA retrieves Device SN from the fuse and makes sure it matches with the Device SN in EKB.
        - The EPS derivation function is executed by the PTA.
- fTPM TA
    - The fTPM TA should support the TPM2 functionalities defined by TCG.
    - The TPM2 function in the fTPM TA should be a black box. It should NOT provide any interface other than TCG defined to access the TPM internal functions.
    - The fTPM TA gets the EPS from the fTPM helper PTA during the first startup time and stores the EPS in the NV memory.

## The fTPM Provisioning and Activation Flow

~~~mermaid
sequenceDiagram
	participant pscbl as PSCBL
	participant mb2 as MB2
	participant fhpta as fTPM-helper PTA
	participant fta as fTPM TA
	participant fhta as fTPM-helper TA
	participant fhca as fTPM-helper CA
	participant usrspace as User Space

	box Secure Bootloaders
		participant pscbl
		participant mb2
	end

	box Secure World
		participant fhpta
		participant fhta
		participant fta
	end

	box Normal World
		participant fhca
		participant usrspace
	end

	Note left of pscbl: Device Power On

	critical
		Note over pscbl: Silicon_ID Derivation
		Note over pscbl: 1. Silicon_ID = KDF(KDK0, Device_SN)
	end

	critical
		Note over mb2: fTPM_Seed Derivation
		Note over mb2: 1. fTPM_Seed = KDF(Silicon_ID, const str1)
		Note over mb2: 2. Generating the Silicon_ID key pair
	end

	critical
		Note over mb2: fTPM MeasuredBoot Support
		Note over mb2: 1. Packaging the MB2 and TOS event log<br>into tpm_event_log buffer.
	end

	mb2->>fhpta: Delivering the OP-TEE DTB
	Note over mb2,fhpta: The OP-TEE DTB includes:<br>1. fTPM_Seed<br>2. The tpm_event_log buffer

	Note left of fhpta: OP-TEE OS Boots Up

	critical
		Note over fhpta: Launching the fTPM TA
		fhpta->>fta: Launching the fTPM TA
	end

	critical
		Note over fta: fTPM Measured Boot Support
		Note over fta: 1. Parsing the tpm_event_log buffer<br>2. Updating the MB2 and TOS event log into PCR0<br>via tpm2_pcrextend command
	end

	critical
		Note over fhpta: Gathering the fTPM Data from OP-TEE DTB and EKB
		Note over fhpta: The fTPM content in DTB:<br>1. fTPM_Seed
		Note over fhpta: The fTPM content in EKB:<br>1. Device_SN<br>2. EPS Seed<br>3. EK Certificates
	end

	Note over usrspace: Kernel Boots Up To User Space
	Note over usrspace: Starting up the fTPM TA<br>Inserting the fTPM Driver Module<br>(Running "modprobe tpm_ftpm_tee")

	usrspace->>fta: Initializing the fTPM TA

	critical
		Note over fta: Initializing fTPM TA
		Note over fta: Provisioning EPS<br>(Only needs to be done at 1st startup time for initial provisioning)<br>1. Invoking the EPS derivation function
		fta->>fhpta: Invoking the EPS derivation function
		Note over fhpta: Processing the EPS derivation function<br>1. Device_SN = fuse_read(ODMID, ODMINFO)<br>2. fTPM Root Seed = KDF(key=fTPM_Seed, const str)<br>3. EPS = KDF(key=fTPM_Root_Seed, info=Device_SN, salt=EPS_Seed)
		fhpta->>fta: Returning EPS
		Note over fta: 2. Storing EPS in fTPM NV memory
	end

	critical
		Note over usrspace: fTPM EK Provisioning and Activation<br>(Running ftpm_device_provisioning.sh)
		Note over usrspace: 1. Querying EK Certificates

		usrspace ->>fhca: Querying EK Certificates
		fhca->>fhta: Querying EK Certificates
		fhta->>fhpta: Querying EK Certificates
		fhpta->>fhta: Returning EK Certificates
		fhta->>fhca: Returning EK Certificates
		fhca->>usrspace: Returning EK Certificates

		Note over usrspace: 2. Taking the ownership of the fTPM<br>(Setting up the authentication value for the fTPM owner)

		usrspace->>fta: Sending tpm2_changeauth command

		Note over usrspace: 3. Creating EK

		usrspace->>fta: Sending tpm2_createek command

		Note over usrspace: 4. Validating the EK public keys

		Note over usrspace: 5. Writing the EK certificates into fTPM NV memory

		usrspace->>fta: Sending tpm2_nvwrite command

		Note over usrspace: fTPM has been provisioned and activated.
	end
~~~

# Cheat Sheet

## Jetson BSP Installation
> [!Note] Jetson BSP Installation
> 1. Download the latest BSP from [Jetson Linux Archive | NVIDIA Developer](https://developer.nvidia.com/embedded/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 toolchain (Optional): aarch64--glibc--stable-${rel_date}.tar.gz
> 2. Download the FSKP package.
>    - This is a partner release package. Please download it separately.
> 3. Create a ${BSP_TOP} folder.
>    ~~~
>    mkdir ${BSP_TOP}
>    cd ${BSP_TOP}
>    ~~~
> 4. Install the BSP.
>    a. Untar the BSP package.
>       ~~~
>       tar jvxf ~/Downloads/jetson_linux_${rel_ver}_aarch64.tbz2
>       ~~~
>    b. Untar the BSP source package.
>       ~~~
>       tar jvxf ~/Downloads/public_sources.tbz2
>       ~~~
>    c. Untar the sample RootFS.
>       ~~~
>       cd Linux_for_Tegra/rootfs
>       sudo tar jvxpf ~/Downloads/tegra_linux_sample-root-filesystem_${rel_ver}_aarch64.tbz2
>       ~~~
>    d. Apply the BSP packages.
>       ~~~
>       cd ${BSP_TOP}/Linux_for_Tegra
>       sudo ./apply_binaries.sh
>       ~~~
>    e. Install the FSKP packages.
>       ~~~
>       cd ${BSP_TOP}
>       tar jvxf ~/Downloads/fskp_partner_t234_${rel_ver}_aarch64.tbz2
>       ~~~
>    f. 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 ../
>       ~~~

## Set up the server to run the fTPM production scripts

- Install the required Python modules
~~~
sudo apt-get update
sudo apt-get install python3-pip
sudo apt-get remove python3-cryptography
sudo pip3 install asn1crypto
sudo pip3 install cryptography
sudo pip3 install ecdsa
sudo pip3 install numpy
sudo pip3 install oscrypto
sudo pip3 install pyaes
sudo pip3 install pycryptodome
sudo pip3 install pycryptodomex
~~~
- Fix the issue of asn1crypto manually ([Ref link](https://github.com/wbond/asn1crypto/issues/260))
	- Apply the fix below into "/usr/local/lib/python\<VERSION\>/dist-packages/asn1crypto/x509.py".
~~~
diff --git a/asn1crypto/x509.py b/asn1crypto/x509.py
index 8cfb2c78be27..761594e1a77d 100644
--- a/asn1crypto/x509.py
+++ b/asn1crypto/x509.py
@@ -686,12 +686,12 @@ class NameTypeAndValue(Sequence):
         'domain_component': DNSName,
         'name_distinguisher': DirectoryString,
         'organization_identifier': DirectoryString,
-        'tpm_manufacturer': UTF8String,
-        'tpm_model': UTF8String,
-        'tpm_version': UTF8String,
-        'platform_manufacturer': UTF8String,
-        'platform_model': UTF8String,
-        'platform_version': UTF8String,
+        'tpm_manufacturer': DirectoryString,
+        'tpm_model': DirectoryString,
+        'tpm_version': DirectoryString,
+        'platform_manufacturer': DirectoryString,
+        'platform_model': DirectoryString,
+        'platform_version': DirectoryString,
         'user_id': DirectoryString,
     }
~~~

## Supporting Production with fTPM
This section provides information about supporting production with fTPM.

Here are the prerequisites to Enabling fTPM with HW Silicon ID support:

- The KDK0 is the root key and must be burned with a 256-bit per-device unique secret value.
- The FUSE_BOOT_SECURITY_INFO must be burned with the following bits:

| Bits  | Description                                                                                         |
| ----- | --------------------------------------------------------------------------------------------------- |
| [2:0] | The authentication scheme field cannot be zero or over 101b.<br>It must be a valid PKC auth scheme. |
| [9]   | OEM key valid. This bit must be 1.                                                                  |
| [11]  | OEM key function. This bit must be 1 to enable the KDF of the OEM fuse key.                         |
| [13]  | OEM key function. This bit must be 1 to enable the KDF of the Silicon ID generation.                |

## KDK Database and Fuseblobs Generation

This section provides information about generating the KDK database (DB) and fuseblobs.

### Installing FSKP package

> [!Note]
> - Before you install the FSKP package:
>     - Assume the Jetson Linux BSP has been installed on the host machine
>     - Copy "fskp\_partner\_t234\_${rel_version}\_aarch64.tbz2" to ${BSP_TOP}
> ~~~
> # Install the FSKP package
> cd Linux_for_Tegra
> tar jvxf ${BSP_TOP}/fskp_partner_t234_${rel_version}_aarch64_tbz2
> cd l4t/tools/flashtools/fuseburn
> ~~~
> - Before running FSKP tool, install the following Python modules:
> ~~~
> sudo pip3 install ecdsa
> sudo pip3 install pyaes
> ~~~

### Generating kdk-db and the corresponding Silicon ID Public Keys

> [!Tip] To generate kdk-db and the corresponding Silicon ID Public Keys, run kdk_gen.py.
> ~~~
> # Command line interface of "kdk_gen.py".
> kdk_gen.py [-h] [--oem_id ${OEM_ID}] [--sn ${SN}] [--num_devices ${NUM_OF_DEVICES}]
> ~~~
>
> For example, to generate five kdk entries, run the following command.
> ~~~
> kdk_gen.py --oem_id 0x102 --sn 0x100000002 --num_devices 5
> ~~~

The above example will generate the following files:
- kdk_db_0102-0000000100000002-5.csv
- pubkey_db_0102-0000000100000002-5.csv

> [!Important] Here is some important information:
> - The KDK database will be used to generate fuseblobs.
> - Here is the content of the KDK database:
> ~~~
> cat kdk_db_0102-0000000100000002-5.csv
> 0102 0000000100000002 681a8d62bffb803e9b1068eb4e14af3e440b5a73d0e81d161d2817c24dac41a6
> 0102 0000000100000003 bfec6db0ddab599b2a34d01ab9e641e3b735d7ead9649579689ed535c73d2053
> 0102 0000000100000004 801575a8ecd51955f8efcc840af0b6f94a4a0d1e2745f97122d46873c83bd751
> 0102 0000000100000005 a8d49348cbbf34814b387c7189187a1c98fe03a254503e879232765651dcf0ee
> 0102 0000000100000006 a8e20c2e0309a6564d0c694c75fcce9b21b846c54dc3b6343a8c276bbbd2dca5
> # where the first two columns are oem-id and SN, and the last column is the randomly generated KDK value.
> ~~~

> [!Important] Here are the corresponding Silicon ID public keys:
> - The Silicon ID public key database can be used by the fTPM manufacturer CA to verify the device identity during the fTPM provisioning process.
> - The content of the Silicon ID public key database:
> ~~~
> cat pubkey_db_0102-0000000100000002-5.csv
> 0102 0000000100000002 435ec556a1e23e9a676b8471ff2b2c25ca262bbc7f581abd69de025198cb56d157882fc257c4f544b206796dadf8257d5c4638e70bf8a2f13d9a2b7a30b57c0a
> 0102 0000000100000003 c25aef9b683cc4a2579bf634e35d42ee40b5e31dccef5dad7a16ab8b860dc2ec25a20ed6391edd8aa871cfa999acf57af1f81f1226e74569b2f3fff549bc0809
> 0102 0000000100000004 b603792a8d28ed60d5b7a6ac604fb56dfbff475cd225665253c58670c19d9b27ea1e0ec21de9ade7d798d0ada7a180c9625b72c6ec5b6074a265e4e8a90b0f6a
> 0102 0000000100000005 7c43b790f97769f86839be2ceaedbbba244d727c311d28c21a8f8ff9a911173d53753cb207ee5f7e11298fed41d603731fe709255f17b2fdf0c7113f15d97e11
> 0102 0000000100000006 ddc67040dbf78afd4a05581dacef51cd2c8cf0554c532b5ffda54f398f98f1136170fcd39ade40ba04db3ece703e0414eb6060cb10de0d7373e2b3a6c1b251ac
> # where the first two columns are oem-id and SN, and the last column is the corresponding silicon-id public key.
> ~~~

### Generating the Corresponding fuseblobs

- Creating a template fuse configuration XML file called fuse_temp.xml like the following template file.
    - Note that ODM and OEM should follow the different template files.

#### The ODM fuse configuration template
~~~
<genericfuse MagicId="0x45535546" version="1.0.0">
    <fuse name="OdmInfo" size="4" value="0xFFFF"/>
    <fuse name="OdmId" size="8" value="0xFFFFFFFFFFFFFFFF"/>
    <fuse name="Kdk0" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/>
    <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="BootSecurityInfo" size="4" value="0x<4-byte_value-SECURITY_INFO>"/>
    <fuse name="SecurityMode" size="4" value="0x1"/>
</genericfuse>
~~~

#### The OEM fuse configuration template
~~~
<genericfuse MagicId="0x45535546" version="1.0.0">
    <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"/>
</genericfuse>
~~~

> [!Note]
> - The field "0x<...>" must be replaced with proper values before running fskp_fuseburn.py.

> [!Tip] To generate fuseblobs, run fskp_fuseburn.py.
> - For example, you can use the kdk db generated above to create five fuseblobs for AGX Orin.
> - Example for ODM:
> ~~~
> 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
> 
> # Create a tarball to send to the FACTORY.
> tar cf odm_out_0102-0000000100000002-5.tar odm_out/
> ~~~
> - Example for OEM:
> ~~~
> 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
>
> # Create a tarball to send to the FACTORY.
> tar cf oem_out.tar oem_out/
> ~~~
> Here are the command-line interface options:
> - -f <fuse_temp.xml>
>     - With the fuse template file, a list of corresponding fuse configurations will be generated and stored in the ftpm_kdk directory. In this example, a file list like the following is generated:
>         - fusexml_0102-0000000100000002.xml
>         - ...
>         - fusexml_0102-0000000100000006.xml
> - --test
>     - This indicates the generated fuseblob will only do a dry run. To actually burn the fuses, replace --test with -b.
> - --skipfskpkey
>     - This indicates that no fskp key is used, which means the fuseblobs generated are not encrypted. As a result, this option is used only for testing.
>     - To generate encrypted fuseblobs for securely burning fuses at the factory, replace --skipfskpkey with the following:
>         - -i \<key index\> --key \<fskpkey\> or with
>         - --key-exp <fskp_ak.bin> <fskp_ek.bin> --fskpcfg <fskp_conf.txt>
> - --multi-blob <kdk_db>
>     - Use the content from the pre-generated KDK DB to fill in the template fuse configuration XML file.
> - --g \<out_dir\>/\<fuse_blob_prefix\>
>     - This indicates the generated fuseblob folder. At the factory, the fuseblob are the only binaries needed to burn fuses.
>     - For example, you can specify "-g fuse/out". The fuseblob folders generated will be:
>         - fuse/out_<OEM_ID>-<SN>
>         - fuse/out_0102-0000000100000002
>         - ...
>         - fuse/out_0102-0000000100000006
> - -c \<chip ID\>
>     - This indicates the NVIDIA Tegra SoC chip ID.
> - --board-spec <board_spec>
>     - This defines the board spec such as BOARDID, SKU, and so on.
> - -B <board_conf>
>     - This specifies the low-level boot components, configs, and dtbs that are used for the board to burn the fuses.

## Generating ODM EKB

The following scripts are hooked by the ODM EKB Gen tool to generate the EK CSR and sign the EK certificate:
- "ftpm_manufacturer_gen_ek_csr.sh"
- "ftpm_manufacturer_ca_simulator.sh"
    - This is the script for signing the EK CSR. The fTPM manufacturer should customize the script to meet the requirements of the CA server.

> [!Tip] Run odm_ekb_gen.py
> ~~~
> # Command line interface of "odm_ekb_gen.py"
> odm_ekb_gen.py [-h] [--kdk_db ${KDK_DB}]
>
> # For example, to generate the EKB_ftpm_db, run the following command.
> odm_ekb_gen.py --kdk_db ftpm_kdk/kdk_db_0102-0000000100000002-5.csv
> ~~~

By default, the output of the ODM EKB DB will be stored in the "odm_out" folder. The ODM should package them and deliver them to the OEM.

## Generating OEM EKB
The OEM unpacks the ODM EKB DB and generates the EKB final DB. This will generate per-device basis EKS images that are authenticated and encrypted by the OEM K1 derivation keys.

> [!Tip] Run oem_ekb_gen.py
> - The OEM EKB Gen tool is a wrapper of the Gen EKB script (gen_ekb.py) from the hwkey-agent sample application.
>     - Move the Gen EKB script to the same folder as the OEM EKB Gen tool.
> ~~~
> oem_ekb_gen.py -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
> ~~~
> Here are the command-line interface options:
> - -oem_k1_key or -oem_k2_key <oem_k1.key>
>     - The root key of the EKB encryption and authentication key. By default, it uses k1.
> - -in_sym_key <sym_t234.key>
>     - The user-defined key for UEFI payloads (such as kernel, kernel-dtb) encryption.
> - -in_sym_key2 <sym2_t234.key>
>     - The user-defined key for disk encryption support.
> - -in_auth_key <auth_t234.key>
>     - The user-defined key for UEFI variable authentication.
> - -in_ftpm_odm_ekb \<IN_FTPM_ODM_EKB\>
>     - The path where the EKB_ftpm_db is located.

The generated per-device EKS images are stored in the "oem_out" folder. The OEM should package them and deliver them back to ODM.

## ODM Packages Generation

### Signing and Encrypting the EKB_final_db

> [!Tip] Run l4t_sign_image.sh
> - Please refer to the help option for detailed usage.
> - Unpack the EKB_final_db to the oem_out folder.
> - The encrypted and signed EKB\_final\_db are in oem\_out/signed folder. These encrypted/signed eks\_<OEM_ID>-<SN>.signed files are packaged to deliver to the OEM.
> ~~~
> cd Linux_for_Tegra
> ./l4t_sign_image.sh --chip <CHIP_ID> \
>                     --key <PKC_KEY> \
>                     --encrypt_key <SBK_KEY> \
>                     --mass-ekb oem_out \
>                     --type data \
>                     --split False
>
> # Packaging the encrypted/signed eks files.
> tar cf eks_<OEM_ID>-<SN>-<num_of_devices>.tar --directory=oem_out/signed/ .
> ~~~

### Generating the ODM QSPI Image

> [!Tip] Run l4t_initrd_flash.sh
> - Please refer to the README file (${BSP_TOP}/Linux_for_Tegra/tools/kernel_flash/README_initrd_flash.txt) and the help option for detailed usage.
> - Run l4t_initrd_flash.sh with --odm-image-gen, using PKC and SBK as the signing and encryption keys, and pv.crt as the UEFI signing public key to generate **lbc_odm.tar.gz (lower boot components)**.
> ~~~
> cd Linux_for_Tegra
> sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
> ./tools/kernel_flash/l4t_initrd_flash.sh \
>                         --odm-image-gen \
>                         --showlogs \
>                         --network usb0 \
>                         --no-flash \
>                         -u <PKC_KEY> \
>                         -v <SBK_KEY> \
>                         --pv-crt <PV_CERT> \
>                         <BOARD_NAME> \
>                         internal
> ~~~

## OEM Packages Generation

### Generating the OEM QSPI Image

> [!Tip] Generating UPI (User Partition Image)
> - Using UEFI keys to generate the UPI. This will output **upi_odm.tar.gz**.
> - Please refer to the README file (${BSP_TOP}/Linux_for_Tegra/tools/README_uefi_secureboot.txt) to generate the UEFI key configuration file.
> ~~~
> cd Linux_for_Tegra
> sudo ./tools/gen_uefi_default_keys_dts.sh <UEFI_KEYS_CONF_FILE>
> sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
> ./tools/kernel_flash/l4t_initrd_flash.sh \
>                         --mass-storage-only
>                         --showlogs \
>                         --network usb0 \
>                         --no-flash \
>                         --uefi-keys <UEFI_KEYS_CONF_FILE> \
>                         --uefi-enc <UEFI_PAYLOAD_ENCRYPTION_KEY> \
>                         <BOARD_NAME> \
>                         internal
> ~~~

> [!Tip] Generating UEFI Image
> - Using flash.sh with PV signing key (-u), PV encryption key (--pv-enc), and UEFI payload signing key (--uefi-keys) to generate the PV signed and encrypted UEFI image.
> ~~~
> cd Linux_for_Tegra
> sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>                         ./flash.sh \
>                         -k A_cpu-bootloader \
>                         --no-flash \
>                         -u <PV_SIGNING_PRIVATE_KEY> \
>                         --pv-enc <PV_ENCRYPTION_KEY> \
>                         --uefi-keys <UEFI_KEYS_CONF_FILE> \
>                         --uefi-enc <UEFI_PAYLOAD_ENCRYPTION_KEY> \
>                         <BOARD_NAME> \
>                         internal
> ~~~

### Generating the Factory Package

> [!Tip] Generating the factory tarball
> ~~~
> cd Linux_for_tegra
>
> # Untar the LBC tarball
> sudo tar xvzf lbc_odm.tar.gz
>
> # Untar the UPI tarball
> sudo tar xvzf upi_oem.tar.gz
>
> cd tools/kernel_flash/images/internal/
>
> # Copy the PV signed/encrypted UEFI image to the same folder
> sudo cp ${BSP_TOP}/Linux_for_Tegra/bootloader/uefi_jetson_with_dtb_aligned_blob_w_bin_sigheader_encrypt.bin.signed ./
>
> # Record the UEFI image file size
> ls -l uefi_jetson_with_dtb_aligned_blob_w_bin_sigheader_encrypt.bin.signed
>
> # Record the sha1sum
> sha1sum uefi_jetson_with_dtb_aligned_blob_w_bin_sigheader_encrypt.bin.signed
>
> # Edit the A/B_cpu-bootloader with PV signed/encrypted file size and sha1Sum
> sudo vim flash.idx
>
> # Untar EKB_final_db received from ODM (e.g., eks_0102-0000000100000002-5.tar)
> sudo mkdir ekb_db
> cd ekb_db
> sudo tar xf ${ODM}/eks_0102-0000000100000002-5.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/
>
> # Send the factory image to the factory
> ~~~

## Factory burning fuses and flashing devices flow

### Burning Fuseblob on a Device

> [!Tip]
> 1. Install L4T BSP and fskp package on the fuse burning host at the factory.
> 2. Untar the fuseblob.
> 3. Burn the OEM fuseblob.
> 4. Burn the ODM fuseblob.
>
> ~~~
> # Ex. Burn the OEM fuseblob:
> sudo ./fskp_fuseburn.py -P ./oem_out \
>                         -c 0x23 \
>                         --board-spec orin-agx-board-spec.txt \
>                         -B ../../../../jetson-agx-orin-devkit.conf
>
> # Ex. Burn the ODM fuseblob:
> sudo ./fskp_fuseburn.py -P ./odm_out \
>                         -c 0x23 \
>                         --board-spec orin-agx-board-spec.txt \
>                         -B ../../../../jetson-agx-orin-devkit.conf
>
> # Note:
> #   1. In this case, it is a dry run. No fuses are actually burned.
> #   2. Make sure the ODM fuseblob is burned after the OEM fuseblob is burned.
> #      This is because the ODM fuseblob contains Security Mode fuse bit. Once burned, no other fuses can be burned.
> ~~~

### Burning Fuseblobs to Multiple Devices

> [!Tip] Run fskp_massfuseburn.py and fskp_multiblobfuse.py respectively
> - The same steps as burning a single device.
>
> ~~~
> # Ex. Burn the OEM mass fuseblobs:
> # Untar the tarball, oem_out.tar, received from the OEM.
> sudo ./fskp_massfuseburn.py --skipconfirmation \
>                             --burnfuse \
>                             -P ./oem_out \
>                             -c 0x23 \
>                             --board-spec orin-agx-board-spec.txt \
>                             -B ../../../../jetson-agx-orin-devkit.conf
>
> # Ex. Burn the ODM mass fuseblobs:
> # Untar the tarball, odm_out_0102-0000000100000002-5.tar, received from the ODM.
> 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 the Devices

> [!Tip]
> 1. Install L4T BSP on the host to flash the devices.
> 2. Untar the factory package.
> 3. Flash the devices.
> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra
>
> # Untar the factory tarball
> sudo tar zvxf factory.tar.gz
> cp tools/kernel_flash/images/rcmboot/* bootloader/
>
> # Flash the devices
> sudo ./tools/kernel_flash/l4t_initrd_flash.sh \
>                         --flash-only \
>                         --showlogs \
>                         --network usb0 \
>                         --ekb-pair \
>                         <BOARD_NAME> \
>                         internal
> ~~~

## Running the fTPM Provisioning Script on the Jetson Device

- The fTPM provisioning script handles the process of provisioning the fTPM on the Jetson device. This involves querying EK certificates from the Encrypted Key Block (EKB), storing the EK certificate in the fTPM Non-Volatile (NV) memory, taking ownership of the fTPM, and creating EK accordingly with default EK handles.
- The fTPM helper TA/CA and PTA are applications designed to support fTPM provisioning. These components provide interfaces for querying SN and EK certificates.
- The fTPM TA supports TPM2 functionalities defined by the Trusted Computing Group (TCG). It receives EPS from the fTPM helper PTA during the first startup time and stores it in NV memory with the provisioning process.

### Optionally Rebuilding and Updating the TOS Image for fTPM Support

To customize the fTPM function on your Jetson device, you may need to rebuild and update the Trusted Operation System (TOS) image. Follow these steps to ensure a secure environment for your application.
> [!Tip]
> 1. Download the source package from [Jetson Linux Archive](https://developer.nvidia.com/embedded/jetson-linux-archive).
> 2. To rebuild the TOS image, refer to the README file in the OP-TEE or ATF source package. Additionally, manually add the "-t" option to enable fTPM feature.
>    ~~~
>    ./optee_src_build.sh -p t234 -t
>
>    # Update the TOS image after rebuild.
>    cp tos.img ${ODM_BSP_TOP}/Linux_for_Tegra/bootloader/tos-optee_t234.img
>    ~~~
> 3. Re-flash the device with the new TOS image.
> 4. Preparing the fTPM provisioning folder
>    - If you need to use fTPM, create a provisioning folder and copy the necessary file into it.
>      ~~~
>      # Create a "ftpm_prov" folder.
>      mkdir -p ftpm_prov
>
>      # Copy the provisioning script into the folder.
>      scp optee/samples/ftpm-helper/host/tool/ftpm_device_provision.sh ${REMOTE_DEVICE}:${DEST_PROV_DIR}/ftpm_prov
>      ~~~
>    - Note: The ${REMOTE_DEVICE} and {DEST_PROV_DIR} variables should be replaced with the actual device's IP address and provisioning directory, respectively.

### Running the fTPM Provisioning Script
> [!Note]
> 1. Load the fTPM driver module.
> ~~~
> sudo modprobe tpm_ftpm_tee
> ~~~
> 2. Provision and activate the fTPM by running the ftpm_device_provision.sh script.
>     - This script completes the following tasks:
>         - Queries the EK Certificates (RSA and EC) and stores them in the fTPM NV memory.
>         - Sets up the fTPM authorization.
>
>     ~~~
>     cd ftpm_prov
>     sudo ./ftpm_device_provision.sh -r ek_cert_rsa.der -e ek_cert_ec.der -p owner
>     ~~~
>
> - Here are the command-line interface options:
>     - -r \<file name of RSA EK certificate\>
>         - This is the must-have option to save the RSA EK certificate after querying from EKB.
>     - -e \<file name of EC EK certificate\>
>         - This is the must-have option to save the EC EK certificate after querying from EKB.
>     - -p \<authorization value of fTPM\>
>         - This is the must-have option of the fTPM authorization value.

## Verifying the fTPM EK

With the offline provision method, the fTPM EKs are pre-generated by the fTPM production scripts. The ODM EKB Gen Tool provides a __verify__ mode, which can help the fTPM manufacturer validate the fTPM EKs before shipping the EKB images to the OEM. The mode is completed by comparing the fTPM EK public keys generated by fTPM production script with the EK public keys generated by the fTPM TA.

Before you verify the fTPM EK, ensure that you have the following:
- A dedicated Jetson device.
    - To verify the fTPM EK, you need to set up a Jetson device with custom fTPM helper TA and the CA.
- A secure environment.
    - The intermediate data for fTPM EK verification should be kept in a secure environment, and you need to remove the data after the verification is complete.

### Generating the fTPM EK Verification Data
The intermediate data for fTPM EK verification can be generated by running the ODM EKB Gen Tool with the __--verify__ option. The output data is only for fTPM EK verification.

> [!Tip]
> ~~~
> odm_ekb_gen.py --kdk_db ftpm_kdk/kdk_db-01020000000100000002-5.csv --verify
> ~~~

The odm_ekb_gen.py creates the "ftpm_keys.txt" file in the output folder when --verify is set.

This file contains the following components:
- Device SN
- EPS
- RSA EK public key
- EC EK public key

### Setting up a Dedicated Jetson Device
To enable the fTPM EK verification function on Jetson device, you need to rebuild OP-TEE with the __CFG_JETSON_FTPM_HELPER_INJECT_EPS__ configuration. This is a custom configuration only for fTPM EK verification, and this configuration should not be enabled in the OP-TEE production build.

To enable the fTPM EK verification function, change the "optee_src_build.sh" script:
~~~
diff --git a/optee_src_build.sh b/optee_src_build.sh
index 383a4d3d9df4..c99500da89f4 100755
--- a/optee_src_build.sh
+++ b/optee_src_build.sh
@@ -94,7 +94,8 @@ function build_optee_sources {
                if [ "${ENABLE_FTPM_BUILD}" == "yes" ]; then
                        optee_config="CFG_CORE_TPM_EVENT_LOG=y \
                                CFG_REE_STATE=y \
-                               CFG_JETSON_FTPM_HELPER_PTA=y"
+                               CFG_JETSON_FTPM_HELPER_PTA=y \
+                               CFG_JETSON_FTPM_HELPER_INJECT_EPS=y"
                        early_tas="${build_dir}/early_ta/cpubl-payload-dec/0e35e2c9-b329-4ad9-a2f5-8ca9bbbd7713.stripped.elf \
                                ${build_dir}/early_ta/ftpm-helper/a6a3a74a-77cb-433a-990c-1dfb8a3fbc4c.stripped.elf \
                                ${build_dir}/early_ta/luks-srv/b83d14a8-7128-49df-9624-35f14f65ca6c.stripped.elf \
~~~
After this process is complete, rebuild OP-TEE, generate the TOS image again, and flash the TOS image to the board.

### Verifying the fTPM EKs

> [!Tip] To verify the RSA and EC EK public keys on a Jetson Orin device, run the ftpm_offline_provisioning_verify.sh script:
> 1. Copy the "ftpm_keys.txt" to the Jetson Orin device.
>    ~~~
>    scp ${SRC_VERIFY_DIR}/ftpm_keys.txt ${REMOTE_DEVICE}:${DEST_VERIFY_DIR}/ftpm_prov
>    ~~~
> 2. Copy the "ftpm_offline_provisioning_verify.sh" script from in the optee source directory to the Jetson Orin device.
>    ~~~
>    scp optee/samples/ftpm-helper/host/tool/ftpm_offline_provisioning_verify.sh ${REMOTE_DEVICE}:${DEST_VERIFY_DIR}/ftpm_prov
>    ~~~
> 3. Copy the custom nvftpm-helper-app to the Jetson Orin device.
>    ~~~
>    scp optee/build/t234/ca/ftpm-helper/nvftpm-helper-app ${REMOTE_DEVICE}:${DEST_VERIFY_DIR}/ftpm_prov
>    ~~~
> 4. Run ftpm_offline_provisioning_verify.sh on the Jetson Orin device.
>    ~~~
>    cd ${DEST_VERIFY_DIR}/ftpm_prov
>    sudo ./ftpm_offline_provisioning_verify.sh
>    ~~~

Appendix A: The Overall Command Reference Flow for fTPM Manufacturing Support
-----------------------------------------------------------------------------

# The ODM Workflow

> [!Note] The ODM Preparation Work
> 1. [Jetson BSP Installation](#jetson-bsp-installation)
> 2. Retrieve the OEM's "pv_key.crt" and store at "~/oem_keys/uefi_keys/pv_key.crt".
> 3. Assume that the ODM's own keys are stored at:
>    ~~~
>    ~/odm_keys/rsa3k.pem
>    ~/odm_keys/sbk-32.key
>    ~~~

> [!Note] ODM EKB Generation
> 1. Generate the ODM EKB DB
>    ~~~
>    cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool
>    ./kdk_gen.py --oem_id 0x21 --sn 0x1000218000 --num_devices 2
>    ./odm_ekb_gen.py --kdk_db ftpm_kdk/kdk_db_0021-0000001000218000-2.csv
>    tar cf odm_out_0021-0000001000218000-2.tar odm_out/
>    ~~~
> 2. The ODM sends the "odm_out_0021-0000001000218000-2.tar" to the OEM.
>    - The OEM will then add OEM/user_defined keys into EKB.
>      - Encrypted the updated EKB.
>      - Generate the EKB tarball.
>      - And then send the tarball back to ODM
> 3. The ODM waits to receive the "oem_out_0021-0000001000218000-2.tar" from the OEM.
>    ~~~
>    cd ${BSP_TOP}/Linux_for_Tegra/
>    tar xvf oem_out_0021-0000001000218000-2.tar
>    # The tarball is untared at the "oem_out" folder.
>
>    # Singing and generating the final ekb_db.
>    ./l4t_sign_image.sh --chip 0x23 \
>                        --key ~/odm_keys/rsa3k.pem \
>                        --encrypt_key ~/odm_keys/sbk-32.key \
>                        --mass-ekb oem_out \
>                        --type data \
>                        --split False
>    tar cf eks_0021-0000001000218000-2.tar --directory=oem_out/signed .
>    ~~~
> 4. The ODM sends the "eks_0021-0000001000218000-2.tar" to the OEM.

> [!Note] ODM Fuseblob Generation
> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra
> cd l4t/tools/flashtools/fuseburn/
> sudo ./fskp_fuseburn.py -f odm_fuse_template.xml \
>                         --multi-blob ${ODM_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 fskp_ak.bin fskp_ek.bin \
>                         --fskpcfg 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 used in this command are for reference only. They should be replaced with the user's specific fskp keys and board.
> - The ODM sends "fuseblob_odm_out_0021-0000001000218000-2.tar" to the OEM.

> [!Note] QSPI Image Generation (aka lower boot components: lbc)
> - AGX Orin with eMMC as the RootFS storage
>   ~~~
>   cd ${BSP_TOP}/Linux_for_Tegra
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --odm-image-gen \
>                           --showlogs \
>                           --network usb0 \
>                           --no-flash \
>                           -u ~/odm_keys/rsa3k.pem \
>                           -v ~/odm_keys/sbk-32.key \
>                           --pv-crt ~/oem_keys/uefi_keys/pv_key.crt \
>                           jetson-agx-orin-devkit \
>                           internal
>   ~~~
> - AGX Orin with NVMe as the RootFS storage
>   ~~~
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --odm-image-gen \
>                           --showlogs \
>                           --network usb0 \
>                           --no-flash \
>                           -c bootloader/t186ref/cfg/flash_t234_qspi.xml \
>                           -u ~/odm_keys/rsa3k.pem \
>                           -v ~/odm_keys/sbk-32.key \
>                           --pv-crt ~/oem_keys/uefi_keys/pv_key.crt \
>                           jetson-agx-orin-devkit \
>                           internal
>   ~~~
> - Orin Nano with NVMe as the RootFS storage
>   ~~~
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --odm-image-gen \
>                           --showlogs \
>                           --network usb0 \
>                           --no-flash \
>                           -p "-c bootloader/t186ref/cfg/flash_t234_qspi.xml" \
>                           -u ~/odm_keys/rsa3k.pem \
>                           -v ~/odm_keys/sbk-32.key \
>                           --pv-crt ~/oem_keys/uefi_keys/pv_key.crt \
>                           jetson-orin-nano-devkit \
>                           internal
>   ~~~
> - The output from the above command is "lbc_odm.tar.gz".
> - The ODM sends it to the OEM.

# The OEM Workflow

> [!Note] The OEM Preparation Work
> 1. [Jetson BSP Installation](#jetson-bsp-installation)
> 2. Assume that the OEM's user defined keys (for ekb generation) are stored at:
>    ~~~
>    ~/oem_keys/oem_k1.key
>    ~/oem_keys/fv_ekb_t234
>    ~/oem_keys/sym_t234.key
>    ~/oem_keys/sym2_t234.key
>    ~/oem_keys/auth_t234.key
>    ~~~
> 3. OEM's PV keys (for UEFI/cpu-bootloader image signing and encryption) are stored at:
>    ~~~
>    ~/oem_keys/pv_keys/rsa_priv-3k-pv.pem
>    ~/oem_keys/pv_keys/pv_key.crt (This public key is shared with the ODM.)
>    ~/oem_keys/pv_keys/pv_enc_k2.key (This is OEM_K2 fuse value.)
>    ~~~
> 4. OEM's uefi keys (for UEFI payloads such as kernel/kernel-dtb signing and encryption) are stored at:
>    ~~~
>    ~/oem_keys/uefi_keys/uefi_keys.conf
>    ~/odm_keys/uefi_keys/uefi_enc.key
>    ~~~

> [!Note] OEM Fuseblob Generation
> 1. Generating the OEM fuseblob
>    ~~~
>    cd ${BSP_TOP}/Linux_for_Tegra
>    cd l4t/tools/flashtools/fuseburn/
>    sudo ./fskp_fuseburn.py --skipfskpkey \
>                            -c 0x23 \
>                            -g fuseblob/oem_out \
>                            --test \
>                            -f oem_fuse_template.xml \
>                            --board-spec orin-agx-board-spec.txt \
>                            -B ../../../../jetson-agx-orin-devkit.conf
>    tar cf fuseblob_oem_out.tar fuseblob/oem_out
>    ~~~
>
> 2. The OEM sends "fuseblob_oem_out.tar" to the FACTORY.
> 3. The OEM receives ODM fuseblob db from the ODM.
> 4. The OEM sends "fuseblob_odm_out_0021-0000001000218000-2.tar" to the FACTORY.

> [!Note] OEM EKB Generation
> 1. Receiving and untar "odm_out_0021-0000001000218000-2.tar" from the ODM.
>    ~~~
>    cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool/
>    cp ${BSP_TOP}/odm_out_0021-0000001000218000-2.tar ./
>    tar xf odm_out_0021-0000001000218000-2.tar
>    ~~~
> 2. Adding the OEM user defined keys into ekb.
>    ~~~
>    cp ../../../hwkey-agent/host/tool/gen_ekb/gen_ekb.py ./
>    ./oem_ekb_gen.py -oem_k1 ~/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
>    tar cf oem_out_0021-0000001000218000-2.tar oem_out/
>    ~~~
> 3. Sending "oem_out_0021-0000001000218000-2.tar" to the ODM.
>    - Note: The result from Step 3 here is the image that ODM awaits at Step 3 in the "ODM EKB Generation" flow, as described earlier.

> [!Note] UPI Image Generation (aka User Partition Image: UPI)
> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra
> sudo ./tools/gen_uefi_default_keys_dts.sh ~/oem_keys/uefi_keys/uefi_keys.conf
> ~~~
> - Note: In the commands below, the key in --uefi-enc \<key\> option is used for UEFI payload encryption. This key value is the sym_t234.key defined and added into the EKB by the OEM.
> - AGX Orin with eMMC as the RootFS storage
>   ~~~
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --mass-storage-only \
>                           --showlogs \
>                           --network usb0 \
>                           --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
>   ~~~
> - AGX Orin with NVMe as the RootFS storage
>   ~~~
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --mass-storage-only \
>                           --showlogs \
>                           --network usb0 \
>                           --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
>   ~~~
> - Orin Nano with NVMe as the RootFS storage
>   ~~~
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --mass-storage-only \
>                           --showlogs \
>                           --network usb0 \
>                           --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 from the above command is "upi_oem.tar.gz".

> [!Note] UEFI Image Generation
> ~~~
> cd ${OEM_BSP_TOP}/Linux_for_Tegra
> sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
> ./flash.sh  -k A_cpu-bootloader \
>             --no-flash \
>             -u ~/oem_keys/pv_keys/rsa_priv-3k-pv.pem \
>             --pv-enc ~/oem_keys/pv_keys/pv_enc_k2.key \
>             --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 from the above command is "uefi_jetson_with_dtb_aligned_blob_w_bin_sigheader_encrypt.bin.signed".

> [!Note] Final Factory tarball Generation
> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra
> sudo tar zvxf lbc_odm.tar.gz
> sudo tar zvxf upi_oem.tar.gz
> cd tools/kernel_flash/images/internal/
> sudo cp ${BSP_TOP}/Linux_for_Tegra/bootloader/uefi_jetson_with_dtb_aligned_blob_w_bin_sigheader_encrypt.bin.signed ./
>
> # Record the UEFI image file size
> ls -l uefi_jetson_with_dtb_aligned_blob_w_bin_sigheader_encrypt.bin.signed
>
> # Record the sha1sum
> sha1sum uefi_jetson_with_dtb_aligned_blob_w_bin_sigheader_encrypt.bin.signed
>
> # Edit the A/B_cpu-bootloader with PV signed/encrypted file size and sha1Sum
> sudo vim flash.idx
>
> # Untar EKB_final_db received from ODM (i.e., eks_0021-0000001000218000-2.tar)
> sudo mkdir ekb_db
> cd ekb_db
> sudo tar xf ${OEM_BSP_TOP}/eks_0021-0000001000218000-2.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/
> ~~~
> - Send the "factory.tar.gz" to the factory.

# The Factory Workflow

> [!Note] The Factory Preparation Work
> 1. [Jetson BSP Installation](#jetson-bsp-installation)

## Fuse Burning

> [!Note] Fuse burning
> 1. Receiving the OEM and ODM fuseblob from the OEM.
>    ~~~
>    cd ${BSP_TOP}/Linux_for_Tegra
>    cp fuseblob_oem_out.tar l4t/tools/flashtools/fuseburn/
>    cp fuseblob_odm_out_0021-0000001000218000-2.tar l4t/tools/flashtools/fuseburn/
>    cd l4t/tools/flashtools/fuseburn
>    tar xf fuseblob_oem_out.tar
>    tar xf fuseblob_odm_out_0021-0000001000218000-2.tar
>    ~~~
>
> > [!Caution]
> > - Burn the OEM fuseblob first, then burn the ODM fuseblob.
> > - There are two examples here, fusing the fuses to each target one by one or fusing fuses to multiple targets in one command. Please choose one of them to apply.
>
> 2. Fusing the fuses to each target.
>    - Fusing the OEM fuses to each target.
>      ~~~
>      sudo ./fskp_fuseburn.py -c 0x23 \
>                              -P fuseblob/oem_out/ \
>                              --board-spec orin-agx-board-spec.txt \
>                              -B ../../../../jetson-agx-orin-devkit.conf
>      ~~~
>      - The command above burns the OEM fuses on device 1.
>      - Repeating the same command on device 2.
>    - Fusing the ODM fuses to each target.
>      ~~~
>      sudo ./fskp_fuseburn.py -c 0x23 \
>                              -P fuseblob/odm_out_0021-0000001000218000/ \
>                              --board-spec orin-agx-board-spec.txt \
>                              -B ../../../../jetson-agx-orin-devkit.conf
>      # The command above burns the ODM fuses on device 1.
>
>      sudo ./fskp_fuseburn.py -c 0x23 \
>                              -P fuseblob/odm_out_0021-0000001000218001/ \
>                              --board-spec orin-agx-board-spec.txt \
>                              -B ../../../../jetson-agx-orin-devkit.conf
>      # The command above burns the ODM fuses on device 2.
>      ~~~
>
> 3. Fusing the fuses to multiple targets in one command.
>    - Note: You need to make sure all the devices are connected to the same host and are in recovery mode.
>    - Fusing the OEM fuses to multiple targets in one command.
>      ~~~
>      sudo ./fskp_massfuseburn.py --skipconfirmation \
>                                  --burnfuse \
>                                  --board-spec orin-agx-board-spec.txt \
>                                  -P fuseblob/oem_out \
>                                  -c 0x23 \
>                                  -B ../../../../jetson-agx-orin-devkit.conf
>      ~~~
>    - Fusing the ODM fuses to multiple targets in one command.
>      ~~~
>      sudo ./fskp_multiblobfuse.py --out fuseblob/odm_out \
>                                   --oem_id 0x0021 \
>                                   --sn 0x0000001000218000 \
>                                   --num_devices 2 \
>                                   --board-spec orin-agx-board-spec.txt \
>                                   -B ../../../../jetson-agx-orin-devkit.conf
>      ~~~

## Flashing Images

> [!Note] Flashing Images
> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra
> sudo tar zvxf factory.tar.gz
> cp tools/kernel_flash/images/rcmboot/* bootloader/
> ~~~
> - AGX Orin with eMMC as the RootFS storage
>   ~~~
>   sudo ./tools/kernel_flash/l4t_initrd_flash.sh \
>                               --flash-only \
>                               --showlogs \
>                               --network usb0 \
>                               --ekb-pair \
>                               jetson-agx-orin-devkit \
>                               internal
>   ~~~
> - AGX Orin with NVMe as the RootFS storage
>   ~~~
>   sudo ./tools/kernel_flash/l4t_initrd_flash.sh \
>                               --flash-only \
>                               --showlogs \
>                               --network usb0 \
>                               --ekb-pair \
>                               jetson-agx-orin-devkit \
>                               nvme0n1p1
>   ~~~
> - Orin Nano with NVMe as the RootFS storage
>   ~~~
>   sudo ./tools/kernel_flash/l4t_initrd_flash.sh \
>                               --flash-only \
>                               --showlogs \
>                               --network usb0 \
>                               --ekb-pair \
>                               jetson-orin-nano-devkit \
>                               nvme0n1p1
>   ~~~

Appendix B: Handle The fTPM Manufacturing Flow In One Entity
------------------------------------------------------------

# The Following Steps Work For An Entity That Behaves As both ODM and OEM

## The Preparation Work

> [!Note] The Preparation Work
> 1. [Jetson BSP Installation](#jetson-bsp-installation)
> 2. Assume that the ODM's own keys are stored at:
>    ~~~
>    ~/odm_keys/rsa3k.pem
>    ~/odm_keys/sbk-32.key
>    ~~~
> 3. Assume that the ODM's fskp fusing files are stored at:
>    ~~~
>    ~/odm_fskp/odm_fuse_template.xml
>    ~/odm_fskp/fskp_ak.bin
>    ~/odm_fskp/fskp_ek.bin
>    ~/odm_fskp/fskp_conf.txt
>    ~~~
> 4. Assume that the ODM's user defined keys (for EKB generation) are stored at:
>    ~~~
>    ~/oem_keys/oem_k1.key
>    ~/oem_keys/sym_t234.key
>    ~/oem_keys/sym2_t234.key
>    ~/oem_keys/auth_t234.key
>    ~~~
> 5. Assume that the OEM's UEFI keys (for UEFI payloads such as kernel/kernel-dtb signing and encryption) are store at:
>    ~~~
>    ~/oem_keys/uefi_keys/uefi_keys.conf
>    ~/oem_keys/uefi_keys/uefi_enc.key
>    ~~~
> 6. Assume that the OEM's fuse template are stored at:
>    ~~~
>    ~/oem_keys/oem_fuse_template.xml
>    ~~~

## EKB Generation

> [!Note] 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
>    ./kdk_gen.py --oem_id 0x21 --sn 0x1000218000 --num_devices 2
>    ./odm_ekb_gen.py --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 ./
>    ./oem_ekb_gen.py -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
>    # The outputs from the above command are stored at the oem_out folder.
>    cp -r oem_out/ ${BSP_TOP}
>    ~~~
>    - oem_k1.key: "OemK1" value in the OEM fuse template xml
>    - sym_t234.key: UEFI payloads (such as kernel, kernel-dtb) encryption key
>    - sym2_t234.key: disk encryption key
>    - auth_t234.key: UEFI variables authentication key
> 3. Signing and generating the final ekb_db.
>    ~~~
>    cd ${BSP_TOP}/Linux_for_Tegra
>    ./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
>    ~~~
>    - The encrypted/signed final ekb_db images are stored at the "${BSP_TOP}/oem_out/signed" folder.

## Fuseblob Generation

> [!Note] Fuseblob Generation
> - To create a new fuse template, merge the contents of `odm_fuse_template.xml` and `oem_fuse_template.xml`, naming the resulting file as `odem_fuse_template.xml`.
> - Please note that when merging these templates, place the content from `oem_fust_template.xml` before that of `odm_fuse_template.xml`.
> - This ensures that the fuse named "SecurityMode" remains the last entry in the merged template.
>   ~~~
>   <genericfuse MagicId="0x45535546" version="1.0.0">
>       <fuse name="OdmInfo" size="4" value="0xFFFF"/>
>       <fuse name="OdmId" size="8" value="0xFFFFFFFFFFFFFFFF"/>
>       <fuse name="Kdk0" size="32" value="0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/>
>       <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/
> sudo ./fskp_fuseburn.py -f ~/odm_fskp/odem_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
> # The outputs generated by the above command are stored at the "fuseblob/odm_out" folder.
> tar cf fuseblob_odm_out_0021-0000001000218000-2.tar fuseblob/
> ~~~
> - Note: The parameters used in this command are for reference only. They should be replaced with the user's specific fskp keys and board.
> - Note: The "--test" option indicates the generated fuse blob will only do a dry run. To actually burn the fuses, replace --test with --burnfuse.
> - Sending "fuseblob_odm_out_0021-0000001000218000-2.tar" to the FACTORY.
> - Please follow the Fusing the ODM Fuses of the [Fuse Burning](#fuse-burning) process in Appendix A.

## Flash Image Generation
- Followings are example commands to generate flash images (LBC + UPI) for different target configurations:
    - AGX Orin with eMMC as rootfs
    - AGX Orin with NVMe as rootfs
    - Orin Nano with NVMe as rootfs

> [!Note]
> 1. Unlike ODM and OEM are separate entities where the UEFI image generated by ODM is only a placeholder. Here, the UEFI image generated by the LBC generating command (with --odm-image-gen option) is the final UEFI image. In order to enable UEFI Secureboot feature at flashing time, option --uefi-keys must be provided in the command line so that all keys required by UEFI can be built in along with the UEFI image.
> 2. The UEFI image is encrypted and signed by the sbk key (-v option) and pkc key (-u option).
> 3. In the commands to generate upi_oem.tar.gz, the key in --uefi-enc <key> option is used for UEFI payload encryption. This key value is the sym_t234.key used in the EKB generation.
> 4. Enable UEFI secure boot feature:
>    ~~~
>    cd ${BSP_TOP}/Linux_for_Tegra
>    sudo ./tools/gen_uefi_default_keys_dts.sh ~/oem_keys/uefi_keys/uefi_keys.conf
>    ~~~

> [!Note] AGX Orin with eMMC as the RootFS storage
> - QSPI image generation (aka lower boot components: lbc)
>   ~~~
>   cd ${BSP_TOP}/Linux_for_Tegra
>   sudo rm -rf tools/kernel_flash/images/
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --odm-image-gen \
>                           --showlogs \
>                           --network usb0 \
>                           --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 image generation (aka User Partition Image: UPI)
>   ~~~
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --mass-storage-only \
>                           --showlogs \
>                           --network usb0 \
>                           --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 from the above commands are "lbc_odm.tar.gz" and "upi_oem.tar.gz".

> [!Note] AGX Orin with NVMe as the RootFS storage
> - QSPI image generation (aka lower boot components: lbc)
>   ~~~
>   cd ${BSP_TOP}/Linux_for_Tegra
>   sudo rm -rf tools/kernel_flash/images/
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --odm-image-gen \
>                           --showlogs \
>                           --network usb0 \
>                           --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 image generation (aka User Partition Image: UPI)
>   ~~~
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --mass-storage-only \
>                           --showlogs \
>                           --network usb0 \
>                           --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 from the above commands are "lbc_odm.tar.gz" and "upi_oem.tar.gz".

> [!Note] Orin Nano with NVMe as the RootFS storage
> - QSPI image generation (aka lower boot components: lbc)
>   ~~~
>   cd ${BSP_TOP}/Linux_for_Tegra
>   sudo rm -rf tools/kernel_flash/images/
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --odm-image-gen \
>                           --showlogs \
>                           --network usb0 \
>                           --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 image generation (aka User Partition Image: UPI)
>   ~~~
>   sudo BOARDID=<BOARDID> FAB=<FAB> BOARDSKU=<BOARDSKU> CHIP_SKU=<CHIP_SKU> RAMCODE_ID=<RAMCODE_ID> \
>   ./tools/kernel_flash/l4t_initrd_flash.sh \
>                           --mass-storage-only \
>                           --showlogs \
>                           --network usb0 \
>                           --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 from the above commands are "lbc_odm.tar.gz" and "upi_oem.tar.gz".

## Factory tarball Generation

> [!Note] Final 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/
> ~~~
> - Sending the factory.tar.gz to FACTORY.
> - Please follow the [Flashing Images](#flashing-images) process in Appendix A.


Appendix C: MassFlash Multiple Targets Simulteneously
-----------------------------------------------------

Use **--massflash** command option to flash multiple targets simulteneoutly.

The following procedures assume that:
  - All targets are fused with the same value to the **Kdk0** fuse;
    The **Kdk0** fuse value is saved as **fuse-kdk0** file.
  - All targets are fused with the same value to the **OdmInfo** fuse.
    The **OdmInfo** fuse value is used as in **--oem_id** in the generation of EKB_db.
    (0x21 is used in the following example).
  - All targets are fused with the sequential values to the **OdmId** fuses.
    The starting **OdmId** fuse value is used as **--sn** in the generation of EKB_db.
    (0x1000218000 is used in the following example.)

## Preparing EKB_db

### ODM: Generating ODM EKB_db

> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool
> ./kdk_gen.py --oem_id 0x21 --sn 0x1000218000 --num_devices 100 --preset fuse-kdk0 --fixed
> ./odm_ekb_gen.py --kdk_db ftpm_kdk/kdk_db_0021-0000001000218000-100.csv
> ~~~

The ODM packages the **odm_out** folder and delivers it to the OEM.

### OEM: Generating OEM EKB_db

The OEM unpacks the ODM's **odm_out** package, and puts it to ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool.

> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra/source/optee/samples/ftpm-helper/host/tool
> oem_ekb_gen.py -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
> ~~~

THE OEM packages the **oem_out** folder and deliver it back to the ODM.

### ODM: Signing and encrypting EKB_db

The ODM unpacks the **oem_out** package, and put it to ${BSP_TOP}/Linux_for_Tegra.

> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra
> ./l4t_sign_image.sh --chip <CHIP_ID> \
>                     --key <PKC_KEY> \
>                     --encrypt_key <SBK_KEY> \
>                     --mass-ekb oem_out \
>                     --type data \
>                     --split False
> ~~~

The ODM packages the encrypted/signed eks files.

> ~~~
> tar cf eks_0021-0000001000218000-100.tar --directory=oem_out/signed/ .
> ~~~

The ODM delivers **eks_0021-0000001000218000-100.tar** to the OEM.

## ODM: QSPI Image Generation

Follow the same steps to generate **QSPI Image Generation** in Appendix A or Appendix B.

For example, to generate QSPI image for Orin Nano:

> ~~~
> sudo BOARDID=3767 FAB=300 BOARDSKU=0005 CHIP_SKU=00:00:00:D5 RAMCODE_ID=2 ./tools/kernel_flash/l4t_initrd_flash.sh \
>           --odm-image-gen \
>           --showlogs \
>           --network usb0 \
>           --no-flash \
>           -p "-c bootloader/t186ref/cfg/flash_t234_qspi.xml" \
>           -u ~/odm_keys/rsa3k.pem \
>           -v ~/odm_keys/sbk-32.key \
>           jetson-orin-nano-devkit \
>           internal
> ~~~

The ODM delivers the **lbc_odm.tar.gz** to the OEM.

## OEM: UPI Image Generation

Follow the same steps to generate **UPI Image Generation** in Appendix A or Appendix B.
In addition, add **--massflash `<num>`** to the --mass-storage-only command, where `<num>` is the maximum number of USB ports that the host can support.

For example, to generate the UPI images for Orin Nano for all 5 flashing USB ports:
> ~~~
> sudo BOARDID=3767 FAB=300 BOARDSKU=0005 CHIP_SKU=00:00:00:D5 RAMCODE_ID=2 ./tools/kernel_flash/l4t_initrd_flash.sh \
>           --mass-storage-only \
>           --massflash 5
>           --showlogs \
>           --network usb0 \
>           --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
> ~~~

## OEM: Final Factory tarball Generation

> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra
> sudo tar zvxf lbc_odm.tar.gz
> sudo tar zvxf 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 ${OEM_BSP_TOP}/eks_0021-0000001000218000-100.tar
> cd ${BSP_TOP}/Linux_for_Tegra
> # Copy boot*.img in mfi_jetson-orin-nano-devkit/bootloader/ to the Factory tarball.
> sudo cp mfi_jetson-orin-nano-devkit/bootloader/boot*.img \
>      tools/kernel_flash/images/rcmboot/
> # Package the factory tarball
> sudo tar zvcf factory.tar.gz \
>      tools/kernel_flash/initrdflashparam.txt \
>      tools/kernel_flash/initrdflashimgmap.txt \
>      tools/kernel_flash/images/
> ~~~

The OEM delivers the **factory.tar.gz** to the FACTORY.

## FACTORY: **MassFlash** Work Flow

Follow the [Flashing Images](#flashing-images) process in Appendix A.
In addition, add a **--massflash `<num>`** to the --flash-only command, where `<num>` is the maximum number of USB ports that the host can support.

For example, to flash 5 targets of Orin Nano simultaneously:
> ~~~
> cd ${BSP_TOP}/Linux_for_Tegra
> sudo ./tools/kernel_flash/l4t_initrd_flash.sh \
>           --flash-only \
>           --massflash 5 \
>           --showlogs \
>           --network usb0 \
>           --ekb-pair \
>           jetson-orin-nano-devkit \
>           nvme0n1p1
> ~~~

The above command will flash upto 5 targets simultaneously, and during the flashing, the script will read the targets under flashing for **OdmInfo** and **OdmId** fuses and flash the matching ekb binary to the A_eks and B_eks partitions of the targets.

After 5 targets are flashed, another 5 targets can be flashed using the exact same command. However, due to the unique combined **OdmInfo** and **OdmId** fused onto each board, the matching ekb image is flashed to the corresponding board, respectively.

The above command script also supports **-k `<part_name>`** option to flash only the specific `<part_name>` partition to all the targets.

For example,
> ~~~
> sudo ./tools/kernel_flash/l4t_initrd_flash.sh \
>           -k A_secure-os \
>           --flash-only \
>           --massflash 5 \
>           --showlogs \
>           --network usb0 \
>           jetson-orin-nano-devkit \
>           nvme0n1p1
> ~~~

The above command script will flash only the **A_secure-os** partition to all the targets.

To ensure that both A_eks and B_eks partitions get the same ekb binary flashed, the command script supports the wildcard **-k \*_eks** option.

For example,
> ~~~
> sudo ./tools/kernel_flash/l4t_initrd_flash.sh \
>           -k *_eks \
>           --flash-only \
>           --massflash 5 \
>           --showlogs \
>           --network usb0 \
>           --ekb-pair \
>           jetson-orin-nano-devkit \
>           nvme0n1p1
> ~~~

The above command script will flash only the **A_eks** and **B_eks** partitions with the matching ekb binary.

```note
NOTE: *_eks is the only wildcard -k option supported.
